From b61953aeffdb50c0bac67451418220253161860a Mon Sep 17 00:00:00 2001 From: Jacky Wang Date: Wed, 8 Jul 2020 16:01:55 -0700 Subject: [PATCH] [node.sh] refactor kmsClientProvider to kmsProvider and awsConfigGetter --- cmd/harmony/blsloader/helper.go | 36 ++----- cmd/harmony/blsloader/kmsProvider.go | 150 +++++++++++++------------- cmd/harmony/blsloader/loader.go | 58 +++++----- cmd/harmony/blsloader/passProvider.go | 4 +- cmd/harmony/blsloader/utils.go | 2 +- 5 files changed, 112 insertions(+), 138 deletions(-) diff --git a/cmd/harmony/blsloader/helper.go b/cmd/harmony/blsloader/helper.go index 5ce3254a2..9b8a6003c 100644 --- a/cmd/harmony/blsloader/helper.go +++ b/cmd/harmony/blsloader/helper.go @@ -86,20 +86,8 @@ func (loader *kmsSingleBlsLoader) loadKeys() (multibls.PrivateKeys, error) { return multibls.GetPrivateKeys(secretKey), nil } -func (loader *kmsSingleBlsLoader) getKmsClientProvider() (kmsClientProvider, error) { - switch loader.awsCfgSrcType { - case AwsCfgSrcFile: - if stringIsSet(loader.awsConfigFile) { - return newFileKmsProvider(*loader.awsConfigFile), nil - } - return newSharedKmsProvider(), nil - case AwsCfgSrcPrompt: - return newPromptKmsProvider(defKmsPromptTimeout), nil - case AwsCfgSrcShared: - return newSharedKmsProvider(), nil - default: - return nil, errors.New("unknown aws config source type") - } +func (loader *kmsSingleBlsLoader) getKmsClientProvider() (kmsProvider, error) { + return newLazyKmsProvider(loader.kmsProviderConfig) } // blsDirLoader is the helper structure for loading bls keys in a directory @@ -111,7 +99,7 @@ type blsDirLoader struct { // providers in process pps []passProvider - kcp kmsClientProvider + kcp kmsProvider // result field secretKeys []*bls_core.SecretKey } @@ -159,20 +147,8 @@ func (loader *blsDirLoader) getPromptPassProvider() passProvider { return provider } -func (loader *blsDirLoader) getKmsClientProvider() (kmsClientProvider, error) { - switch loader.awsCfgSrcType { - case AwsCfgSrcFile: - if stringIsSet(loader.awsConfigFile) { - return newFileKmsProvider(*loader.awsConfigFile), nil - } - return newSharedKmsProvider(), nil - case AwsCfgSrcPrompt: - return newPromptKmsProvider(defKmsPromptTimeout), nil - case AwsCfgSrcShared: - return newSharedKmsProvider(), nil - default: - return nil, errors.New("unknown aws config source type") - } +func (loader *blsDirLoader) getKmsClientProvider() (kmsProvider, error) { + return newLazyKmsProvider(loader.kmsProviderConfig) } func (loader *blsDirLoader) loadKeyFiles() (multibls.PrivateKeys, error) { @@ -190,7 +166,7 @@ func (loader *blsDirLoader) processFileWalk(path string, info os.FileInfo, err e // unexpected error, return the error and break the file walk loop return err } - // expected error. Skipping these files + // errors to be skipped. Skipping these files skipStr := fmt.Sprintf("Skipping [%s]: %v\n", path, err) console.println(skipStr) return nil diff --git a/cmd/harmony/blsloader/kmsProvider.go b/cmd/harmony/blsloader/kmsProvider.go index 24ddc9b18..e4e700fe3 100644 --- a/cmd/harmony/blsloader/kmsProvider.go +++ b/cmd/harmony/blsloader/kmsProvider.go @@ -37,49 +37,61 @@ type kmsProviderConfig struct { awsConfigFile *string } -func (cfg kmsProviderConfig) validate() error { - if !cfg.awsCfgSrcType.isValid() { +func (config kmsProviderConfig) validate() error { + if !config.awsCfgSrcType.isValid() { return errors.New("unknown AwsCfgSrcType") } - if cfg.awsCfgSrcType == AwsCfgSrcFile { - if !stringIsSet(cfg.awsConfigFile) { + if config.awsCfgSrcType == AwsCfgSrcFile { + if !stringIsSet(config.awsConfigFile) { return errors.New("config field AwsConfig file must set for AwsCfgSrcFile") } - if !isFile(*cfg.awsConfigFile) { - return fmt.Errorf("aws config file not exist %v", *cfg.awsConfigFile) + if !isFile(*config.awsConfigFile) { + return fmt.Errorf("aws config file not exist %v", *config.awsConfigFile) } } return nil } -// kmsClientProvider provides the kms client. Implemented by -// baseKMSProvider - abstract implementation -// sharedKMSProvider - provide the client with default .aws folder -// fileKMSProvider - provide the aws config with a json file -// promptKMSProvider - provide the config field from prompt with time out -type kmsClientProvider interface { - // getKMSClient returns the KMSClient of the kmsClientProvider with lazy loading. +// kmsProvider provide the aws kms client +type kmsProvider interface { getKMSClient() (*kms.KMS, error) - - // toStr return the string presentation of kmsClientProvider - toStr() string } -type getAwsCfgFunc func() (*AwsConfig, error) +// lazyKmsProvider provide the kms client with singleton lazy initialization with config get +// from awsConfigGetter for aws credential and regions loading. +type lazyKmsProvider struct { + acGetter awsConfigGetter -// baseKMSProvider provide the kms client with singleton initialization through -// function getConfig for aws credential and regions loading. -type baseKMSProvider struct { client *kms.KMS err error once sync.Once +} - getAWSConfig getAwsCfgFunc +// newLazyKmsProvider creates a kmsProvider with the given config +func newLazyKmsProvider(config kmsProviderConfig) (*lazyKmsProvider, error) { + var acg awsConfigGetter + switch config.awsCfgSrcType { + case AwsCfgSrcFile: + if stringIsSet(config.awsConfigFile) { + acg = newFileACGetter(*config.awsConfigFile) + } else { + acg = newSharedAwsConfigGetter() + } + case AwsCfgSrcPrompt: + acg = newPromptACGetter(defKmsPromptTimeout) + case AwsCfgSrcShared: + acg = newSharedAwsConfigGetter() + default: + return nil, errors.New("unknown aws config source type") + } + return &lazyKmsProvider{ + acGetter: acg, + }, nil } -func (provider *baseKMSProvider) getKMSClient() (*kms.KMS, error) { +func (provider *lazyKmsProvider) getKMSClient() (*kms.KMS, error) { provider.once.Do(func() { - cfg, err := provider.getAWSConfig() + cfg, err := provider.acGetter.getAwsConfig() if err != nil { provider.err = err return @@ -92,94 +104,80 @@ func (provider *baseKMSProvider) getKMSClient() (*kms.KMS, error) { return provider.client, nil } -func (provider *baseKMSProvider) toStr() string { - return "not implemented" +// awsConfigGetter provides the aws config. Implemented by +// sharedACGetter - provide the nil to use shared AWS configuration +// fileACGetter - provide the aws config with a json file +// promptACGetter - provide the config field from prompt with time out +type awsConfigGetter interface { + getAwsConfig() (*AwsConfig, error) + String() string } -// sharedKMSProvider provide the kms session with the default aws config -// locates in directory $HOME/.aws/config -type sharedKMSProvider struct { - baseKMSProvider -} +// sharedACGetter returns nil for getAwsConfig to use shared aws configurations +type sharedACGetter struct{} -func newSharedKmsProvider() *sharedKMSProvider { - provider := &sharedKMSProvider{baseKMSProvider{}} - provider.baseKMSProvider.getAWSConfig = provider.getAWSConfig - return provider +func newSharedAwsConfigGetter() *sharedACGetter { + return &sharedACGetter{} } -// TODO(Jacky): set getAwsConfig into a function, not bind with structure -func (provider *sharedKMSProvider) getAWSConfig() (*AwsConfig, error) { +func (getter *sharedACGetter) getAwsConfig() (*AwsConfig, error) { return nil, nil } -func (provider *sharedKMSProvider) toStr() string { +func (getter *sharedACGetter) String() string { return "shared aws config" } -// fileKMSProvider provide the kms session from a file with json data of structure -// AwsConfig -type fileKMSProvider struct { - baseKMSProvider - +// fileACGetter get aws config through a customized json file +type fileACGetter struct { file string } -func newFileKmsProvider(file string) *fileKMSProvider { - provider := &fileKMSProvider{ - baseKMSProvider: baseKMSProvider{}, - file: file, - } - provider.baseKMSProvider.getAWSConfig = provider.getAWSConfig - return provider +func newFileACGetter(file string) *fileACGetter { + return &fileACGetter{file} } -func (provider *fileKMSProvider) getAWSConfig() (*AwsConfig, error) { - b, err := ioutil.ReadFile(provider.file) +func (getter *fileACGetter) getAwsConfig() (*AwsConfig, error) { + b, err := ioutil.ReadFile(getter.file) if err != nil { return nil, err } - var cfg *AwsConfig - if err := json.Unmarshal(b, cfg); err != nil { + var cfg AwsConfig + if err := json.Unmarshal(b, &cfg); err != nil { return nil, err } - return cfg, nil + return &cfg, nil } -func (provider *fileKMSProvider) toStr() string { - return fmt.Sprintf("file %v", provider.file) +func (getter *fileACGetter) String() string { + return fmt.Sprintf("file %v", getter.file) } -// promptKMSProvider provide a user interactive console for AWS config. -// Three fields are asked: +// promptACGetter provide a user interactive console for AWS config. +// Four fields are asked: // 1. AccessKey 2. SecretKey 3. Region // Each field is asked with a timeout mechanism. -type promptKMSProvider struct { - baseKMSProvider - +type promptACGetter struct { timeout time.Duration } -func newPromptKmsProvider(timeout time.Duration) *promptKMSProvider { - provider := &promptKMSProvider{ - baseKMSProvider: baseKMSProvider{}, - timeout: timeout, +func newPromptACGetter(timeout time.Duration) *promptACGetter { + return &promptACGetter{ + timeout: timeout, } - provider.baseKMSProvider.getAWSConfig = provider.getAWSConfig - return provider } -func (provider *promptKMSProvider) getAWSConfig() (*AwsConfig, error) { +func (getter *promptACGetter) getAwsConfig() (*AwsConfig, error) { console.println("Please provide AWS configurations for KMS encoded BLS keys:") - accessKey, err := provider.prompt(" AccessKey:") + accessKey, err := getter.prompt(" AccessKey:") if err != nil { return nil, fmt.Errorf("cannot get aws access key: %v", err) } - secretKey, err := provider.prompt(" SecretKey:") + secretKey, err := getter.prompt(" SecretKey:") if err != nil { return nil, fmt.Errorf("cannot get aws secret key: %v", err) } - region, err := provider.prompt("Region:") + region, err := getter.prompt("Region:") if err != nil { return nil, fmt.Errorf("cannot get aws region: %v", err) } @@ -192,17 +190,17 @@ func (provider *promptKMSProvider) getAWSConfig() (*AwsConfig, error) { } // prompt prompt the user to input a string for a certain field with timeout. -func (provider *promptKMSProvider) prompt(hint string) (string, error) { +func (getter *promptACGetter) prompt(hint string) (string, error) { var ( res string err error finished = make(chan struct{}) - timedOut = time.After(provider.timeout) + timedOut = time.After(getter.timeout) ) go func() { - res, err = provider.threadedPrompt(hint) + res, err = getter.threadedPrompt(hint) close(finished) }() @@ -216,12 +214,12 @@ func (provider *promptKMSProvider) prompt(hint string) (string, error) { } } -func (provider *promptKMSProvider) threadedPrompt(hint string) (string, error) { +func (getter *promptACGetter) threadedPrompt(hint string) (string, error) { console.print(hint) return console.readPassword() } -func (provider *promptKMSProvider) toStr() string { +func (getter *promptACGetter) String() string { return "prompt" } diff --git a/cmd/harmony/blsloader/loader.go b/cmd/harmony/blsloader/loader.go index 2da5de912..20068bd3b 100644 --- a/cmd/harmony/blsloader/loader.go +++ b/cmd/harmony/blsloader/loader.go @@ -20,41 +20,13 @@ func LoadKeys(cfg Config) (multibls.PrivateKeys, error) { return helper.loadKeys() } -func getHelper(cfg Config) (loadHelper, error) { - switch { - case stringIsSet(cfg.BlsKeyFile): - switch filepath.Ext(*cfg.BlsKeyFile) { - case basicKeyExt: - return &basicSingleBlsLoader{ - blsKeyFile: *cfg.BlsKeyFile, - passProviderConfig: cfg.getPassProviderConfig(), - }, nil - case kmsKeyExt: - return &kmsSingleBlsLoader{ - blsKeyFile: *cfg.BlsKeyFile, - kmsProviderConfig: cfg.getKmsProviderConfig(), - }, nil - default: - return nil, errors.New("unknown extension") - } - case stringIsSet(cfg.BlsDir): - return &blsDirLoader{ - dirPath: *cfg.BlsDir, - passProviderConfig: cfg.getPassProviderConfig(), - kmsProviderConfig: cfg.getKmsProviderConfig(), - }, nil - default: - return nil, errors.New("either BlsKeyFile or BlsDir must be set") - } -} - // Loader is the structure to load bls keys. type Config struct { // source for bls key loading. At least one of the BlsKeyFile and BlsDir // need to be provided. // // BlsKeyFile defines a single key file to load from. Based on the file - // extension, decryption with passphrase or aws kms will be used. + // extension, decryption with either passphrase or aws kms will be used. BlsKeyFile *string // BlsDir defines a file directory to load keys from. BlsDir *string @@ -134,3 +106,31 @@ func (cfg *Config) getKmsProviderConfig() kmsProviderConfig { awsConfigFile: cfg.AwsConfigFile, } } + +func getHelper(cfg Config) (loadHelper, error) { + switch { + case stringIsSet(cfg.BlsKeyFile): + switch filepath.Ext(*cfg.BlsKeyFile) { + case basicKeyExt: + return &basicSingleBlsLoader{ + blsKeyFile: *cfg.BlsKeyFile, + passProviderConfig: cfg.getPassProviderConfig(), + }, nil + case kmsKeyExt: + return &kmsSingleBlsLoader{ + blsKeyFile: *cfg.BlsKeyFile, + kmsProviderConfig: cfg.getKmsProviderConfig(), + }, nil + default: + return nil, errors.New("unknown extension") + } + case stringIsSet(cfg.BlsDir): + return &blsDirLoader{ + dirPath: *cfg.BlsDir, + passProviderConfig: cfg.getPassProviderConfig(), + kmsProviderConfig: cfg.getKmsProviderConfig(), + }, nil + default: + return nil, errors.New("either BlsKeyFile or BlsDir must be set") + } +} diff --git a/cmd/harmony/blsloader/passProvider.go b/cmd/harmony/blsloader/passProvider.go index 700d0a4ac..d285c2785 100644 --- a/cmd/harmony/blsloader/passProvider.go +++ b/cmd/harmony/blsloader/passProvider.go @@ -25,8 +25,8 @@ func (config passProviderConfig) validate() error { // passProvider is the interface to provide the passphrase of a bls keys. // Implemented by // promptPassProvider - provide passphrase through user-interactive prompt -// filePassProvider - provide passphrase from a .pass file -// dirPassProvider - provide passphrase from .pass files in a directory +// filePassProvider - provide passphrase from a .pass file +// dirPassProvider - provide passphrase from .pass files in a directory type passProvider interface { toStr() string getPassphrase(keyFile string) (string, error) diff --git a/cmd/harmony/blsloader/utils.go b/cmd/harmony/blsloader/utils.go index 24360f6b6..b884858f7 100644 --- a/cmd/harmony/blsloader/utils.go +++ b/cmd/harmony/blsloader/utils.go @@ -47,7 +47,7 @@ func loadBasicKeyWithProvider(blsKeyFile string, pp passProvider) (*bls_core.Sec } // loadKmsKeyFromFile loads a single KMS BLS key from file -func loadKmsKeyFromFile(blsKeyFile string, kcp kmsClientProvider) (*bls_core.SecretKey, error) { +func loadKmsKeyFromFile(blsKeyFile string, kcp kmsProvider) (*bls_core.SecretKey, error) { if kcp == nil { return nil, errNilKMSClientProvider }