/
credentialset.go
112 lines (100 loc) · 3.47 KB
/
credentialset.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package credentials
import (
"fmt"
"io/ioutil"
"time"
"gopkg.in/yaml.v2"
"github.com/cnabio/cnab-go/bundle"
"github.com/cnabio/cnab-go/schema"
"github.com/cnabio/cnab-go/secrets"
"github.com/cnabio/cnab-go/valuesource"
)
const (
// DefaultSchemaVersion is the default SchemaVersion value
// set on new CredentialSet instances, and is the semver portion
// of CNABSpecVersion.
DefaultSchemaVersion = schema.Version("1.0.0-DRAFT+b6c701f")
// CNABSpecVersion represents the CNAB Spec version of the Credentials
// that this library implements
// This value is prefixed with e.g. `cnab-credentials-` so isn't itself valid semver.
CNABSpecVersion string = "cnab-credentialsets-" + string(DefaultSchemaVersion)
)
// CredentialSet represents a collection of credentials
type CredentialSet struct {
// SchemaVersion is the version of the credential-set schema.
SchemaVersion schema.Version `json:"schemaVersion" yaml:"schemaVersion"`
// Name is the name of the credentialset.
Name string `json:"name" yaml:"name"`
// Created timestamp of the credentialset.
Created time.Time `json:"created" yaml:"created"`
// Modified timestamp of the credentialset.
Modified time.Time `json:"modified" yaml:"modified"`
// Credentials is a list of credential specs.
Credentials []valuesource.Strategy `json:"credentials" yaml:"credentials"`
}
// NewCredentialSet creates a new CredentialSet with the required fields initialized.
func NewCredentialSet(name string, creds ...valuesource.Strategy) CredentialSet {
now := time.Now()
cs := CredentialSet{
SchemaVersion: DefaultSchemaVersion,
Name: name,
Created: now,
Modified: now,
Credentials: creds,
}
return cs
}
// Load a CredentialSet from a file at a given path.
//
// It does not load the individual credentials.
func Load(path string) (*CredentialSet, error) {
cset := &CredentialSet{}
data, err := ioutil.ReadFile(path)
if err != nil {
return cset, err
}
return cset, yaml.Unmarshal(data, cset)
}
// Validate compares the given credentials with the spec.
//
// This will result in an error only when the following conditions are true:
// - a credential in the spec is not present in the given set
// - the credential is required
// - the credential applies to the specified action
//
// It is allowed for spec to specify both an env var and a file. In such case, if
// the given set provides either, it will be considered valid.
func Validate(given valuesource.Set, spec map[string]bundle.Credential, action string) error {
for name, cred := range spec {
if !cred.AppliesTo(action) {
continue
}
if !valuesource.IsValid(given, name) && cred.Required {
return fmt.Errorf("bundle requires credential for %s", name)
}
}
return nil
}
// ResolveCredentials looks up the credentials as described in Source, then copies
// the resulting value into the Value field of each credential strategy.
//
// The typical workflow for working with a credential set is:
//
// - Load the set
// - Validate the credentials against a spec
// - Resolve the credentials
// - Expand them into bundle values
func (c *CredentialSet) ResolveCredentials(s secrets.Store) (valuesource.Set, error) {
l := len(c.Credentials)
res := make(map[string]string, l)
for i := 0; i < l; i++ {
cred := c.Credentials[i]
val, err := s.Resolve(cred.Source.Key, cred.Source.Value)
if err != nil {
return nil, fmt.Errorf("credential %q: %v", c.Credentials[i].Name, err)
}
cred.Value = val
res[c.Credentials[i].Name] = cred.Value
}
return res, nil
}