forked from Versent/saml2aws
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cfg.go
208 lines (170 loc) · 5.63 KB
/
cfg.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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
package cfg
import (
"fmt"
"net/url"
"github.com/mitchellh/go-homedir"
"github.com/pkg/errors"
ini "gopkg.in/ini.v1"
)
// ErrIdpAccountNotFound returned if the idp account is not found in the configuration file
var ErrIdpAccountNotFound = errors.New("IDP account not found, run configure to set it up")
const (
// DefaultConfigPath the default saml2aws configuration path
DefaultConfigPath = "~/.saml2aws"
// DefaultAmazonWebservicesURN URN used when authenticating to aws using SAML
// NOTE: This only needs to be changed to log into GovCloud
DefaultAmazonWebservicesURN = "urn:amazon:webservices"
// DefaultSessionDuration this is the default session duration which can be overridden in the AWS console
// see https://aws.amazon.com/blogs/security/enable-federated-api-access-to-your-aws-resources-for-up-to-12-hours-using-iam-roles/
DefaultSessionDuration = 3600
// DefaultProfile this is the default profile name used to save the credentials in the aws cli
DefaultProfile = "saml"
)
// IDPAccount saml IDP account
type IDPAccount struct {
AppID string `ini:"app_id"` // used by OneLogin and AzureAD
URL string `ini:"url"`
Username string `ini:"username"`
Provider string `ini:"provider"`
MFA string `ini:"mfa"`
SkipVerify bool `ini:"skip_verify"`
Timeout int `ini:"timeout"`
AmazonWebservicesURN string `ini:"aws_urn"`
SessionDuration int `ini:"aws_session_duration"`
Profile string `ini:"aws_profile"`
ResourceID string `ini:"resource_id"` // used by F5APM
Subdomain string `ini:"subdomain"` // used by OneLogin
RoleARN string `ini:"role_arn"`
}
func (ia IDPAccount) String() string {
var appID string
var policyID string
switch ia.Provider {
case "OneLogin":
appID = fmt.Sprintf(`
AppID: %s
Subdomain: %s`, ia.AppID, ia.Subdomain)
case "F5APM":
policyID = fmt.Sprintf("\n ResourceID: %s", ia.ResourceID)
case "AzureAD":
appID = fmt.Sprintf(`
AppID: %s`, ia.AppID)
}
return fmt.Sprintf(`account {%s%s
URL: %s
Username: %s
Provider: %s
MFA: %s
SkipVerify: %v
AmazonWebservicesURN: %s
SessionDuration: %d
Profile: %s
RoleARN: %s
}`, appID, policyID, ia.URL, ia.Username, ia.Provider, ia.MFA, ia.SkipVerify, ia.AmazonWebservicesURN, ia.SessionDuration, ia.Profile, ia.RoleARN)
}
// Validate validate the required / expected fields are set
func (ia *IDPAccount) Validate() error {
switch ia.Provider {
case "OneLogin":
if ia.AppID == "" {
return errors.New("app ID empty in idp account")
}
if ia.Subdomain == "" {
return errors.New("subdomain empty in idp account")
}
case "F5APM":
if ia.ResourceID == "" {
return errors.New("Resource ID empty in idp account")
}
case "AzureAD":
if ia.AppID == "" {
return errors.New("app ID empty in idp account")
}
}
if ia.URL == "" {
return errors.New("URL empty in idp account")
}
_, err := url.Parse(ia.URL)
if err != nil {
return errors.New("URL parse failed")
}
if ia.Provider == "" {
return errors.New("Provider empty in idp account")
}
if ia.MFA == "" {
return errors.New("MFA empty in idp account")
}
if ia.Profile == "" {
return errors.New("Profile empty in idp account")
}
return nil
}
// NewIDPAccount Create an idp account and fill in any default fields with sane values
func NewIDPAccount() *IDPAccount {
return &IDPAccount{
AmazonWebservicesURN: DefaultAmazonWebservicesURN,
SessionDuration: DefaultSessionDuration,
Profile: DefaultProfile,
}
}
// ConfigManager manage the various IDP account settings
type ConfigManager struct {
configPath string
}
// NewConfigManager build a new config manager and optionally override the config path
func NewConfigManager(configFile string) (*ConfigManager, error) {
if configFile == "" {
configFile = DefaultConfigPath
}
configPath, err := homedir.Expand(configFile)
if err != nil {
return nil, err
}
return &ConfigManager{configPath}, nil
}
// SaveIDPAccount save idp account
func (cm *ConfigManager) SaveIDPAccount(idpAccountName string, account *IDPAccount) error {
if err := account.Validate(); err != nil {
return errors.Wrap(err, "Account validation failed")
}
cfg, err := ini.LoadSources(ini.LoadOptions{Loose: true}, cm.configPath)
if err != nil {
return errors.Wrap(err, "Unable to load configuration file")
}
newSec, err := cfg.NewSection(idpAccountName)
if err != nil {
return errors.Wrap(err, "Unable to build a new section in configuration file")
}
err = newSec.ReflectFrom(account)
if err != nil {
return errors.Wrap(err, "Unable to save account to configuration file")
}
err = cfg.SaveTo(cm.configPath)
if err != nil {
return errors.Wrap(err, "Failed to save configuration file")
}
return nil
}
// LoadIDPAccount load the idp account and default to an empty one if it doesn't exist
func (cm *ConfigManager) LoadIDPAccount(idpAccountName string) (*IDPAccount, error) {
cfg, err := ini.LoadSources(ini.LoadOptions{Loose: true}, cm.configPath)
if err != nil {
return nil, errors.Wrap(err, "Unable to load configuration file")
}
// attempt to map a specific idp account by name
// this will return an empty account if one is not found by the given name
account, err := readAccount(idpAccountName, cfg)
if err != nil {
return nil, errors.Wrap(err, "Unable to read idp account")
}
return account, nil
}
func readAccount(idpAccountName string, cfg *ini.File) (*IDPAccount, error) {
account := NewIDPAccount()
sec := cfg.Section(idpAccountName)
err := sec.MapTo(account)
if err != nil {
return nil, errors.Wrap(err, "Unable to map account")
}
return account, nil
}