github.com/influxdata/influxdb/v2@v2.7.6/secret/storage.go (about) 1 package secret 2 3 import ( 4 "context" 5 "encoding/base64" 6 "errors" 7 8 "github.com/influxdata/influxdb/v2" 9 "github.com/influxdata/influxdb/v2/kit/platform" 10 errors2 "github.com/influxdata/influxdb/v2/kit/platform/errors" 11 "github.com/influxdata/influxdb/v2/kv" 12 ) 13 14 var secretBucket = []byte("secretsv1") 15 16 // Storage is a store translation layer between the data storage unit and the 17 // service layer. 18 type Storage struct { 19 store kv.Store 20 } 21 22 // NewStore creates a new storage system 23 func NewStore(s kv.Store) (*Storage, error) { 24 return &Storage{s}, nil 25 } 26 27 func (s *Storage) View(ctx context.Context, fn func(kv.Tx) error) error { 28 return s.store.View(ctx, fn) 29 } 30 31 func (s *Storage) Update(ctx context.Context, fn func(kv.Tx) error) error { 32 return s.store.Update(ctx, fn) 33 } 34 35 // GetSecret Returns the value of a secret 36 func (s *Storage) GetSecret(ctx context.Context, tx kv.Tx, orgID platform.ID, k string) (string, error) { 37 key, err := encodeSecretKey(orgID, k) 38 if err != nil { 39 return "", err 40 } 41 42 b, err := tx.Bucket(secretBucket) 43 if err != nil { 44 return "", err 45 } 46 47 val, err := b.Get(key) 48 if kv.IsNotFound(err) { 49 return "", &errors2.Error{ 50 Code: errors2.ENotFound, 51 Msg: influxdb.ErrSecretNotFound, 52 } 53 } 54 55 if err != nil { 56 return "", err 57 } 58 59 v, err := decodeSecretValue(val) 60 if err != nil { 61 return "", err 62 } 63 64 return v, nil 65 } 66 67 // ListSecrets returns a list of secret keys 68 func (s *Storage) ListSecret(ctx context.Context, tx kv.Tx, orgID platform.ID) ([]string, error) { 69 b, err := tx.Bucket(secretBucket) 70 if err != nil { 71 return nil, err 72 } 73 74 prefix, err := orgID.Encode() 75 if err != nil { 76 return nil, err 77 } 78 79 cur, err := b.ForwardCursor(prefix, kv.WithCursorPrefix(prefix)) 80 if err != nil { 81 return nil, err 82 } 83 84 keys := []string{} 85 86 err = kv.WalkCursor(ctx, cur, func(k, v []byte) (bool, error) { 87 id, key, err := decodeSecretKey(k) 88 if err != nil { 89 return false, err 90 } 91 92 if id != orgID { 93 // We've reached the end of the keyspace for the provided orgID 94 return false, nil 95 } 96 97 keys = append(keys, key) 98 99 return true, nil 100 }) 101 if err != nil { 102 return nil, err 103 } 104 105 return keys, nil 106 } 107 108 // PutSecret sets a secret in the db. 109 func (s *Storage) PutSecret(ctx context.Context, tx kv.Tx, orgID platform.ID, k, v string) error { 110 key, err := encodeSecretKey(orgID, k) 111 if err != nil { 112 return err 113 } 114 115 val := encodeSecretValue(v) 116 117 b, err := tx.Bucket(secretBucket) 118 if err != nil { 119 return err 120 } 121 122 if err := b.Put(key, val); err != nil { 123 return err 124 } 125 126 return nil 127 } 128 129 // DeleteSecret removes a secret for the db 130 func (s *Storage) DeleteSecret(ctx context.Context, tx kv.Tx, orgID platform.ID, k string) error { 131 key, err := encodeSecretKey(orgID, k) 132 if err != nil { 133 return err 134 } 135 136 b, err := tx.Bucket(secretBucket) 137 if err != nil { 138 return err 139 } 140 141 return b.Delete(key) 142 } 143 144 func encodeSecretKey(orgID platform.ID, k string) ([]byte, error) { 145 buf, err := orgID.Encode() 146 if err != nil { 147 return nil, err 148 } 149 150 key := make([]byte, 0, platform.IDLength+len(k)) 151 key = append(key, buf...) 152 key = append(key, k...) 153 154 return key, nil 155 } 156 157 func decodeSecretKey(key []byte) (platform.ID, string, error) { 158 if len(key) < platform.IDLength { 159 // This should not happen. 160 return platform.InvalidID(), "", errors.New("provided key is too short to contain an ID (please report this error)") 161 } 162 163 var id platform.ID 164 if err := id.Decode(key[:platform.IDLength]); err != nil { 165 return platform.InvalidID(), "", err 166 } 167 168 k := string(key[platform.IDLength:]) 169 170 return id, k, nil 171 } 172 173 func decodeSecretValue(val []byte) (string, error) { 174 // store the secret value base64 encoded so that it's marginally better than plaintext 175 v, err := base64.StdEncoding.DecodeString(string(val)) 176 if err != nil { 177 return "", err 178 } 179 180 return string(v), nil 181 } 182 183 func encodeSecretValue(v string) []byte { 184 val := make([]byte, base64.StdEncoding.EncodedLen(len(v))) 185 base64.StdEncoding.Encode(val, []byte(v)) 186 return val 187 }