github.com/polarismesh/polaris@v1.17.8/config/config_chain.go (about)

     1  /**
     2   * Tencent is pleased to support the open source community by making Polaris available.
     3   *
     4   * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
     5   *
     6   * Licensed under the BSD 3-Clause License (the "License");
     7   * you may not use this file except in compliance with the License.
     8   * You may obtain a copy of the License at
     9   *
    10   * https://opensource.org/licenses/BSD-3-Clause
    11   *
    12   * Unless required by applicable law or agreed to in writing, software distributed
    13   * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
    14   * CONDITIONS OF ANY KIND, either express or implied. See the License for the
    15   * specific language governing permissions and limitations under the License.
    16   */
    17  
    18  package config
    19  
    20  import (
    21  	"context"
    22  	"encoding/base64"
    23  
    24  	apiconfig "github.com/polarismesh/specification/source/go/api/v1/config_manage"
    25  	apimodel "github.com/polarismesh/specification/source/go/api/v1/model"
    26  	"go.uber.org/zap"
    27  
    28  	api "github.com/polarismesh/polaris/common/api/v1"
    29  	"github.com/polarismesh/polaris/common/model"
    30  	"github.com/polarismesh/polaris/common/utils"
    31  )
    32  
    33  // ConfigFileChain
    34  type ConfigFileChain interface {
    35  	// Init
    36  	Init(svr *Server)
    37  	// Name
    38  	Name() string
    39  	// BeforeCreateFile
    40  	BeforeCreateFile(context.Context, *model.ConfigFile) *apiconfig.ConfigResponse
    41  	// AfterGetFile
    42  	AfterGetFile(context.Context, *model.ConfigFile) (*model.ConfigFile, error)
    43  	// BeforeUpdateFile
    44  	BeforeUpdateFile(context.Context, *model.ConfigFile) *apiconfig.ConfigResponse
    45  	// AfterGetFileRelease
    46  	AfterGetFileRelease(context.Context, *model.ConfigFileRelease) (*model.ConfigFileRelease, error)
    47  	// AfterGetFileHistory
    48  	AfterGetFileHistory(context.Context, *model.ConfigFileReleaseHistory) (*model.ConfigFileReleaseHistory, error)
    49  }
    50  
    51  type CryptoConfigFileChain struct {
    52  	svr *Server
    53  }
    54  
    55  func (chain *CryptoConfigFileChain) Init(svr *Server) {
    56  	chain.svr = svr
    57  }
    58  
    59  func (chain *CryptoConfigFileChain) Name() string {
    60  	return "CryptoConfigFileChain"
    61  }
    62  
    63  // BeforeCreateFile
    64  func (chain *CryptoConfigFileChain) BeforeCreateFile(ctx context.Context,
    65  	file *model.ConfigFile) *apiconfig.ConfigResponse {
    66  	// 配置加密
    67  	if !file.IsEncrypted() {
    68  		return nil
    69  	}
    70  	if err := chain.encryptConfigFile(ctx, file, file.GetEncryptAlgo(), ""); err != nil {
    71  		log.Error("[Config][Service] encrypt config file error.", utils.RequestID(ctx),
    72  			utils.ZapNamespace(file.Namespace), utils.ZapGroup(file.Group),
    73  			utils.ZapFileName(file.Name), zap.Error(err))
    74  		return api.NewConfigResponseWithInfo(apimodel.Code_EncryptConfigFileException, err.Error())
    75  	}
    76  	return nil
    77  }
    78  
    79  // AfterCreateFile
    80  func (chain *CryptoConfigFileChain) AfterGetFile(ctx context.Context,
    81  	file *model.ConfigFile) (*model.ConfigFile, error) {
    82  	if file.IsEncrypted() {
    83  		file.Encrypt = true
    84  	}
    85  
    86  	encryptAlgo := file.GetEncryptAlgo()
    87  	dataKey := file.GetEncryptDataKey()
    88  	plainContent, err := chain.decryptConfigFileContent(dataKey, encryptAlgo, file.Content)
    89  
    90  	// TODO: 这个逻辑需要优化,在1.17.3处理
    91  	// 前一次发布的配置并未加密,现在准备发布的配置是开启了加密的,因此这里可能配置就是一个未加密的状态
    92  	// 这里就直接原样返回
    93  	if err == nil && plainContent != "" {
    94  		file.Content = plainContent
    95  	}
    96  	if err != nil {
    97  		log.Error("[Config][Chain][Crypto] decrypt config file content",
    98  			utils.ZapNamespace(file.Namespace), utils.ZapGroup(file.Group),
    99  			utils.ZapFileName(file.Name), zap.Error(err))
   100  	}
   101  	delete(file.Metadata, utils.ConfigFileTagKeyDataKey)
   102  	return file, nil
   103  }
   104  
   105  // BeforeUpdateFile
   106  func (chain *CryptoConfigFileChain) BeforeUpdateFile(ctx context.Context,
   107  	file *model.ConfigFile) *apiconfig.ConfigResponse {
   108  
   109  	// 配置加密
   110  	encryAlgo := file.GetEncryptAlgo()
   111  	encryDataKey := file.GetEncryptDataKey()
   112  	// 算法以传进来的参数为准
   113  	if file.IsEncrypted() {
   114  		// 如果加密算法进行了调整,dataKey 需要重新生成
   115  		if err := chain.encryptConfigFile(ctx, file, encryAlgo, encryDataKey); err != nil {
   116  			log.Error("[Config][Service] update encrypt config file error.", utils.RequestID(ctx),
   117  				utils.ZapNamespace(file.Namespace), utils.ZapGroup(file.Group), utils.ZapFileName(file.Name),
   118  				zap.Error(err))
   119  			return api.NewConfigResponseWithInfo(apimodel.Code_EncryptConfigFileException, err.Error())
   120  		}
   121  	} else {
   122  		chain.cleanEncryptConfigFileInfo(ctx, file)
   123  	}
   124  	return nil
   125  }
   126  
   127  // AfterGetFileRelease
   128  func (chain *CryptoConfigFileChain) AfterGetFileRelease(ctx context.Context,
   129  	release *model.ConfigFileRelease) (*model.ConfigFileRelease, error) {
   130  
   131  	s := chain.svr
   132  	// decryptConfigFileRelease 解密配置文件发布纪录
   133  	if s.cryptoManager == nil || release == nil {
   134  		return release, nil
   135  	}
   136  	encryptAlgo := release.GetEncryptAlgo()
   137  	encryptDataKey := release.GetEncryptDataKey()
   138  	plainContent, err := chain.decryptConfigFileContent(encryptDataKey, encryptAlgo, release.Content)
   139  	if err == nil && plainContent != "" {
   140  		release.Content = plainContent
   141  	}
   142  	if err != nil {
   143  		log.Error("[Config][Chain][Crypto] decrypt release config file content",
   144  			utils.ZapNamespace(release.Namespace), utils.ZapGroup(release.Group),
   145  			utils.ZapFileName(release.Name), zap.Error(err))
   146  	}
   147  	return release, nil
   148  }
   149  
   150  // AfterGetFileHistory
   151  func (chain *CryptoConfigFileChain) AfterGetFileHistory(ctx context.Context,
   152  	history *model.ConfigFileReleaseHistory) (*model.ConfigFileReleaseHistory, error) {
   153  	if history == nil {
   154  		return history, nil
   155  	}
   156  	if !history.IsEncrypted() {
   157  		return history, nil
   158  	}
   159  	encryptAlgo := history.GetEncryptAlgo()
   160  	dataKey := history.GetEncryptDataKey()
   161  	plainContent, err := chain.decryptConfigFileContent(dataKey, encryptAlgo, history.Content)
   162  	if err == nil && plainContent != "" {
   163  		history.Content = plainContent
   164  	} else {
   165  		log.Error("[Config][Chain][Crypto] decrypt history config file content",
   166  			utils.ZapNamespace(history.Namespace), utils.ZapGroup(history.Group),
   167  			utils.ZapFileName(history.Name), zap.Error(err))
   168  	}
   169  	return history, err
   170  }
   171  
   172  // decryptConfigFileContent 解密配置文件
   173  func (chain *CryptoConfigFileChain) decryptConfigFileContent(dataKey, algorithm, content string) (string, error) {
   174  	cryptoMgr := chain.svr.cryptoManager
   175  	if cryptoMgr == nil {
   176  		return "", nil
   177  	}
   178  	// 没有加密算法不解密
   179  	if algorithm == "" {
   180  		return "", nil
   181  	}
   182  	crypto, err := cryptoMgr.GetCrypto(algorithm)
   183  	if err != nil {
   184  		return "", err
   185  	}
   186  	if crypto == nil {
   187  		return "", nil
   188  	}
   189  	dateKeyBytes, err := base64.StdEncoding.DecodeString(dataKey)
   190  	if err != nil {
   191  		return "", err
   192  	}
   193  	// 解密
   194  	plainContent, err := crypto.Decrypt(content, dateKeyBytes)
   195  	if err != nil {
   196  		return "", err
   197  	}
   198  	return plainContent, nil
   199  }
   200  
   201  // cleanEncryptConfigFileInfo 清理配置加密文件的内容信息
   202  func (chain *CryptoConfigFileChain) cleanEncryptConfigFileInfo(ctx context.Context, configFile *model.ConfigFile) {
   203  	delete(configFile.Metadata, utils.ConfigFileTagKeyDataKey)
   204  	delete(configFile.Metadata, utils.ConfigFileTagKeyEncryptAlgo)
   205  	delete(configFile.Metadata, utils.ConfigFileTagKeyUseEncrypted)
   206  }
   207  
   208  // encryptConfigFile 加密配置文件
   209  func (chain *CryptoConfigFileChain) encryptConfigFile(ctx context.Context, configFile *model.ConfigFile,
   210  	algorithm string, dataKey string) error {
   211  
   212  	s := chain.svr
   213  	if s.cryptoManager == nil || configFile == nil {
   214  		return nil
   215  	}
   216  	crypto, err := s.cryptoManager.GetCrypto(algorithm)
   217  	if err != nil {
   218  		return err
   219  	}
   220  
   221  	var dateKeyBytes []byte
   222  	if dataKey == "" {
   223  		dateKeyBytes, err = crypto.GenerateKey()
   224  		if err != nil {
   225  			return err
   226  		}
   227  	} else {
   228  		dateKeyBytes, err = base64.StdEncoding.DecodeString(dataKey)
   229  		if err != nil {
   230  			return err
   231  		}
   232  	}
   233  	content := configFile.Content
   234  	cipherContent, err := crypto.Encrypt(content, dateKeyBytes)
   235  	if err != nil {
   236  		return err
   237  	}
   238  	configFile.Content = cipherContent
   239  	if len(configFile.Metadata) == 0 {
   240  		configFile.Metadata = map[string]string{}
   241  	}
   242  	configFile.Metadata[utils.ConfigFileTagKeyDataKey] = base64.StdEncoding.EncodeToString(dateKeyBytes)
   243  	configFile.Metadata[utils.ConfigFileTagKeyEncryptAlgo] = algorithm
   244  	configFile.Metadata[utils.ConfigFileTagKeyUseEncrypted] = "true"
   245  
   246  	return nil
   247  }
   248  
   249  type ReleaseConfigFileChain struct {
   250  	svr *Server
   251  }
   252  
   253  func (chain *ReleaseConfigFileChain) Init(svr *Server) {
   254  	chain.svr = svr
   255  }
   256  
   257  func (chain *ReleaseConfigFileChain) Name() string {
   258  	return "CryptoConfigFileChain"
   259  }
   260  
   261  // BeforeCreateFile
   262  func (chain *ReleaseConfigFileChain) BeforeCreateFile(ctx context.Context,
   263  	file *model.ConfigFile) *apiconfig.ConfigResponse {
   264  	return nil
   265  }
   266  
   267  // AfterCreateFile
   268  func (chain *ReleaseConfigFileChain) AfterGetFile(ctx context.Context,
   269  	file *model.ConfigFile) (*model.ConfigFile, error) {
   270  
   271  	namespace := file.Namespace
   272  	group := file.Group
   273  	name := file.Name
   274  
   275  	// 填充发布信息
   276  	activeFile := chain.svr.fileCache.GetActiveRelease(namespace, group, name)
   277  	if activeFile != nil {
   278  		// 如果最后一次发布的内容和当前文件内容一致,则展示最后一次发布状态。否则说明文件有修改,待发布
   279  		if activeFile.Content == file.OriginContent {
   280  			file.Status = utils.ReleaseTypeNormal
   281  			file.ReleaseBy = activeFile.ModifyBy
   282  			file.ReleaseTime = activeFile.ModifyTime
   283  		} else {
   284  			file.Status = utils.ReleaseStatusToRelease
   285  		}
   286  	} else {
   287  		// 如果从来没有发布过,也是待发布状态
   288  		file.Status = utils.ReleaseStatusToRelease
   289  	}
   290  	return file, nil
   291  }
   292  
   293  // BeforeUpdateFile
   294  func (chain *ReleaseConfigFileChain) BeforeUpdateFile(ctx context.Context,
   295  	file *model.ConfigFile) *apiconfig.ConfigResponse {
   296  	return nil
   297  }
   298  
   299  // AfterGetFileRelease
   300  func (chain *ReleaseConfigFileChain) AfterGetFileRelease(ctx context.Context,
   301  	release *model.ConfigFileRelease) (*model.ConfigFileRelease, error) {
   302  	return release, nil
   303  }
   304  
   305  // AfterGetFileHistory
   306  func (chain *ReleaseConfigFileChain) AfterGetFileHistory(ctx context.Context,
   307  	history *model.ConfigFileReleaseHistory) (*model.ConfigFileReleaseHistory, error) {
   308  	return history, nil
   309  }