github.com/webonyx/up@v0.7.4-0.20180808230834-91b94e551323/platform/aws/runtime/secrets.go (about)

     1  package runtime
     2  
     3  import (
     4  	"strings"
     5  
     6  	"github.com/aws/aws-sdk-go/aws"
     7  	"github.com/aws/aws-sdk-go/aws/session"
     8  	"github.com/aws/aws-sdk-go/service/ssm"
     9  	"github.com/pkg/errors"
    10  
    11  	"github.com/apex/up"
    12  	"github.com/apex/up/internal/secret"
    13  	"github.com/apex/up/internal/util"
    14  )
    15  
    16  // TODO: secret pagination
    17  
    18  // Secrets implementation.
    19  type Secrets struct {
    20  	client *ssm.SSM
    21  	name   string
    22  	stage  string
    23  }
    24  
    25  // NewSecrets returns a new secrets manager.
    26  func NewSecrets(name, stage, region string) *Secrets {
    27  	return &Secrets{
    28  		client: ssm.New(session.New(aws.NewConfig().WithRegion(region))),
    29  		name:   name,
    30  		stage:  stage,
    31  	}
    32  }
    33  
    34  // List implementation.
    35  func (s *Secrets) List(decrypt bool) (v []*up.Secret, err error) {
    36  	res, err := s.client.DescribeParameters(&ssm.DescribeParametersInput{
    37  		MaxResults: aws.Int64(50),
    38  		Filters: []*ssm.ParametersFilter{
    39  			{
    40  				Key:    aws.String("Name"),
    41  				Values: aws.StringSlice([]string{"/up/" + s.name + "/"}),
    42  			},
    43  		},
    44  	})
    45  
    46  	if err != nil {
    47  		return
    48  	}
    49  
    50  	for _, p := range res.Parameters {
    51  		var value string
    52  
    53  		if *p.Type == "String" || decrypt {
    54  			p, err := s.client.GetParameter(&ssm.GetParameterInput{
    55  				Name:           p.Name,
    56  				WithDecryption: &decrypt,
    57  			})
    58  
    59  			if err != nil {
    60  				return nil, errors.Wrap(err, "getting parameter")
    61  			}
    62  
    63  			value = *p.Parameter.Value
    64  		}
    65  
    66  		app, stage, name := secret.Parse(*p.Name)
    67  		v = append(v, &up.Secret{
    68  			App:              app,
    69  			Name:             name,
    70  			Stage:            stage,
    71  			Type:             *p.Type,
    72  			Description:      util.DefaultString(p.Description, ""),
    73  			LastModifiedUser: userFromARN(p.LastModifiedUser),
    74  			LastModified:     *p.LastModifiedDate,
    75  			Value:            value,
    76  		})
    77  	}
    78  
    79  	return
    80  }
    81  
    82  // Load implementation.
    83  func (s *Secrets) Load() (v []*up.Secret, err error) {
    84  	var token *string
    85  
    86  	for {
    87  		res, err := s.client.GetParametersByPath(&ssm.GetParametersByPathInput{
    88  			MaxResults:     aws.Int64(10),
    89  			Path:           aws.String("/up/" + s.name + "/"),
    90  			WithDecryption: aws.Bool(true),
    91  			Recursive:      aws.Bool(true),
    92  			NextToken:      token,
    93  		})
    94  
    95  		if err != nil {
    96  			return nil, err
    97  		}
    98  
    99  		for _, p := range res.Parameters {
   100  			app, stage, name := secret.Parse(*p.Name)
   101  			v = append(v, &up.Secret{
   102  				App:   app,
   103  				Name:  name,
   104  				Stage: stage,
   105  				Type:  *p.Type,
   106  				Value: *p.Value,
   107  			})
   108  		}
   109  
   110  		token = res.NextToken
   111  
   112  		if token == nil {
   113  			break
   114  		}
   115  	}
   116  
   117  	return
   118  }
   119  
   120  // Add implementation.
   121  func (s *Secrets) Add(key, val, desc string, clear bool) error {
   122  	key = s.secretName(key)
   123  
   124  	kind := "SecureString"
   125  	if clear {
   126  		kind = "String"
   127  	}
   128  
   129  	params := &ssm.PutParameterInput{
   130  		Type:      &kind,
   131  		Name:      &key,
   132  		Value:     &val,
   133  		Overwrite: aws.Bool(true),
   134  	}
   135  
   136  	if desc != "" {
   137  		params.Description = &desc
   138  	}
   139  
   140  	_, err := s.client.PutParameter(params)
   141  	return err
   142  }
   143  
   144  // Get implementation.
   145  func (s *Secrets) Get(key string) (string, error) {
   146  	key = s.secretName(key)
   147  
   148  	res, err := s.client.GetParameter(&ssm.GetParameterInput{
   149  		Name:           &key,
   150  		WithDecryption: aws.Bool(true),
   151  	})
   152  
   153  	if err != nil {
   154  		return "", err
   155  	}
   156  
   157  	return *res.Parameter.Value, nil
   158  }
   159  
   160  // Remove implementation.
   161  func (s *Secrets) Remove(key string) error {
   162  	key = s.secretName(key)
   163  
   164  	_, err := s.client.DeleteParameter(&ssm.DeleteParameterInput{
   165  		Name: &key,
   166  	})
   167  
   168  	return err
   169  }
   170  
   171  // secretName returns the secret name normalized.
   172  func (s *Secrets) secretName(name string) string {
   173  	return secret.Format(s.name, s.stage, name)
   174  }
   175  
   176  // userFromARN returns the username from ARN if present.
   177  func userFromARN(arn *string) string {
   178  	if arn == nil {
   179  		return ""
   180  	}
   181  
   182  	p := strings.Split(*arn, "/")
   183  
   184  	if len(p) >= 2 {
   185  		return p[1]
   186  	}
   187  
   188  	return ""
   189  }