- refactor secrets

- add support for env in config
- redact sensitive information in logs/responses
This commit is contained in:
seydx
2025-05-26 21:56:45 +02:00
parent e8e798d955
commit bf45f64a7e
14 changed files with 202 additions and 160 deletions
+40 -122
View File
@@ -1,23 +1,19 @@
package app
import (
"fmt"
"regexp"
"strings"
"sync"
"github.com/AlexxIT/go2rtc/pkg/yaml"
)
var secrets = make(map[string]*Secret)
var secretsMu sync.Mutex
var templateRegex = regexp.MustCompile(`\{\{\s*([^\}]+)\s*\}\}`)
var (
secrets = make(map[string]*Secret)
secretsMu sync.Mutex
)
type Secrets interface {
Get(key string) any
Set(key string, value any)
Parse(template string) string
Marshal(v any) ([]byte, error)
Unmarshal(v any) error
Save() error
@@ -27,37 +23,36 @@ type Secret struct {
Secrets
Name string
Values map[string]any
Values map[string]string
}
func NewSecret(name string, values interface{}) *Secret {
func NewSecret(name string, values interface{}) (*Secret, error) {
secretsMu.Lock()
defer secretsMu.Unlock()
if s, exists := secrets[name]; exists {
return s
return s, nil
}
s := &Secret{Name: name, Values: make(map[string]any)}
s := &Secret{Name: name, Values: make(map[string]string)}
switch v := values.(type) {
case map[string]any:
s.Values = v
default:
data, err := yaml.Encode(values, 2)
if err == nil {
var mapValues map[string]any
if err := yaml.Unmarshal(data, &mapValues); err == nil {
s.Values = mapValues
}
}
data, err := yaml.Encode(values, 2)
if err != nil {
return nil, err
}
if err := yaml.Unmarshal(data, &s.Values); err != nil {
return nil, err
}
secrets[name] = s
return s
return s, nil
}
func GetSecret(name string) *Secret {
secretsMu.Lock()
defer secretsMu.Unlock()
return secrets[name]
}
@@ -65,79 +60,50 @@ func (s *Secret) Get(key string) any {
secretsMu.Lock()
defer secretsMu.Unlock()
if s.Values == nil {
return nil
}
return s.Values[key]
}
func (s *Secret) Set(key string, value any) {
func (s *Secret) Set(key string, value string) {
secretsMu.Lock()
defer secretsMu.Unlock()
if s.Values == nil {
s.Values = make(map[string]any)
s.Values = make(map[string]string)
}
s.Values[key] = value
secrets[s.Name] = s
}
func (s *Secret) Parse(template string) string {
if !templateRegex.MatchString(template) {
return template
}
secretsMu.Lock()
defer secretsMu.Unlock()
if _, exists := secrets[s.Name]; !exists {
return template
}
result := templateRegex.ReplaceAllStringFunc(template, func(match string) string {
varName := strings.TrimSpace(templateRegex.FindStringSubmatch(match)[1])
pathParts := strings.Split(varName, ".")
value := getNestedValue(s.Values, pathParts)
if value != nil {
return stringify(value)
}
return ""
})
return result
}
func (s *Secret) Marshal(v any) ([]byte, error) {
func (s *Secret) Marshal() (interface{}, error) {
secretsMu.Lock()
defer secretsMu.Unlock()
if s.Values == nil {
return nil, fmt.Errorf("no values in secret %s", s.Name)
return make(map[string]any), nil
}
data, err := yaml.Encode(s.Values, 2)
if err != nil {
return nil, fmt.Errorf("error encoding secret values: %w", err)
}
return data, nil
return s.Values, nil
}
func (s *Secret) Unmarshal(v any) error {
func (s *Secret) Unmarshal(value any) error {
secretsMu.Lock()
defer secretsMu.Unlock()
if s.Values == nil {
return fmt.Errorf("no values in secret %s", s.Name)
s.Values = make(map[string]string)
}
data, err := yaml.Encode(s.Values, 2)
data, err := yaml.Encode(value, 2)
if err != nil {
return fmt.Errorf("error encoding secret values: %w", err)
return err
}
if err := yaml.Unmarshal(data, v); err != nil {
return fmt.Errorf("error unmarshaling secret values: %w", err)
if err := yaml.Unmarshal(data, value); err != nil {
return err
}
return nil
@@ -151,17 +117,9 @@ func (s *Secret) Save() error {
func initSecrets() {
var cfg struct {
Secrets map[string]map[string]any `yaml:"secrets"`
Secrets map[string]map[string]string `yaml:"secrets"`
}
/*
Example config:
secrets:
test_camera:
username: test
password: test
*/
LoadConfig(&cfg)
if cfg.Secrets == nil {
@@ -172,53 +130,13 @@ func initSecrets() {
defer secretsMu.Unlock()
for name, values := range cfg.Secrets {
secrets[name] = &Secret{Name: name, Values: values}
}
}
func saveSecret(name string, secret map[string]any) error {
return PatchConfig([]string{"secrets", name}, secret)
}
func getNestedValue(m map[string]any, path []string) interface{} {
if len(path) == 0 || m == nil {
return nil
}
key := path[0]
value, exists := m[key]
if !exists {
return nil
}
if len(path) == 1 {
return value
}
// Check nested maps
switch nextMap := value.(type) {
case map[string]any:
return getNestedValue(nextMap, path[1:])
case map[interface{}]interface{}:
stringMap := make(map[string]any)
for k, v := range nextMap {
if keyStr, ok := k.(string); ok {
stringMap[keyStr] = v
}
secrets[name] = &Secret{
Name: name,
Values: values,
}
return getNestedValue(stringMap, path[1:])
default:
return nil
}
}
func stringify(value interface{}) string {
switch v := value.(type) {
case string:
return v
case int, int64, float64, bool:
return fmt.Sprintf("%v", v)
default:
return ""
}
func saveSecret(name string, secretValues map[string]string) error {
return PatchConfig([]string{"secrets", name}, secretValues)
}