fix: on demand plant id generation (#21873)

This commit is contained in:
Michael Geers 2025-06-17 10:58:14 +02:00 • committed by GitHub
parent d43ab9fa6a
commit 36b6036203
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 82 additions and 50 deletions

View file

@ -8,10 +8,15 @@ import (
"strings"
"github.com/denisbrodbeck/machineid"
"github.com/evcc-io/evcc/core/keys"
"github.com/evcc-io/evcc/server/db/settings"
"github.com/samber/lo"
)
var id string
var (
id string
getMachineID = machineid.ID
)
// CustomID sets the machine id to a custom value
func CustomID(cid string) error {
@ -37,28 +42,37 @@ func RandomID() string {
}
// ID returns the platform specific machine id of the current host OS.
// If ID cannot be generated, a random value is suggested.
func ID() (string, error) {
// If ID cannot be generated, a random one from settings will be used (generated on demand)
func ID() string {
if id == "" {
var err error
if id, err = machineid.ID(); err != nil {
rid := RandomID()
return "", fmt.Errorf("could not get %w; for manual configuration use plant: %s", err, rid)
if id, err = getMachineID(); err == nil && id != "" {
return id
}
// no machine id found, use is from settings
return getOrCreateIDFromSettings()
}
return id, nil
return id
}
// getOrCreateIDFromSettings return instance id from settings if exists, otherwise creates and stores a new one
func getOrCreateIDFromSettings() string {
if id, err := settings.String(keys.Plant); err == nil && id != "" {
return id
}
id := RandomID()
settings.SetString(keys.Plant, id)
return id
}
// ProtectedID returns a hashed version of the machine id
// using a fixed, application-specific key.
func ProtectedID(key string) (string, error) {
id, err := ID()
if err != nil {
return id, err
}
return protect(key, id), nil
func ProtectedID(key string) string {
return protect(key, ID())
}
// protect calculates HMAC-SHA256 of the id, keyed by key and returns a hex-encoded string

View file

@ -1,22 +1,65 @@
package machine
import (
"errors"
"testing"
"github.com/denisbrodbeck/machineid"
"github.com/evcc-io/evcc/core/keys"
"github.com/evcc-io/evcc/server/db/settings"
)
func TestProtectedMachineId(t *testing.T) {
const key = "foo"
if mid, err := machineid.ProtectedID(key); err == nil {
id, err := ProtectedID(key)
if err != nil {
t.Error(err)
}
id := ProtectedID(key)
if mid != id {
t.Errorf("machine id mismatch. expected %s, got %s", mid, id)
}
} else {
t.Skip("cannot get machineid, skipping test")
}
}
func TestIdFromSettings(t *testing.T) {
// reset machine id cache
id = ""
// reset settings
settings.Delete(keys.Plant)
// force machineid.ID() to fail
getMachineID = func() (string, error) {
return "", errors.New("dummy error")
}
t.Cleanup(func() {
getMachineID = machineid.ID
})
// generate new random id
generatedId := ID()
if len(generatedId) != 64 {
t.Errorf("expected 64 char id, got %d", len(generatedId))
}
// check that id is stored in settings
settingsId, _ := settings.String(keys.Plant)
if generatedId != settingsId {
t.Errorf("expected id %s, got %s from settings", generatedId, settingsId)
}
// check reproducability
idA1 := ProtectedID("A")
idA2 := ProtectedID("A")
idB := ProtectedID("B")
if idA1 != idA2 {
t.Errorf("expected same id, got %s and %s", idA1, idA2)
}
if idA1 == idB {
t.Errorf("expected different id, got %s and %s", idA1, idB)
}
}