diff --git a/cache/config_file.go b/cache/config_file.go index fb8d365db..265915855 100644 --- a/cache/config_file.go +++ b/cache/config_file.go @@ -90,13 +90,35 @@ type Entry struct { Content string Md5 string Version uint64 - DataKey string + Tags []*model.ConfigFileTag // 创建的时候,设置过期时间 ExpireTime time.Time // 标识是否是空缓存 Empty bool } +func (e *Entry) GetDataKey() string { + for _, tag := range e.Tags { + if tag.Key == utils.ConfigFileTagKeyDataKey { + return tag.Value + } + } + return "" +} + +func (e *Entry) GetEncryptAlgo() string { + for _, tag := range e.Tags { + if tag.Key == utils.ConfigFileTagKeyEncryptAlgo { + return tag.Value + } + } + return "" +} + +func (e *Entry) Encrypted() bool { + return e.GetDataKey() != "" +} + // newFileCache 创建文件缓存 func newFileCache(ctx context.Context, storage store.Store) FileCache { cache := &fileCache{ @@ -240,9 +262,9 @@ func (fc *fileCache) GetOrLoadIfAbsent(namespace, group, fileName string) (*Entr // 从数据库中加载数据 atomic.AddInt32(&fc.loadCnt, 1) - file, dataKey, err := fc.getConfigFileReleaseAndDataKey(namespace, group, fileName) + file, tags, err := fc.getConfigFileReleaseAndTags(namespace, group, fileName) if err != nil { - configLog.Error("[Config][Cache] load config file release error.", + configLog.Error("[Config][Cache] load config file release and tags error.", zap.String("namespace", namespace), zap.String("group", group), zap.String("fileName", fileName), @@ -270,7 +292,7 @@ func (fc *fileCache) GetOrLoadIfAbsent(namespace, group, fileName string) (*Entr Content: file.Content, Md5: file.Md5, Version: file.Version, - DataKey: dataKey, + Tags: tags, ExpireTime: fc.getExpireTime(), } @@ -289,8 +311,8 @@ func (fc *fileCache) GetOrLoadIfAbsent(namespace, group, fileName string) (*Entr return newEntry, nil } -func (fc *fileCache) getConfigFileReleaseAndDataKey( - namespace, group, fileName string) (*model.ConfigFileRelease, string, error) { +func (fc *fileCache) getConfigFileReleaseAndTags( + namespace, group, fileName string) (*model.ConfigFileRelease, []*model.ConfigFileTag, error) { file, err := fc.storage.GetConfigFileRelease(nil, namespace, group, fileName) if err != nil { configLog.Error("[Config][Cache] load config file release error.", @@ -298,7 +320,7 @@ func (fc *fileCache) getConfigFileReleaseAndDataKey( zap.String("group", group), zap.String("fileName", fileName), zap.Error(err)) - return nil, "", err + return nil, nil, err } tags, err := fc.storage.QueryTagByConfigFile(namespace, group, fileName) if err != nil { @@ -307,15 +329,9 @@ func (fc *fileCache) getConfigFileReleaseAndDataKey( zap.String("group", group), zap.String("fileName", fileName), zap.Error(err)) - return nil, "", err - } - var dataKey string - for _, tag := range tags { - if tag.Key == utils.ConfigFileTagKeyDataKey { - dataKey = tag.Value - } + return nil, nil, err } - return file, dataKey, nil + return file, tags, nil } // Remove 删除缓存对象 diff --git a/config/bootstrap_test.go b/config/bootstrap_test.go index b64f01661..f8fa68cd0 100644 --- a/config/bootstrap_test.go +++ b/config/bootstrap_test.go @@ -324,7 +324,7 @@ func assembleConfigFile() *apiconfig.ConfigFile { func assembleEncryptConfigFile() *apiconfig.ConfigFile { configFile := assembleConfigFile() - configFile.IsEncrypted = utils.NewBoolValue(true) + configFile.Encrypted = utils.NewBoolValue(true) configFile.EncryptAlgo = utils.NewStringValue("AES") return configFile } diff --git a/config/client_config_file.go b/config/client_config_file.go index 25ef93c3a..04f1f063a 100644 --- a/config/client_config_file.go +++ b/config/client_config_file.go @@ -61,12 +61,10 @@ func (s *Server) GetConfigFileForClient(ctx context.Context, // 从缓存中获取配置内容 entry, err := s.fileCache.GetOrLoadIfAbsent(namespace, group, fileName) - if err != nil { log.Error("[Config][Service] get or load config file from cache error.", zap.String("requestId", requestID), zap.Error(err)) - return api.NewConfigClientResponseWithMessage( apimodel.Code_ExecuteException, "load config file error") } @@ -94,41 +92,15 @@ func (s *Server) GetConfigFileForClient(ctx context.Context, zap.String("file", fileName), zap.Uint64("version", entry.Version)) - // 加密配置返回用公钥加密的数据密钥 - if entry.DataKey != "" && publicKey != "" { - dataKeyBytes, err := base64.StdEncoding.DecodeString(entry.DataKey) - if err != nil { - log.Error("[Config][Service] base64 decode data key error.", - zap.String("requestId", requestID), - zap.String("dataKey", entry.DataKey), - zap.Error(err)) - } - cipherDataKey, err := rsa.EncryptToBase64(dataKeyBytes, publicKey) - if err != nil { - log.Error("[Config][Service] rsa encrypt data key error.", - zap.String("requestId", requestID), - zap.String("dataKey", entry.DataKey), - zap.Error(err)) - } - resp := utils2.GenConfigFileResponse(namespace, group, fileName, - entry.Content, entry.Md5, entry.Version, true, cipherDataKey) - log.Info("[Config][Client] client get config file resp.", + configFile, err := transferEntry2APIModel(client, entry) + if err != nil { + log.Error("[Config][Service] transfer entry to api model error.", zap.String("requestId", requestID), - zap.String("file", resp.GetConfigFile().GetFileName().GetValue()), - zap.String("dataKey", resp.GetConfigFile().GetDataKey().GetValue()), - zap.Bool("isEncrypted", resp.GetConfigFile().GetIsEncrypted().GetValue())) - return resp + zap.Error(err)) + return api.NewConfigClientResponseWithMessage( + apimodel.Code_ExecuteException, "transfer entry to api model error") } - isEntrypted := entry.DataKey != "" - resp := utils2.GenConfigFileResponse(namespace, group, fileName, - entry.Content, entry.Md5, entry.Version, isEntrypted, "") - - log.Info("[Config][Client] client get config file resp.", - zap.String("requestId", requestID), - zap.String("file", resp.GetConfigFile().GetFileName().GetValue()), - zap.String("dataKey", resp.GetConfigFile().GetDataKey().GetValue()), - zap.Bool("isEncrypted", resp.GetConfigFile().GetIsEncrypted().GetValue())) - return resp + return api.NewConfigClientResponse(apimodel.Code_ExecuteSuccess, configFile) } // CreateConfigFileFromClient 调用config_file接口获取配置文件 @@ -211,9 +183,65 @@ func (s *Server) doCheckClientConfigFile(ctx context.Context, configFiles []*api } if compartor(configFile, entry) { - return utils2.GenConfigFileResponse(namespace, group, fileName, "", entry.Md5, entry.Version, false, "") + return utils2.GenConfigFileResponse(namespace, group, fileName, "", entry.Md5, entry.Version) } } return api.NewConfigClientResponse(apimodel.Code_DataNoChange, nil) } + +func transferEntry2APIModel(client *apiconfig.ClientConfigFileInfo, + entry *cache.Entry) (*apiconfig.ClientConfigFileInfo, error) { + namespace := client.GetNamespace().GetValue() + group := client.GetGroup().GetValue() + fileName := client.GetFileName().GetValue() + publicKey := client.GetPublicKey().GetValue() + + configFile := &apiconfig.ClientConfigFileInfo{ + Namespace: utils.NewStringValue(namespace), + Group: utils.NewStringValue(group), + FileName: utils.NewStringValue(fileName), + Content: utils.NewStringValue(entry.Content), + Version: utils.NewUInt64Value(entry.Version), + Md5: utils.NewStringValue(entry.Md5), + Encrypted: utils.NewBoolValue(entry.Encrypted()), + } + for _, tag := range entry.Tags { + if tag.Key != utils.ConfigFileTagKeyDataKey && tag.Key != utils.ConfigFileTagKeyEncryptAlgo { + configFile.Tags = append(configFile.Tags, &apiconfig.ConfigFileTag{ + Key: utils.NewStringValue(tag.Key), + Value: utils.NewStringValue(tag.Value), + }) + } + } + + dataKey := entry.GetDataKey() + encryptAlgo := entry.GetEncryptAlgo() + if dataKey != "" && publicKey != "" { + dataKeyBytes, err := base64.StdEncoding.DecodeString(dataKey) + if err != nil { + log.Error("[Config][Service] base64 decode data key error.", + zap.String("dataKey", dataKey), + zap.Error(err)) + return nil, err + } + cipherDataKey, err := rsa.EncryptToBase64(dataKeyBytes, publicKey) + if err != nil { + log.Error("[Config][Service] rsa encrypt data key error.", + zap.String("dataKey", dataKey), + zap.Error(err)) + } + configFile.Tags = append(configFile.Tags, + &apiconfig.ConfigFileTag{ + Key: utils.NewStringValue(utils.ConfigFileTagKeyDataKey), + Value: utils.NewStringValue(cipherDataKey), + }, + &apiconfig.ConfigFileTag{ + Key: utils.NewStringValue(utils.ConfigFileTagKeyEncryptAlgo), + Value: utils.NewStringValue(encryptAlgo), + }, + ) + return configFile, nil + } + return configFile, nil +} diff --git a/config/config_file.go b/config/config_file.go index f581a5b97..c9906c8a8 100644 --- a/config/config_file.go +++ b/config/config_file.go @@ -68,7 +68,7 @@ func (s *Server) CreateConfigFile(ctx context.Context, configFile *apiconfig.Con } // 配置加密 - if configFile.IsEncrypted.GetValue() && configFile.EncryptAlgo.GetValue() != "" { + if configFile.Encrypted.GetValue() && configFile.EncryptAlgo.GetValue() != "" { if err := s.encryptConfigFile(ctx, configFile, configFile.EncryptAlgo.GetValue(), ""); err != nil { log.Error("[Config][Service] encrypt config file error.", utils.ZapRequestID(requestID), @@ -112,7 +112,7 @@ func (s *Server) CreateConfigFile(ctx context.Context, configFile *apiconfig.Con retConfigFile := transferConfigFileStoreModel2APIModel(createdFile) - if configFile.IsEncrypted.GetValue() && configFile.EncryptAlgo.GetValue() != "" { + if configFile.Encrypted.GetValue() && configFile.EncryptAlgo.GetValue() != "" { if err := s.decryptConfigFile(ctx, retConfigFile); err != nil { log.Error("[Config][Service] decrypt config file error.", utils.ZapRequestID(requestID), diff --git a/config/config_file_test.go b/config/config_file_test.go index 0c9145f38..0b4bdf775 100644 --- a/config/config_file_test.go +++ b/config/config_file_test.go @@ -386,7 +386,7 @@ func TestConfigFileCRUD(t *testing.T) { t.Run("step12-create-entrypted", func(t *testing.T) { configFile := assembleConfigFile() - configFile.IsEncrypted = utils.NewBoolValue(true) + configFile.Encrypted = utils.NewBoolValue(true) configFile.EncryptAlgo = utils.NewStringValue("AES") rsp := testSuit.testService.CreateConfigFile(testSuit.defaultCtx, configFile) assert.Equal(t, api.ExecuteSuccess, rsp.Code.GetValue()) @@ -768,30 +768,35 @@ func TestServer_GetConfigFileBaseInfo(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - Convey("获取配置文件", t, func() { + Convey("获取配置文件基本信息", t, func() { + Convey("存储层-查询配置文件-返回error", func() { + storage := storemock.NewMockStore(ctrl) + storage.EXPECT().GetConfigFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(nil, errors.New("mock storage error")) + testSuit.testServer.storage = storage + + configFile := assembleConfigFile() + got := testSuit.testService.GetConfigFileBaseInfo(testSuit.defaultCtx, configFile.Namespace.Value, configFile.Group.Value, configFile.Name.Value) + So(apimodel.Code_StoreLayerException, ShouldEqual, apimodel.Code(got.GetCode().GetValue())) + }) + Convey("解密配置文件-返回error", func() { + storage := storemock.NewMockStore(ctrl) + storage.EXPECT().GetConfigFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(&model.ConfigFile{ + CreateBy: operator, + }, nil) + storage.EXPECT().QueryTagByConfigFile(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return([]*model.ConfigFileTag{{Key: utils.ConfigFileTagKeyDataKey, Value: "abc"}}, nil) + testSuit.testServer.storage = storage + crypto := &aes.AESCrypto{} encryptFunc := ApplyMethod(reflect.TypeOf(crypto), "Decrypt", func(_ *aes.AESCrypto, plaintext string, key []byte) (string, error) { return "", errors.New("mock encrypt error") }) defer encryptFunc.Reset() - configFile := assembleEncryptConfigFile() - testSuit.defaultCtx = context.WithValue(testSuit.defaultCtx, utils.ContextUserNameKey, configFile.CreateBy.GetValue()) - got := testSuit.testService.CreateConfigFile(testSuit.defaultCtx, configFile) + configFile := assembleConfigFile() + got := testSuit.testService.GetConfigFileBaseInfo(testSuit.defaultCtx, configFile.Namespace.Value, configFile.Group.Value, configFile.Name.Value) So(apimodel.Code_DecryptConfigFileException, ShouldEqual, apimodel.Code(got.GetCode().GetValue())) }) - Convey("存储层-查询配置文件-返回error", func() { - storage := storemock.NewMockStore(ctrl) - storage.EXPECT().GetConfigFileGroup(gomock.Any(), gomock.Any()).AnyTimes().Return(&model.ConfigFileGroup{}, nil) - storage.EXPECT().GetConfigFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(nil, errors.New("mock storage error")) - testSuit.testServer.storage = storage - - configFile := assembleConfigFile() - - got := testSuit.testService.CreateConfigFile(testSuit.defaultCtx, configFile) - So(apimodel.Code_StoreLayerException, ShouldEqual, apimodel.Code(got.GetCode().GetValue())) - }) }) } diff --git a/config/utils/utils.go b/config/utils/utils.go index 82281b45d..5b785b437 100644 --- a/config/utils/utils.go +++ b/config/utils/utils.go @@ -97,16 +97,14 @@ func CheckContentLength(content string) error { // GenConfigFileResponse 为客户端生成响应对象 func GenConfigFileResponse(namespace, group, fileName, content, md5str string, - version uint64, isEncrypted bool, dataKey string) *apiconfig.ConfigClientResponse { + version uint64) *apiconfig.ConfigClientResponse { configFile := &apiconfig.ClientConfigFileInfo{ - Namespace: utils.NewStringValue(namespace), - Group: utils.NewStringValue(group), - FileName: utils.NewStringValue(fileName), - Content: utils.NewStringValue(content), - Version: utils.NewUInt64Value(version), - Md5: utils.NewStringValue(md5str), - IsEncrypted: utils.NewBoolValue(isEncrypted), - DataKey: utils.NewStringValue(dataKey), + Namespace: utils.NewStringValue(namespace), + Group: utils.NewStringValue(group), + FileName: utils.NewStringValue(fileName), + Content: utils.NewStringValue(content), + Version: utils.NewUInt64Value(version), + Md5: utils.NewStringValue(md5str), } return api.NewConfigClientResponse(apimodel.Code_ExecuteSuccess, configFile) } diff --git a/config/watcher.go b/config/watcher.go index ceea7cb5f..5be09e3ad 100644 --- a/config/watcher.go +++ b/config/watcher.go @@ -147,7 +147,7 @@ func (wc *watchCenter) notifyToWatchers(publishConfigFile *model.ConfigFileRelea } response := utils2.GenConfigFileResponse(publishConfigFile.Namespace, publishConfigFile.Group, - publishConfigFile.FileName, "", publishConfigFile.Md5, publishConfigFile.Version, false, "") + publishConfigFile.FileName, "", publishConfigFile.Md5, publishConfigFile.Version) watcherMap := watchers.(*sync.Map) watcherMap.Range(func(clientId, watchCtx interface{}) bool { diff --git a/go.mod b/go.mod index b42c51216..d2e3a1d60 100644 --- a/go.mod +++ b/go.mod @@ -84,7 +84,7 @@ require ( require ( github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/agiledragon/gomonkey/v2 v2.10.1 - github.com/polarismesh/specification v1.3.2-alpha.1 + github.com/polarismesh/specification v1.3.2-alpha.2 ) require gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect diff --git a/go.sum b/go.sum index 29d34a528..be3c479e2 100644 --- a/go.sum +++ b/go.sum @@ -323,8 +323,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/polarismesh/go-restful-openapi/v2 v2.0.0-20220928152401-083908d10219 h1:XnFyNUWnciM6zgXaz6tm+Egs35rhoD0KGMmKh4gCdi0= github.com/polarismesh/go-restful-openapi/v2 v2.0.0-20220928152401-083908d10219/go.mod h1:4WhwBysTom9Eoy0hQ4W69I0FmO+T0EpjEW9/5sgHoUk= -github.com/polarismesh/specification v1.3.2-alpha.1 h1:+VWCtValTedIrNYaOPhpT8Lt1At+mhimUs9yj0Darsc= -github.com/polarismesh/specification v1.3.2-alpha.1/go.mod h1:rDvMMtl5qebPmqiBLNa5Ps0XtwkP31ZLirbH4kXA0YU= +github.com/polarismesh/specification v1.3.2-alpha.2 h1:cMghyvCnRVM5ca2kYCGHOgIIxVnokiMvw0720q8a8RA= +github.com/polarismesh/specification v1.3.2-alpha.2/go.mod h1:rDvMMtl5qebPmqiBLNa5Ps0XtwkP31ZLirbH4kXA0YU= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= diff --git a/plugin/crypto/aes/aes.go b/plugin/crypto/aes/aes.go index 2dcff19d5..4bd2ce35c 100644 --- a/plugin/crypto/aes/aes.go +++ b/plugin/crypto/aes/aes.go @@ -68,7 +68,7 @@ func (c *AESCrypto) GenerateKey() ([]byte, error) { // Encrypt AES encrypt plaintext and base64 encode ciphertext func (c *AESCrypto) Encrypt(plaintext string, key []byte) (string, error) { - ciphertext, err := c.encrypt([]byte(plaintext), key) + ciphertext, err := c.doEncrypt([]byte(plaintext), key) if err != nil { return "", err } @@ -81,7 +81,7 @@ func (c *AESCrypto) Decrypt(ciphertext string, key []byte) (string, error) { if err != nil { return "", err } - plaintext, err := c.decrypt(ciphertextBytes, key) + plaintext, err := c.doDecrypt(ciphertextBytes, key) if err != nil { return "", err } @@ -89,7 +89,7 @@ func (c *AESCrypto) Decrypt(ciphertext string, key []byte) (string, error) { } // Encrypt AES encryption -func (c *AESCrypto) encrypt(plaintext []byte, key []byte) ([]byte, error) { +func (c *AESCrypto) doEncrypt(plaintext []byte, key []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err @@ -103,7 +103,7 @@ func (c *AESCrypto) encrypt(plaintext []byte, key []byte) ([]byte, error) { } // Decrypt AES decryption -func (c *AESCrypto) decrypt(ciphertext []byte, key []byte) ([]byte, error) { +func (c *AESCrypto) doDecrypt(ciphertext []byte, key []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err diff --git a/plugin/crypto/aes/aes_test.go b/plugin/crypto/aes/aes_test.go index 5fae98555..0d7b2c1ab 100644 --- a/plugin/crypto/aes/aes_test.go +++ b/plugin/crypto/aes/aes_test.go @@ -47,7 +47,7 @@ func Test_AESCrypto_GenerateKey(t *testing.T) { } } -func Test_AESCrypto_EncryptToBase64(t *testing.T) { +func Test_AESCrypto_Encrypt(t *testing.T) { type args struct { plaintext string key []byte @@ -78,7 +78,7 @@ func Test_AESCrypto_EncryptToBase64(t *testing.T) { } } -func Test_AESCrypto_DecryptFromBase64(t *testing.T) { +func Test_AESCrypto_Decrypt(t *testing.T) { type args struct { base64Ciphertext string key []byte