-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathroles.go
160 lines (128 loc) · 3.56 KB
/
roles.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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package finto
import (
"fmt"
"sort"
"sync"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/sts"
)
// Credentials represents a set of temporary credentials and their expiration.
type Credentials struct {
AccessKeyId string
Expiration time.Time
SecretAccessKey string
SessionToken string
}
func (c *Credentials) IsExpired() bool {
return c.Expiration.Before(time.Now())
}
func (c *Credentials) SetCredentials(id, key, token string) {
c.AccessKeyId = id
c.SecretAccessKey = key
c.SessionToken = token
}
// Sets expiration. Accepts an offset to allow for early expiration. This helps
// avoid returning credentials that expire "in flight."
func (c *Credentials) SetExpiration(t time.Time, offset time.Duration) {
c.Expiration = t.Add(-offset)
}
// AssumeRoleClient is a basic interface that wraps role assumption.
//
// AssumeRole takes the input of a role ARN and session name, and returns a set
// of credentials including: an access key ID, a secret access key, a session
// token, and their expiration.
//
// https://godoc.org/github.com/aws/aws-sdk-go/service/sts#AssumeRoleInput
// https://godoc.org/github.com/aws/aws-sdk-go/service/sts#AssumeRoleOutput
type AssumeRoleClient interface {
AssumeRole(input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error)
}
// Implements a role, the retrieval of its credentials, and management of their
// expiration.
type Role struct {
arn string // The role's Amazon Resource Name
creds Credentials // The role's credentials
sessionName string // The session name recorded by assumption
client AssumeRoleClient // An AssumeRoleClient for retrieving credentials
m sync.Mutex
}
func NewRole(a, s string, c AssumeRoleClient) *Role {
return &Role{
arn: a,
sessionName: s,
client: c,
}
}
func (r *Role) Arn() string {
return r.arn
}
func (r *Role) SessionName() string {
return r.sessionName
}
// Returns whether the role's current credentials are expired.
func (r *Role) IsExpired() bool {
r.m.Lock()
defer r.m.Unlock()
return r.isExpired()
}
func (r *Role) isExpired() bool {
return r.creds.IsExpired()
}
// Returns the role's credentials. If expired, credentials are refreshed through
// the client.
func (r *Role) Credentials() (Credentials, error) {
r.m.Lock()
defer r.m.Unlock()
if r.isExpired() {
resp, err := r.client.AssumeRole(&sts.AssumeRoleInput{
RoleArn: aws.String(r.Arn()),
RoleSessionName: aws.String(r.SessionName()),
})
if err != nil {
return Credentials{}, err
}
creds := resp.Credentials
r.creds.SetCredentials(*creds.AccessKeyId, *creds.SecretAccessKey, *creds.SessionToken)
r.creds.SetExpiration(*creds.Expiration, 300)
}
return r.creds, nil
}
// A collection of aliased roles.
type RoleSet struct {
roles map[string]*Role
client AssumeRoleClient
m sync.Mutex
}
func NewRoleSet(c AssumeRoleClient) *RoleSet {
return &RoleSet{
client: c,
roles: make(map[string]*Role),
}
}
func (rs *RoleSet) Role(alias string) (*Role, error) {
rs.m.Lock()
defer rs.m.Unlock()
if role, ok := rs.roles[alias]; ok {
return role, nil
}
return &Role{}, fmt.Errorf("unknown role: %s", alias)
}
func (rs *RoleSet) Roles() (roles []string) {
rs.m.Lock()
defer rs.m.Unlock()
roles = make([]string, len(rs.roles))
i := 0
for k := range rs.roles {
roles[i] = k
i += 1
}
sort.Strings(roles)
return
}
// Set an alias's role configuration.
func (rs *RoleSet) SetRole(alias, arn string) {
rs.m.Lock()
defer rs.m.Unlock()
rs.roles[alias] = NewRole(arn, fmt.Sprintf("finto-%s", alias), rs.client)
}