github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/config/handler/handler.go (about) 1 package handler 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/base64" 7 "encoding/json" 8 "errors" 9 "fmt" 10 "strings" 11 "sync" 12 13 pb "github.com/tickoalcantara12/micro/v3/proto/config" 14 "github.com/tickoalcantara12/micro/v3/service/config" 15 merrors "github.com/tickoalcantara12/micro/v3/service/errors" 16 "github.com/tickoalcantara12/micro/v3/service/logger" 17 "github.com/tickoalcantara12/micro/v3/service/store" 18 "github.com/tickoalcantara12/micro/v3/util/auth/namespace" 19 ) 20 21 const ( 22 defaultNamespace = "micro" 23 pathSplitter = "." 24 ) 25 26 var ( 27 // we now support json only 28 mtx sync.RWMutex 29 ) 30 31 type Config struct { 32 secret []byte 33 } 34 35 func NewConfig(key string) *Config { 36 var dec []byte 37 var err error 38 if len(key) == 0 { 39 logger.Warn("No encryption key provided") 40 } else { 41 dec, err = base64.StdEncoding.DecodeString(key) 42 if err != nil { 43 logger.Warnf("Error decoding key: %v", err) 44 } 45 } 46 47 return &Config{ 48 secret: dec, 49 } 50 } 51 52 func (c *Config) Get(ctx context.Context, req *pb.GetRequest, rsp *pb.GetResponse) error { 53 if len(req.Namespace) == 0 { 54 req.Namespace = defaultNamespace 55 } 56 57 // authorize the request 58 if err := namespace.AuthorizeAdmin(ctx, req.Namespace, "config.Config.Get"); err != nil { 59 return err 60 } 61 62 ch, err := store.Read(req.Namespace) 63 if err == store.ErrNotFound { 64 return merrors.NotFound("config.Config.Get", "Not found") 65 } else if err != nil { 66 return merrors.BadRequest("config.Config.Get", "read error: %v: %v", err, req.Namespace) 67 } 68 69 // get secret from options 70 var secret bool 71 if req.GetOptions().GetSecret() { 72 secret = true 73 } 74 75 rsp.Value = &pb.Value{} 76 77 values := config.NewJSONValues(ch[0].Value) 78 79 var bs []byte 80 if len(req.Path) > 0 { 81 bs = values.Get(req.Path).Bytes() 82 } else { 83 bs = values.Bytes() 84 } 85 dat, err := leavesToValues(string(bs), secret, string(c.secret)) 86 if err != nil { 87 return merrors.InternalServerError("config.config.Get", "Error in config structure: %v", err) 88 } 89 90 buf := new(bytes.Buffer) 91 enc := json.NewEncoder(buf) 92 enc.SetEscapeHTML(false) 93 err = enc.Encode(dat) 94 if err != nil { 95 return merrors.BadRequest("config.Config.Get", "JSOn encode error: %v", err) 96 } 97 rsp.Value.Data = strings.TrimSpace(buf.String()) 98 99 return nil 100 } 101 102 // Read method is only here for backwards compatibility 103 func (c *Config) Read(ctx context.Context, req *pb.ReadRequest, rsp *pb.ReadResponse) error { 104 logger.Info("doing config read", req.Path, req.Namespace) 105 if len(req.Namespace) == 0 { 106 req.Namespace = defaultNamespace 107 } 108 109 // authorize the request 110 if err := namespace.AuthorizeAdmin(ctx, req.Namespace, "config.Config.Read"); err != nil { 111 return err 112 } 113 114 ch, err := store.Read(req.Namespace) 115 if err == store.ErrNotFound { 116 return merrors.NotFound("config.Config.Read", "Not found") 117 } else if err != nil { 118 return merrors.BadRequest("config.Config.Read", "read error: %v: %v", err, req.Namespace) 119 } 120 121 rsp.Change = &pb.Change{ 122 Namespace: req.Namespace, 123 Path: req.Path, 124 ChangeSet: &pb.ChangeSet{}, 125 } 126 127 values := config.NewJSONValues(ch[0].Value) 128 129 var bs []byte 130 if len(req.Path) > 0 { 131 bs = values.Get(req.Path).Bytes() 132 } else { 133 bs = values.Bytes() 134 } 135 136 dat, err := leavesToValues(string(bs), false, string(c.secret)) 137 if err != nil { 138 return merrors.InternalServerError("config.config.Read", "Error in config structure: %v", err) 139 } 140 141 buf := new(bytes.Buffer) 142 enc := json.NewEncoder(buf) 143 enc.SetEscapeHTML(false) 144 err = enc.Encode(dat) 145 if err != nil { 146 return merrors.BadRequest("config.Config.Read", "JSOn encode error: %v", err) 147 } 148 rsp.Change.ChangeSet.Data = strings.TrimSpace(buf.String()) 149 rsp.Change.ChangeSet.Format = "json" 150 151 return nil 152 } 153 154 func leavesToValues(data string, decodeSecrets bool, encryptionKey string) (interface{}, error) { 155 var m interface{} 156 err := json.Unmarshal([]byte(data), &m) 157 if err != nil { 158 return m, err 159 } 160 return traverse(m, decodeSecrets, encryptionKey) 161 } 162 163 func traverseMaps(m map[string]interface{}, paths []string, callback func(path string, value interface{}) error) error { 164 for k, v := range m { 165 val, ok := v.(map[string]interface{}) 166 if !ok { 167 err := callback(strings.Join(append(paths, k), "."), v) 168 if err != nil { 169 return err 170 } 171 continue 172 } 173 err := traverseMaps(val, append(paths, k), callback) 174 if err != nil { 175 return err 176 } 177 } 178 return nil 179 } 180 181 func traverse(i interface{}, decodeSecrets bool, encryptionKey string) (interface{}, error) { 182 switch v := i.(type) { 183 case map[string]interface{}: 184 if val, ok := v["leaf"].(bool); ok && val { 185 isSecret, isSecretOk := v["secret"].(bool) 186 if isSecretOk && isSecret && !decodeSecrets { 187 return "[secret]", nil 188 } 189 marshalledValue, ok := v["value"].(string) 190 if !ok { 191 return nil, fmt.Errorf("Value field in leaf %v can't be found", v) 192 } 193 if isSecretOk && isSecret { 194 if len(encryptionKey) == 0 { 195 return nil, errors.New("Can't decode secret: secret key is not set") 196 } 197 dec, err := base64.StdEncoding.DecodeString(marshalledValue) 198 if err != nil { 199 return nil, errors.New("Badly encoded secret") 200 } 201 decrypted, err := decrypt(string(dec), []byte(encryptionKey)) 202 if err != nil { 203 return nil, fmt.Errorf("Failed to decrypt: %v", err) 204 } 205 marshalledValue = decrypted 206 } 207 var value interface{} 208 err := json.Unmarshal([]byte(marshalledValue), &value) 209 return value, err 210 } 211 ret := map[string]interface{}{} 212 for key, val := range v { 213 value, err := traverse(val, decodeSecrets, encryptionKey) 214 if err != nil { 215 return ret, err 216 } 217 ret[key] = value 218 } 219 return ret, nil 220 case []interface{}: 221 for _, e := range v { 222 ret := []interface{}{} 223 value, err := traverse(e, decodeSecrets, encryptionKey) 224 if err != nil { 225 return ret, err 226 } 227 ret = append(ret, value) 228 return ret, nil 229 } 230 default: 231 return i, nil 232 } 233 return i, nil 234 } 235 236 func (c *Config) Set(ctx context.Context, req *pb.SetRequest, rsp *pb.SetResponse) error { 237 if req.Value == nil { 238 return merrors.BadRequest("config.Config.Update", "invalid change") 239 } 240 ns := req.Namespace 241 if len(ns) == 0 { 242 ns = defaultNamespace 243 } 244 245 // authorize the request 246 if err := namespace.AuthorizeAdmin(ctx, ns, "config.Config.Set"); err != nil { 247 return err 248 } 249 250 ch, err := store.Read(ns) 251 dat := []byte{} 252 if err == store.ErrNotFound { 253 dat = []byte("{}") 254 } else if err != nil { 255 return merrors.BadRequest("config.Config.Set", "read error: %v: %v", err, ns) 256 } 257 258 if len(ch) > 0 { 259 dat = ch[0].Value 260 } 261 values := config.NewJSONValues(dat) 262 263 var secret bool 264 if req.GetOptions().GetSecret() { 265 secret = true 266 } 267 268 // req.Value.Data is a json encoded value 269 data := req.Value.Data 270 var i interface{} 271 err = json.Unmarshal([]byte(data), &i) 272 if err != nil { 273 return merrors.BadRequest("config.Config.Set", "Request is invalid JSON: %v", err) 274 } 275 m, ok := i.(map[string]interface{}) 276 // If it's a map, we do a merge 277 if ok { 278 // Need to nuke top level metadata as traverseMaps won't handle this 279 cleanNode(values, req.Path) 280 err = traverseMaps(m, strings.Split(req.Path, "."), func(p string, value interface{}) error { 281 val, err := json.Marshal(value) 282 if err != nil { 283 return err 284 } 285 return c.setValue(values, secret, p, string(val)) 286 }) 287 } else { 288 err = c.setValue(values, secret, req.Path, data) 289 } 290 291 return store.Write(&store.Record{ 292 Key: req.Namespace, 293 Value: values.Bytes(), 294 }) 295 } 296 297 func cleanNode(values *config.JSONValues, path string) { 298 // Whatever the new value is being set, we need to delete 299 // old metadat to prevent making a mess and introducing weird bugs, 300 // ie. see `TestConfig/Test_plain_old_type_being_overwritten_by_map` 301 values.Delete(path + ".leaf") 302 values.Delete(path + ".value") 303 values.Delete(path + ".secret") 304 } 305 306 func (c *Config) setValue(values *config.JSONValues, secret bool, path, data string) error { 307 cleanNode(values, path) 308 if secret { 309 if len(c.secret) == 0 { 310 return merrors.InternalServerError("config.Config.Set", "Can't encode secret: secret key is not set") 311 } 312 encrypted, err := encrypt(data, c.secret) 313 if err != nil { 314 return merrors.InternalServerError("config.Config.Set", "Failed to encrypt: %v", err) 315 } 316 data = string(base64.StdEncoding.EncodeToString([]byte(encrypted))) 317 // Need to save metainformation with secret values too 318 values.Set(path, map[string]interface{}{ 319 "secret": true, 320 "value": data, 321 "leaf": true, 322 }) 323 } else { 324 values.Set(path, map[string]interface{}{ 325 "value": data, 326 "leaf": true, 327 }) 328 } 329 return nil 330 } 331 332 func (c *Config) Delete(ctx context.Context, req *pb.DeleteRequest, rsp *pb.DeleteResponse) error { 333 ns := req.Namespace 334 if len(ns) == 0 { 335 ns = defaultNamespace 336 } 337 338 // authorize the request 339 if err := namespace.AuthorizeAdmin(ctx, ns, "config.Config.Delete"); err != nil { 340 return err 341 } 342 343 ch, err := store.Read(ns) 344 if err == store.ErrNotFound { 345 return merrors.NotFound("config.Config.Delete", "Not found") 346 } else if err != nil { 347 return merrors.BadRequest("config.Config.Delete", "read error: %v: %v", err, ns) 348 } 349 350 values := config.NewJSONValues(ch[0].Value) 351 352 values.Delete(req.Path) 353 return store.Write(&store.Record{ 354 Key: ns, 355 Value: values.Bytes(), 356 }) 357 }