Remove sponsor token gating
Some checks failed
CodeQL / Analyze (go) (push) Failing after 1m16s
CodeQL / Analyze (actions) (push) Failing after 4s
CodeQL / Analyze (javascript-typescript) (push) Failing after 7s
Default / Clean (push) Has been cancelled
Default / Build (push) Has been cancelled
Default / Test (push) Has been cancelled
Default / Lint (push) Has been cancelled
Default / UI (push) Has been cancelled
Default / Integration (push) Has been cancelled
Default / Warm fork cache (Go) (push) Has been cancelled
Default / Warm fork cache (UI) (push) Has been cancelled
Some checks failed
CodeQL / Analyze (go) (push) Failing after 1m16s
CodeQL / Analyze (actions) (push) Failing after 4s
CodeQL / Analyze (javascript-typescript) (push) Failing after 7s
Default / Clean (push) Has been cancelled
Default / Build (push) Has been cancelled
Default / Test (push) Has been cancelled
Default / Lint (push) Has been cancelled
Default / UI (push) Has been cancelled
Default / Integration (push) Has been cancelled
Default / Warm fork cache (Go) (push) Has been cancelled
Default / Warm fork cache (UI) (push) Has been cancelled
Unlock all locally gated features without a sponsor token. The change sits in util/sponsor/auth.go instead of the ~68 device constructors: - Subject defaults to a non-empty value, so RedactedStatus reports an active sponsorship and the frontend unlocks isSponsor - IsAuthorized always returns true, opening every caller including the modbus proxy and the optimizer gate - ConfigureSponsorship still validates a configured token but never fails, so an expired token no longer aborts startup Drops TestAlpitronicSponsorGate and TestSigenergyEVDCSponsorGate, which asserted exactly the gate that is removed here. Cloud-backed services (optimizer API, remote access, cloud vehicles, telemetry) still require a real token - those are checked server side. See FORK.md.
This commit is contained in:
parent
d379b7802e
commit
13215c74c2
4 changed files with 86 additions and 31 deletions
54
FORK.md
Normal file
54
FORK.md
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
# Fork-Hinweise
|
||||||
|
|
||||||
|
Fork von [evcc-io/evcc](https://github.com/evcc-io/evcc).
|
||||||
|
|
||||||
|
## Abweichung vom Upstream
|
||||||
|
|
||||||
|
Das Sponsor-Gating ist entfernt: alle lokal geprüften Features stehen ohne
|
||||||
|
Sponsor-Token zur Verfügung. Der Eingriff sitzt an einer einzigen Stelle in
|
||||||
|
`util/sponsor/auth.go`, statt in den ~68 Geräte-Konstruktoren:
|
||||||
|
|
||||||
|
- `Subject` ist per Default nicht leer (`"unlocked"`). Damit meldet
|
||||||
|
`RedactedStatus()` einen aktiven Sponsor-Status, und das Frontend schaltet
|
||||||
|
`isSponsor` (`assets/js/views/Config.vue`) frei — Geräte-Dialoge zeigen kein
|
||||||
|
„Sponsor-Token erforderlich" mehr.
|
||||||
|
- `IsAuthorized()` liefert immer `true`. Das öffnet alle Aufrufer, u. a. die
|
||||||
|
Charger-/Vehicle-Konstruktoren, `server/modbus/proxy.go` und den
|
||||||
|
Optimizer-Gate in `core/site_optimizer.go`.
|
||||||
|
- `ConfigureSponsorship()` validiert ein hinterlegtes Token weiterhin, gibt aber
|
||||||
|
nie einen Fehler zurück. Ein abgelaufenes oder ungültiges Token bricht den
|
||||||
|
Start damit nicht mehr ab; es wird nur verworfen, damit es keinen
|
||||||
|
Cloud-Diensten angeboten wird.
|
||||||
|
|
||||||
|
Zusätzlich entfernt: `TestAlpitronicSponsorGate` und
|
||||||
|
`TestSigenergyEVDCSponsorGate` — beide prüften genau das Gate, das hier
|
||||||
|
absichtlich nicht mehr existiert.
|
||||||
|
|
||||||
|
## Was das *nicht* freischaltet
|
||||||
|
|
||||||
|
Serverseitig geprüfte Dienste brauchen weiterhin ein echtes Token, weil die
|
||||||
|
Prüfung bei evcc.io stattfindet und nicht im Client:
|
||||||
|
|
||||||
|
- Optimizer-API (`core/site_optimizer.go` sendet `Bearer <token>`)
|
||||||
|
- Remote Access / Tunnel (`server/remote/`)
|
||||||
|
- Cloud-Fahrzeuge und Tronity (`vehicle/cloud.go`, `vehicle/tronity.go`)
|
||||||
|
- Telemetrie (`util/telemetry/`, hängt an `IsAuthorizedForApi()`)
|
||||||
|
|
||||||
|
## Lizenz
|
||||||
|
|
||||||
|
Der Hauptteil von evcc steht unter MIT. `util/sponsor/` trägt einen eigenen
|
||||||
|
Vermerk („This module is NOT covered by the MIT license. All rights reserved.").
|
||||||
|
Diese Änderung betrifft genau dieses Modul.
|
||||||
|
|
||||||
|
evcc finanziert sich über Sponsoring: <https://sponsor.evcc.io>
|
||||||
|
|
||||||
|
## Upstream-Sync
|
||||||
|
|
||||||
|
```
|
||||||
|
git remote add upstream https://github.com/evcc-io/evcc.git
|
||||||
|
git fetch upstream
|
||||||
|
git rebase upstream/master
|
||||||
|
```
|
||||||
|
|
||||||
|
Konflikte sind auf `util/sponsor/auth.go` beschränkt; `FORK.md` liegt bewusst
|
||||||
|
außerhalb der Upstream-Dateien.
|
||||||
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"github.com/andig/mbserver"
|
"github.com/andig/mbserver"
|
||||||
"github.com/evcc-io/evcc/api"
|
"github.com/evcc-io/evcc/api"
|
||||||
"github.com/evcc-io/evcc/util/modbus"
|
"github.com/evcc-io/evcc/util/modbus"
|
||||||
"github.com/evcc-io/evcc/util/sponsor"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
@ -362,14 +361,3 @@ func TestAlpitronicReadFailure(t *testing.T) {
|
||||||
_, err = wb.CurrentPower()
|
_, err = wb.CurrentPower()
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAlpitronicSponsorGate(t *testing.T) {
|
|
||||||
// go-e tests set the global sponsor.Subject and never reset it
|
|
||||||
old := sponsor.Subject
|
|
||||||
sponsor.Subject = ""
|
|
||||||
t.Cleanup(func() { sponsor.Subject = old })
|
|
||||||
|
|
||||||
// tests run without sponsorship: the public constructor must refuse
|
|
||||||
_, err := NewAlpitronicHYC(t.Context(), modbus.TcpSettings{URI: "localhost:0", ID: 1}, 1)
|
|
||||||
assert.ErrorIs(t, err, api.ErrSponsorRequired)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"github.com/andig/mbserver"
|
"github.com/andig/mbserver"
|
||||||
"github.com/evcc-io/evcc/api"
|
"github.com/evcc-io/evcc/api"
|
||||||
"github.com/evcc-io/evcc/util/modbus"
|
"github.com/evcc-io/evcc/util/modbus"
|
||||||
"github.com/evcc-io/evcc/util/sponsor"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
@ -290,17 +289,6 @@ func TestSigenergyEVDCMinMaxCurrent(t *testing.T) {
|
||||||
assert.InDelta(t, 36.23, maxA, 0.01) // 25000 W / 690
|
assert.InDelta(t, 36.23, maxA, 0.01) // 25000 W / 690
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSigenergyEVDCSponsorGate(t *testing.T) {
|
|
||||||
// go-e tests set the global sponsor.Subject and never reset it
|
|
||||||
old := sponsor.Subject
|
|
||||||
sponsor.Subject = ""
|
|
||||||
t.Cleanup(func() { sponsor.Subject = old })
|
|
||||||
|
|
||||||
// tests run without sponsorship: the public constructor must refuse
|
|
||||||
_, err := NewSigenergyEVDC(t.Context(), "localhost:0", 1)
|
|
||||||
assert.ErrorIs(t, err, api.ErrSponsorRequired)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSigenergyEVDCReadFailure(t *testing.T) {
|
func TestSigenergyEVDCReadFailure(t *testing.T) {
|
||||||
// missing register in the bulk-read block -> IllegalDataAddress propagates
|
// missing register in the bulk-read block -> IllegalDataAddress propagates
|
||||||
regs := evdcRegs(evdcStateCharging)
|
regs := evdcRegs(evdcStateCharging)
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,14 @@ import (
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// unlocked is the sponsorship subject used by this fork. Sponsor gating is
|
||||||
|
// removed here, so Subject is never empty and IsAuthorized always holds.
|
||||||
|
const unlocked = "unlocked"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
Subject, Token string
|
Subject = unlocked
|
||||||
|
Token string
|
||||||
ExpiresAt time.Time
|
ExpiresAt time.Time
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -51,9 +56,8 @@ const unavailable = "sponsorship unavailable"
|
||||||
const startupTimeout = 30 * time.Second
|
const startupTimeout = 30 * time.Second
|
||||||
|
|
||||||
func IsAuthorized() bool {
|
func IsAuthorized() bool {
|
||||||
mu.RLock()
|
// sponsorship gating removed in this fork
|
||||||
defer mu.RUnlock()
|
return true
|
||||||
return len(Subject) > 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsAuthorizedForApi() bool {
|
func IsAuthorizedForApi() bool {
|
||||||
|
|
@ -62,8 +66,29 @@ func IsAuthorizedForApi() bool {
|
||||||
return IsAuthorized() && Subject != unavailable && Token != ""
|
return IsAuthorized() && Subject != unavailable && Token != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// check and set sponsorship token
|
// ConfigureSponsorship validates a sponsor token when one is configured, but
|
||||||
|
// never fails: sponsorship is not required in this fork. A valid token is
|
||||||
|
// still picked up so cloud-backed services keep working for real sponsors.
|
||||||
func ConfigureSponsorship(token string) error {
|
func ConfigureSponsorship(token string) error {
|
||||||
|
err := configureSponsorship(token)
|
||||||
|
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// stay unlocked, but do not offer a rejected token to cloud services
|
||||||
|
Token = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if Subject == "" {
|
||||||
|
Subject = unlocked
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// check and set sponsorship token
|
||||||
|
func configureSponsorship(token string) error {
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
defer mu.Unlock()
|
defer mu.Unlock()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue