forked from folbricht/desync
/
credentials.go
184 lines (153 loc) · 5.46 KB
/
credentials.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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
package main
import (
"os"
"time"
"path/filepath"
"github.com/go-ini/ini"
"github.com/minio/minio-go/pkg/credentials"
"github.com/pkg/errors"
)
// SharedCredentialsFilename returns the SDK's default file path
// for the shared credentials file.
//
// Builds the shared config file path based on the OS's platform.
//
// - Linux/Unix: $HOME/.aws/credentials
func SharedCredentialsFilename() string {
return filepath.Join(os.Getenv("HOME"), ".aws", "credentials")
}
// UserHomeDir returns the home directory for the user the process is
// running under.
func UserHomeDir() string {
return os.Getenv("HOME")
}
// Implements credentials.Provider from github.com/minio/minio-go/pkg/credentials
type StaticCredentialsProvider struct {
creds credentials.Value
}
func (cp *StaticCredentialsProvider) IsExpired() bool {
return false
}
func (cp *StaticCredentialsProvider) Retrieve() (credentials.Value, error) {
return cp.creds, nil
}
func NewStaticCredentials(accessKey, secretKey string) *credentials.Credentials {
p := &StaticCredentialsProvider{
credentials.Value{
AccessKeyID: accessKey,
SecretAccessKey: secretKey,
},
}
return credentials.New(p)
}
// A SharedCredentialsProvider retrieves credentials from the current user's home
// directory, and keeps track if those credentials are expired.
//
// Profile ini file example: $HOME/.aws/credentials
type RefreshableSharedCredentialsProvider struct {
// Path to the shared credentials file.
//
// If empty will look for "AWS_SHARED_CREDENTIALS_FILE" env variable. If the
// env value is empty will default to current user's home directory.
// Linux/OSX: "$HOME/.aws/credentials"
Filename string
// AWS Profile to extract credentials from the shared credentials file. If empty
// will default to environment variable "AWS_PROFILE" or "default" if
// environment variable is also not set.
Profile string
// The expiration time of the current fetched credentials.
exp time.Time
// The function to get the current timestamp
now func() time.Time
}
// NewRefreshableSharedCredentials returns a pointer to a new Credentials object
// wrapping the Profile file provider.
func NewRefreshableSharedCredentials(filename string, profile string, now func() time.Time) *credentials.Credentials {
return credentials.New(&RefreshableSharedCredentialsProvider{
Filename: filename,
Profile: profile,
// To ensure the credentials are always valid, the provider should fetch the credentials every 5 minutes or so.
// It's set to 1 minute here.
exp: now().Add(time.Minute),
now: now,
})
}
// IsExpired returns if the shared credentials have expired.
func (p *RefreshableSharedCredentialsProvider) IsExpired() bool {
return p.now().After(p.exp)
}
// Retrieve reads and extracts the shared credentials from the current
// users home directory.
func (p *RefreshableSharedCredentialsProvider) Retrieve() (credentials.Value, error) {
filename, err := p.filename()
if err != nil {
return credentials.Value{}, err
}
creds, err := loadProfile(filename, p.profile())
if err != nil {
return credentials.Value{}, err
}
// After retrieving the credentials, reset the expiration time.
p.exp = p.now().Add(time.Minute)
return creds, nil
}
// loadProfiles loads from the file pointed to by shared credentials filename for profile.
// The credentials retrieved from the profile will be returned or error. Error will be
// returned if it fails to read from the file, or the data is invalid.
func loadProfile(filename, profile string) (credentials.Value, error) {
config, err := ini.Load(filename)
if err != nil {
return credentials.Value{}, errors.Wrap(err, "failed to load shared credentials file")
}
iniProfile, err := config.GetSection(profile)
if err != nil {
return credentials.Value{}, errors.Wrap(err, "failed to get profile")
}
id, err := iniProfile.GetKey("aws_access_key_id")
if err != nil {
return credentials.Value{}, errors.Wrapf(err, "shared credentials %s in %s did not contain aws_access_key_id", profile, filename)
}
secret, err := iniProfile.GetKey("aws_secret_access_key")
if err != nil {
return credentials.Value{}, errors.Wrapf(err, "shared credentials %s in %s did not contain aws_secret_access_key", profile, filename)
}
// Default to empty string if not found
token := iniProfile.Key("aws_session_token")
return credentials.Value{
AccessKeyID: id.String(),
SecretAccessKey: secret.String(),
SessionToken: token.String(),
}, nil
}
// filename returns the filename to use to read AWS shared credentials.
//
// Will return an error if the user's home directory path cannot be found.
func (p *RefreshableSharedCredentialsProvider) filename() (string, error) {
if len(p.Filename) != 0 {
return p.Filename, nil
}
if p.Filename = os.Getenv("AWS_SHARED_CREDENTIALS_FILE"); len(p.Filename) != 0 {
return p.Filename, nil
}
homeDir := UserHomeDir()
if len(homeDir) == 0 {
// Backwards compatibility of home directly not found error being returned.
return "", errors.New("user home directory not found.")
}
// SDK's default file path
// - Linux/Unix: $HOME/.aws/credentials
p.Filename = SharedCredentialsFilename()
return p.Filename, nil
}
// profile returns the AWS shared credentials profile. If empty will read
// environment variable "AWS_PROFILE". If that is not set profile will
// return "default".
func (p *RefreshableSharedCredentialsProvider) profile() string {
if p.Profile == "" {
p.Profile = os.Getenv("AWS_PROFILE")
}
if p.Profile == "" {
p.Profile = "default"
}
return p.Profile
}