get.porter.sh/porter@v1.3.0/pkg/storage/credential_store.go (about)

     1  package storage
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"get.porter.sh/porter/pkg/secrets"
     9  	hostSecrets "get.porter.sh/porter/pkg/secrets/plugins/host"
    10  	"get.porter.sh/porter/pkg/tracing"
    11  	"github.com/cnabio/cnab-go/secrets/host"
    12  	"github.com/hashicorp/go-multierror"
    13  )
    14  
    15  var _ CredentialSetProvider = &CredentialStore{}
    16  
    17  const (
    18  	CollectionCredentials = "credentials"
    19  )
    20  
    21  // CredentialStore is a wrapper around Porter's datastore
    22  // providing typed access and additional business logic around
    23  // credential sets, usually referred to as "credentials" as a shorthand.
    24  type CredentialStore struct {
    25  	Documents   Store
    26  	Secrets     secrets.Store
    27  	HostSecrets hostSecrets.Store
    28  }
    29  
    30  func NewCredentialStore(storage Store, secrets secrets.Store) *CredentialStore {
    31  	return &CredentialStore{
    32  		Documents:   storage,
    33  		Secrets:     secrets,
    34  		HostSecrets: hostSecrets.NewStore(),
    35  	}
    36  }
    37  
    38  // EnsureCredentialIndices creates indices on the credentials collection.
    39  func EnsureCredentialIndices(ctx context.Context, store Store) error {
    40  	ctx, span := tracing.StartSpan(ctx)
    41  	defer span.EndSpan()
    42  
    43  	span.Debug("Initializing credentials collection indices")
    44  
    45  	indices := EnsureIndexOptions{
    46  		Indices: []Index{
    47  			// query credentials by namespace + name
    48  			{Collection: CollectionCredentials, Keys: []string{"namespace", "name"}, Unique: true},
    49  		},
    50  	}
    51  	err := store.EnsureIndex(ctx, indices)
    52  	return span.Error(err)
    53  }
    54  
    55  func (s CredentialStore) GetDataStore() Store {
    56  	return s.Documents
    57  }
    58  
    59  /*
    60  	Secrets
    61  */
    62  
    63  func (s CredentialStore) ResolveAll(ctx context.Context, creds CredentialSet, keys []string) (secrets.Set, error) {
    64  	ctx, span := tracing.StartSpan(ctx)
    65  	defer span.EndSpan()
    66  
    67  	resolvedCreds := make(secrets.Set)
    68  	var resolveErrors *multierror.Error
    69  
    70  	for _, key := range keys {
    71  		cred, ok := creds.GetCredential(key)
    72  		if !ok {
    73  			resolveErrors = multierror.Append(resolveErrors, span.Errorf("credential %s not found", key))
    74  			continue
    75  		}
    76  
    77  		var value string
    78  		var err error
    79  		if isHandledByHostPlugin(cred.Source.Strategy) {
    80  			value, err = s.HostSecrets.Resolve(ctx, cred.Source.Strategy, cred.Source.Hint)
    81  		} else {
    82  			value, err = s.Secrets.Resolve(ctx, cred.Source.Strategy, cred.Source.Hint)
    83  		}
    84  		if err != nil {
    85  			resolveErrors = multierror.Append(resolveErrors, span.Errorf("unable to resolve credential %s.%s from %s %s: %w", creds.Name, cred.Name, cred.Source.Strategy, cred.Source.Hint, err))
    86  		}
    87  
    88  		resolvedCreds[cred.Name] = value
    89  	}
    90  
    91  	return resolvedCreds, resolveErrors.ErrorOrNil()
    92  }
    93  
    94  func (s CredentialStore) Validate(ctx context.Context, creds CredentialSet) error {
    95  	validSources := []string{secrets.SourceSecret, host.SourceValue, host.SourceEnv, host.SourcePath, host.SourceCommand}
    96  	var errors error
    97  
    98  	for _, cs := range creds.Credentials {
    99  		valid := false
   100  		for _, validSource := range validSources {
   101  			if cs.Source.Strategy == validSource {
   102  				valid = true
   103  				break
   104  			}
   105  		}
   106  		if !valid {
   107  			errors = multierror.Append(errors, fmt.Errorf(
   108  				"%s is not a valid source. Valid sources are: %s",
   109  				cs.Source.Strategy,
   110  				strings.Join(validSources, ", "),
   111  			))
   112  		}
   113  	}
   114  
   115  	return errors
   116  }
   117  
   118  /*
   119    Document Storage
   120  */
   121  
   122  func (s CredentialStore) InsertCredentialSet(ctx context.Context, creds CredentialSet) error {
   123  	creds.SchemaVersion = DefaultCredentialSetSchemaVersion
   124  	opts := InsertOptions{
   125  		Documents: []interface{}{creds},
   126  	}
   127  	return s.Documents.Insert(ctx, CollectionCredentials, opts)
   128  }
   129  
   130  func (s CredentialStore) ListCredentialSets(ctx context.Context, listOptions ListOptions) ([]CredentialSet, error) {
   131  	var out []CredentialSet
   132  	err := s.Documents.Find(ctx, CollectionCredentials, listOptions.ToFindOptions(), &out)
   133  	return out, err
   134  }
   135  
   136  func (s CredentialStore) GetCredentialSet(ctx context.Context, namespace string, name string) (CredentialSet, error) {
   137  	var out CredentialSet
   138  	opts := FindOptions{
   139  		Filter: map[string]interface{}{
   140  			"namespace": namespace,
   141  			"name":      name,
   142  		},
   143  	}
   144  	err := s.Documents.FindOne(ctx, CollectionCredentials, opts, &out)
   145  	return out, err
   146  }
   147  
   148  func (s CredentialStore) UpdateCredentialSet(ctx context.Context, creds CredentialSet) error {
   149  	creds.SchemaVersion = DefaultCredentialSetSchemaVersion
   150  	opts := UpdateOptions{
   151  		Document: creds,
   152  	}
   153  	return s.Documents.Update(ctx, CollectionCredentials, opts)
   154  }
   155  
   156  func (s CredentialStore) UpsertCredentialSet(ctx context.Context, creds CredentialSet) error {
   157  	creds.SchemaVersion = DefaultCredentialSetSchemaVersion
   158  	opts := UpdateOptions{
   159  		Document: creds,
   160  		Upsert:   true,
   161  	}
   162  	return s.Documents.Update(ctx, CollectionCredentials, opts)
   163  }
   164  
   165  func (s CredentialStore) RemoveCredentialSet(ctx context.Context, namespace string, name string) error {
   166  	opts := RemoveOptions{
   167  		Namespace: namespace,
   168  		Name:      name,
   169  	}
   170  	return s.Documents.Remove(ctx, CollectionCredentials, opts)
   171  }