Issue UI: redact private data like (user, locations, ...) (#25039)

This commit is contained in:
Copilot 2025-11-26 13:34:57 +01:00 • committed by GitHub
parent f8dcd4dd78
commit a65a1ce2d7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 277 additions and 109 deletions

View file

@ -23,11 +23,11 @@ import (
"go.yaml.in/yaml/v4"
)
func devicesConfig[T any](class templates.Class, h config.Handler[T]) ([]map[string]any, error) {
func devicesConfig[T any](class templates.Class, h config.Handler[T], hidePrivate bool) ([]map[string]any, error) {
var res []map[string]any
for _, dev := range h.Devices() {
dc, err := deviceConfigMap(class, dev)
dc, err := deviceConfigMap(class, dev, hidePrivate)
if err != nil {
return nil, err
}
@ -54,20 +54,23 @@ func devicesConfigHandler(w http.ResponseWriter, r *http.Request) {
return
}
// Check if private data should be hidden (default: true, showing private data)
hidePrivate := r.URL.Query().Get("private") == "false"
var res []map[string]any
switch class {
case templates.Meter:
res, err = devicesConfig(class, config.Meters())
res, err = devicesConfig(class, config.Meters(), hidePrivate)
case templates.Charger:
res, err = devicesConfig(class, config.Chargers())
res, err = devicesConfig(class, config.Chargers(), hidePrivate)
case templates.Vehicle:
res, err = devicesConfig(class, config.Vehicles())
res, err = devicesConfig(class, config.Vehicles(), hidePrivate)
case templates.Circuit:
res, err = devicesConfig(class, config.Circuits())
res, err = devicesConfig(class, config.Circuits(), hidePrivate)
}
if err != nil {
@ -78,7 +81,7 @@ func devicesConfigHandler(w http.ResponseWriter, r *http.Request) {
jsonWrite(w, res)
}
func deviceConfigMap[T any](class templates.Class, dev config.Device[T]) (map[string]any, error) {
func deviceConfigMap[T any](class templates.Class, dev config.Device[T], hidePrivate bool) (map[string]any, error) {
conf := dev.Config()
dc := map[string]any{
@ -102,7 +105,7 @@ func deviceConfigMap[T any](class templates.Class, dev config.Device[T]) (map[st
}
if conf.Type == typeTemplate {
params, err := sanitizeMasked(class, conf.Other)
params, err := sanitizeMasked(class, conf.Other, hidePrivate)
if err != nil {
return nil, err
}
@ -146,13 +149,13 @@ func deviceConfigMap[T any](class templates.Class, dev config.Device[T]) (map[st
return dc, nil
}
func deviceConfig[T any](class templates.Class, id int, h config.Handler[T]) (map[string]any, error) {
func deviceConfig[T any](class templates.Class, id int, h config.Handler[T], hidePrivate bool) (map[string]any, error) {
dev, err := h.ByName(config.NameForID(id))
if err != nil {
return nil, err
}
return deviceConfigMap(class, dev)
return deviceConfigMap(class, dev, hidePrivate)
}
// deviceConfigHandler returns a device configuration by class
@ -171,20 +174,23 @@ func deviceConfigHandler(w http.ResponseWriter, r *http.Request) {
return
}
// Check if private data should be hidden (default: true, showing private data)
hidePrivate := r.URL.Query().Get("private") == "false"
var res map[string]any
switch class {
case templates.Meter:
res, err = deviceConfig(class, id, config.Meters())
res, err = deviceConfig(class, id, config.Meters(), hidePrivate)
case templates.Charger:
res, err = deviceConfig(class, id, config.Chargers())
res, err = deviceConfig(class, id, config.Chargers(), hidePrivate)
case templates.Vehicle:
res, err = deviceConfig(class, id, config.Vehicles())
res, err = deviceConfig(class, id, config.Vehicles(), hidePrivate)
case templates.Circuit:
res, err = deviceConfig(class, id, config.Circuits())
res, err = deviceConfig(class, id, config.Circuits(), hidePrivate)
}
if err != nil {

View file

@ -133,7 +133,7 @@ func filterValidTemplateParams(tmpl *templates.Template, conf map[string]any) ma
return res
}
func sanitizeMasked(class templates.Class, conf map[string]any) (map[string]any, error) {
func sanitizeMasked(class templates.Class, conf map[string]any, hidePrivate bool) (map[string]any, error) {
tmpl, err := templateForConfig(class, conf)
if err != nil {
return nil, err
@ -142,8 +142,12 @@ func sanitizeMasked(class templates.Class, conf map[string]any) (map[string]any,
res := make(map[string]any, len(conf))
for k, v := range conf {
if i, p := tmpl.ParamByName(k); i >= 0 && p.IsMasked() {
v = masked
if i, p := tmpl.ParamByName(k); i >= 0 {
if p.IsMasked() {
v = masked
} else if hidePrivate && p.IsPrivate() {
v = masked
}
}
res[k] = v

View file

@ -5,7 +5,7 @@ import (
"net/http"
"os"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/redact"
)
// configYamlHandler returns the redacted evcc.yaml configuration file
@ -25,7 +25,7 @@ func configYamlHandler(configFilePath string) http.HandlerFunc {
}
// Redact sensitive information
redacted := util.RedactConfigString(string(src))
redacted := redact.String(string(src))
// Return the redacted content as plain text
w.Header().Set("Content-Type", "text/plain; charset=utf-8")

View file

@ -11,6 +11,7 @@ import (
"github.com/evcc-io/evcc/server/db/settings"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/redact"
"github.com/gorilla/mux"
"go.yaml.in/yaml/v4"
)
@ -18,6 +19,12 @@ import (
func settingsGetStringHandler(key string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
res, _ := settings.String(key)
// Check if private data should be hidden
if r.URL.Query().Get("private") == "false" && res != "" {
res = redact.String(res)
}
jsonWrite(w, res)
}
}