github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/core/secrets/secretvalue.go (about) 1 // Copyright 2021 Canonical Ltd. 2 // Licensed under the LGPLv3, see LICENCE file for details. 3 4 package secrets 5 6 import ( 7 "bytes" 8 "encoding/base64" 9 "io" 10 "strings" 11 12 "github.com/juju/errors" 13 ) 14 15 // SecretValue holds the value of a secret. 16 // Instances of SecretValue are returned by a secret store 17 // when a secret look up is performed. The underlying value 18 // is a map of base64 encoded values represented as []byte. 19 type SecretValue interface { 20 // EncodedValues returns the key values of a secret as 21 // the raw base64 encoded strings. 22 // For the special case where the secret only has a 23 // single key value "data", then use BinaryValue() 24 //to get the result. 25 EncodedValues() map[string]string 26 27 // Values returns the key values of a secret as strings. 28 // For the special case where the secret only has a 29 // single key value "data", then use StringValue() 30 //to get the result. 31 Values() (map[string]string, error) 32 33 // KeyValue returns the specified secret value for the key. 34 // If the key has a #base64 suffix, the returned value is base64 encoded. 35 KeyValue(string) (string, error) 36 37 // IsEmpty checks if the value is empty. 38 IsEmpty() bool 39 } 40 41 type secretValue struct { 42 // Data holds the key values of a secret. 43 // We use a map to hold multiple values, eg cert and key 44 // The serialised form of any string values is a 45 // base64 encoded string, representing arbitrary values. 46 data map[string][]byte 47 } 48 49 // NewSecretValue returns a secret using the specified map of values. 50 // The map values are assumed to be already base64 encoded. 51 func NewSecretValue(data map[string]string) SecretValue { 52 dataCopy := make(map[string][]byte, len(data)) 53 for k, v := range data { 54 dataCopy[k] = append([]byte(nil), v...) 55 } 56 return &secretValue{data: dataCopy} 57 } 58 59 // NewSecretBytes returns a secret using the specified map of values. 60 // The map values are assumed to be already base64 encoded. 61 func NewSecretBytes(data map[string][]byte) SecretValue { 62 dataCopy := make(map[string][]byte, len(data)) 63 for k, v := range data { 64 dataCopy[k] = append([]byte(nil), v...) 65 } 66 return &secretValue{data: dataCopy} 67 } 68 69 // IsEmpty checks if the value is empty. 70 func (v secretValue) IsEmpty() bool { 71 return len(v.data) == 0 72 } 73 74 // EncodedValues implements SecretValue. 75 func (v *secretValue) EncodedValues() map[string]string { 76 dataCopy := make(map[string]string, len(v.data)) 77 for k, val := range v.data { 78 dataCopy[k] = string(val) 79 } 80 return dataCopy 81 } 82 83 // Values implements SecretValue. 84 func (v *secretValue) Values() (map[string]string, error) { 85 dataCopy := v.EncodedValues() 86 for k, v := range dataCopy { 87 data, err := base64.StdEncoding.DecodeString(v) 88 if err != nil { 89 return nil, errors.Trace(err) 90 } 91 dataCopy[k] = string(data) 92 } 93 return dataCopy, nil 94 } 95 96 // KeyValue implements SecretValue. 97 func (v *secretValue) KeyValue(key string) (string, error) { 98 useBase64 := false 99 if strings.HasSuffix(key, base64Suffix) { 100 key = strings.TrimSuffix(key, base64Suffix) 101 useBase64 = true 102 } 103 val, ok := v.data[key] 104 if !ok { 105 return "", errors.NotFoundf("secret key value %q", key) 106 } 107 // The stored value is always base64 encoded. 108 if useBase64 { 109 return string(val), nil 110 } 111 b64 := base64.NewDecoder(base64.StdEncoding, bytes.NewReader(val)) 112 result, err := io.ReadAll(b64) 113 if err != nil { 114 return "", errors.Trace(err) 115 } 116 return string(result), nil 117 }