Config UI: keep mqtt and influx secrets (part 2) (#20340)

This commit is contained in:
Leon 2025-04-04 12:43:11 +02:00 • committed by GitHub
parent 1c1ec03aac
commit 2e5f1808d7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 91 additions and 45 deletions

View file

@ -7,6 +7,7 @@ import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/plugin/mqtt"
"github.com/evcc-io/evcc/push"
"github.com/evcc-io/evcc/server/eebus"
"github.com/evcc-io/evcc/util/config"
@ -79,39 +80,18 @@ func masked(s any) string {
}
type Mqtt struct {
Broker string `json:"broker"`
Topic string `json:"topic"`
User string `json:"user"`
ClientID string `json:"clientID"`
Insecure bool `json:"insecure"`
Password string `json:"password"`
CaCert string `json:"caCert"`
ClientCert string `json:"clientCert"`
ClientKey string `json:"clientKey"`
mqtt.Config
Topic string `json:"topic"`
}
// Redacted implements the redactor interface used by the tee publisher
func (m Mqtt) Redacted() any {
return struct {
Broker string `json:"broker"`
Topic string `json:"topic"`
User string `json:"user,omitempty"`
ClientID string `json:"clientID,omitempty"`
Insecure bool `json:"insecure,omitempty"`
Password string `json:"password,omitempty"`
CaCert string `json:"caCert,omitempty"`
ClientCert string `json:"clientCert,omitempty"`
ClientKey string `json:"clientKey,omitempty"`
mqtt.Config
Topic string `json:"topic"`
}{
Broker: m.Broker,
Topic: m.Topic,
User: m.User,
ClientID: m.ClientID,
Insecure: m.Insecure,
Password: masked(m.Password),
CaCert: masked(m.CaCert),
ClientCert: masked(m.ClientCert),
ClientKey: masked(m.ClientKey),
m.Config.Redacted(),
m.Topic,
}
}

View file

@ -28,12 +28,33 @@ func ClientID() string {
type Config struct {
Broker string `json:"broker"`
User string `json:"user"`
Password string `json:"password"`
ClientID string `json:"clientID"`
Insecure bool `json:"insecure"`
CaCert string `json:"caCert"`
ClientCert string `json:"clientCert"`
ClientKey string `json:"clientKey"`
Password string `json:"password,omitempty"`
ClientID string `json:"clientID,omitempty"`
Insecure bool `json:"insecure,omitempty"`
CaCert string `json:"caCert,omitempty"`
ClientCert string `json:"clientCert,omitempty"`
ClientKey string `json:"clientKey,omitempty"`
}
func masked(s any) string {
if s != "" {
return "***"
}
return ""
}
// Redacted implements the redactor interface
func (c Config) Redacted() Config {
return Config{
Broker: c.Broker,
User: c.User,
Password: masked(c.Password),
ClientID: c.ClientID,
Insecure: c.Insecure,
CaCert: masked(c.CaCert),
ClientCert: masked(c.ClientCert),
ClientKey: masked(c.ClientKey),
}
}
// Client encapsulates mqtt publish/subscribe functions

View file

@ -123,13 +123,23 @@ func mergeSettings(old any, new any) error {
oldMap := structs.Map(old)
redactedMap := structs.Map(redactable.Redacted())
for k, v := range newMap {
if rv, ok := redactedMap[k]; ok && v == rv {
if ov, ok := oldMap[k]; ok {
newMap[k] = ov
}
}
}
newMap = mergeRedacted(oldMap, newMap, redactedMap)
return mapstructure.Decode(newMap, &new)
}
func mergeRedacted(old, new, redacted map[string]any) map[string]any {
for k, v := range new {
rv := redacted[k]
ov := old[k]
if nv, ok := v.(map[string]any); ok {
rv, ok := rv.(map[string]any)
ov, ok2 := ov.(map[string]any)
if ok && ok2 {
new[k] = mergeRedacted(ov, nv, rv)
}
} else if rv == v {
new[k] = ov
}
}
return new
}

View file

@ -9,8 +9,8 @@ import (
func TestMergeSettings(t *testing.T) {
tests := []struct {
old any
new *RedactedStruct
expected *RedactedStruct
new any
expected any
}{
{
old: nil,
@ -32,12 +32,29 @@ func TestMergeSettings(t *testing.T) {
new: &RedactedStruct{"newValue1", 42},
expected: &RedactedStruct{"newValue1", 42},
},
{
old: &NestedStruct{RedactedStruct{"oldValue1", 35}, 24},
new: &NestedStruct{RedactedStruct{"newValue1", 45}, 42},
expected: &NestedStruct{RedactedStruct{"newValue1", 45}, 42},
},
{
old: &NestedStruct{RedactedStruct{"oldValue1", 35}, 24},
new: &NestedStruct{RedactedStruct{"redacted", 45}, 0},
expected: &NestedStruct{RedactedStruct{"oldValue1", 45}, 24},
},
}
for _, tc := range tests {
mergeSettings(tc.old, tc.new)
assert.Equal(t, tc.expected.Field1, tc.new.Field1)
assert.Equal(t, tc.expected.Field2, tc.new.Field2)
switch expected := tc.expected.(type) {
case *RedactedStruct:
assert.Equal(t, expected.Field1, tc.new.(*RedactedStruct).Field1)
assert.Equal(t, expected.Field2, tc.new.(*RedactedStruct).Field2)
case *NestedStruct:
assert.Equal(t, expected.Field1.Field1, tc.new.(*NestedStruct).Field1.Field1)
assert.Equal(t, expected.Field1.Field2, tc.new.(*NestedStruct).Field1.Field2)
assert.Equal(t, expected.Field2, tc.new.(*NestedStruct).Field2)
}
}
}
@ -51,6 +68,11 @@ type RedactedStruct struct {
Field2 int
}
type NestedStruct struct {
Field1 RedactedStruct
Field2 int
}
func (t *RedactedStruct) Redacted() any {
return struct {
Field1 string
@ -60,3 +82,16 @@ func (t *RedactedStruct) Redacted() any {
Field2: t.Field2,
}
}
func (t *NestedStruct) Redacted() any {
return struct {
Field1 RedactedStruct
Field2 int
}{
Field1: t.Field1.Redacted().(struct {
Field1 string
Field2 int
}),
Field2: 0,
}
}