github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/creds/secretsmanager/secretsmanager.go (about)

     1  package secretsmanager
     2  
     3  import (
     4  	"encoding/json"
     5  	"time"
     6  
     7  	"github.com/pf-qiu/concourse/v6/atc/creds"
     8  
     9  	"code.cloudfoundry.org/lager"
    10  	"github.com/aws/aws-sdk-go/aws/awserr"
    11  	"github.com/aws/aws-sdk-go/service/secretsmanager"
    12  	"github.com/aws/aws-sdk-go/service/secretsmanager/secretsmanageriface"
    13  )
    14  
    15  type SecretsManager struct {
    16  	log             lager.Logger
    17  	api             secretsmanageriface.SecretsManagerAPI
    18  	secretTemplates []*creds.SecretTemplate
    19  }
    20  
    21  func NewSecretsManager(log lager.Logger, api secretsmanageriface.SecretsManagerAPI, secretTemplates []*creds.SecretTemplate) *SecretsManager {
    22  	return &SecretsManager{
    23  		log:             log,
    24  		api:             api,
    25  		secretTemplates: secretTemplates,
    26  	}
    27  }
    28  
    29  // NewSecretLookupPaths defines how variables will be searched in the underlying secret manager
    30  func (s *SecretsManager) NewSecretLookupPaths(teamName string, pipelineName string, allowRootPath bool) []creds.SecretLookupPath {
    31  	lookupPaths := []creds.SecretLookupPath{}
    32  	for _, tmpl := range s.secretTemplates {
    33  		if lPath := creds.NewSecretLookupWithTemplate(tmpl, teamName, pipelineName); lPath != nil {
    34  			lookupPaths = append(lookupPaths, lPath)
    35  		}
    36  	}
    37  	return lookupPaths
    38  }
    39  
    40  // Get retrieves the value and expiration of an individual secret
    41  func (s *SecretsManager) Get(secretPath string) (interface{}, *time.Time, bool, error) {
    42  	value, expiration, found, err := s.getSecretById(secretPath)
    43  	if err != nil {
    44  		s.log.Error("failed-to-fetch-aws-secret", err, lager.Data{
    45  			"secret-path": secretPath,
    46  		})
    47  		return nil, nil, false, err
    48  	}
    49  	if found {
    50  		return value, expiration, true, nil
    51  	}
    52  	return nil, nil, false, nil
    53  }
    54  
    55  /*
    56  	Looks up secret by path. Depending on which field is filled it will either
    57  	return a string value (SecretString) or a map[string]interface{} (SecretBinary).
    58  
    59  	In case SecretBinary is set, it is expected to be a valid JSON object or it will error.
    60  */
    61  func (s *SecretsManager) getSecretById(path string) (interface{}, *time.Time, bool, error) {
    62  	value, err := s.api.GetSecretValue(&secretsmanager.GetSecretValueInput{
    63  		SecretId: &path,
    64  	})
    65  	if err == nil {
    66  		switch {
    67  		case value.SecretString != nil:
    68  			return *value.SecretString, nil, true, nil
    69  		case value.SecretBinary != nil:
    70  			values, err := decodeJsonValue(value.SecretBinary)
    71  			if err != nil {
    72  				return nil, nil, true, err
    73  			}
    74  			return values, nil, true, nil
    75  		}
    76  	} else if errObj, ok := err.(awserr.Error); ok && errObj.Code() == secretsmanager.ErrCodeResourceNotFoundException {
    77  		return nil, nil, false, nil
    78  	}
    79  
    80  	return nil, nil, false, err
    81  }
    82  
    83  func decodeJsonValue(data []byte) (map[string]interface{}, error) {
    84  	var values map[string]interface{}
    85  	if err := json.Unmarshal(data, &values); err != nil {
    86  		return nil, err
    87  	}
    88  	return values, nil
    89  }