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  }