blob: 0b4a02bb9de3a6a3808a06ebe6bd3c86976455c8 [file] [log] [blame]
Ash Wilson70dfe0c2014-08-28 13:57:09 -04001package gophercloud
2
Ash Wilson730a5062014-10-31 15:13:35 -04003/*
Jon Perrittdb0ae142016-03-13 00:33:41 -06004type AuthOptionsBuilder interface {
5 ToTokenCreateMap() (map[string]interface{}, error)
6}
7*/
8
9/*
Ash Wilson730a5062014-10-31 15:13:35 -040010AuthOptions stores information needed to authenticate to an OpenStack cluster.
11You can populate one manually, or use a provider's AuthOptionsFromEnv() function
12to read relevant information from the standard environment variables. Pass one
13to a provider's AuthenticatedClient function to authenticate and obtain a
14ProviderClient representing an active session on that provider.
15
16Its fields are the union of those recognized by each identity implementation and
17provider.
18*/
Ash Wilson70dfe0c2014-08-28 13:57:09 -040019type AuthOptions struct {
Jamie Hannafordb280dea2014-10-24 15:14:06 +020020 // IdentityEndpoint specifies the HTTP endpoint that is required to work with
Ash Wilson730a5062014-10-31 15:13:35 -040021 // the Identity API of the appropriate version. While it's ultimately needed by
22 // all of the identity services, it will often be populated by a provider-level
23 // function.
Jon Perrittdb0ae142016-03-13 00:33:41 -060024 IdentityEndpoint string `json:"-"`
Ash Wilson70dfe0c2014-08-28 13:57:09 -040025
Jamie Hannafordb280dea2014-10-24 15:14:06 +020026 // Username is required if using Identity V2 API. Consult with your provider's
27 // control panel to discover your account's username. In Identity V3, either
Ash Wilson730a5062014-10-31 15:13:35 -040028 // UserID or a combination of Username and DomainID or DomainName are needed.
Jon Perrittdb0ae142016-03-13 00:33:41 -060029 Username string `json:"username,omitempty"`
30 UserID string `json:"id,omitempty"`
Ash Wilson70dfe0c2014-08-28 13:57:09 -040031
Jon Perrittdb0ae142016-03-13 00:33:41 -060032 Password string `json:"password,omitempty"`
Ash Wilson70dfe0c2014-08-28 13:57:09 -040033
Jamie Hannafordb280dea2014-10-24 15:14:06 +020034 // At most one of DomainID and DomainName must be provided if using Username
35 // with Identity V3. Otherwise, either are optional.
Jon Perrittdb0ae142016-03-13 00:33:41 -060036 DomainID string `json:"id,omitempty"`
37 DomainName string `json:"name,omitempty"`
Ash Wilson70dfe0c2014-08-28 13:57:09 -040038
39 // The TenantID and TenantName fields are optional for the Identity V2 API.
40 // Some providers allow you to specify a TenantName instead of the TenantId.
Ash Wilson730a5062014-10-31 15:13:35 -040041 // Some require both. Your provider's authentication policies will determine
Ash Wilson70dfe0c2014-08-28 13:57:09 -040042 // how these fields influence authentication.
Jon Perrittdb0ae142016-03-13 00:33:41 -060043 TenantID string `json:"tenantId,omitempty"`
44 TenantName string `json:"tenantName,omitempty"`
Ash Wilson70dfe0c2014-08-28 13:57:09 -040045
46 // AllowReauth should be set to true if you grant permission for Gophercloud to
47 // cache your credentials in memory, and to allow Gophercloud to attempt to
48 // re-authenticate automatically if/when your token expires. If you set it to
49 // false, it will not cache these settings, but re-authentication will not be
50 // possible. This setting defaults to false.
Jon Perrittdb0ae142016-03-13 00:33:41 -060051 AllowReauth bool `json:"-"`
jrperritt95b74c82015-07-28 20:39:27 -060052
jrperritt1f218c82015-07-29 08:54:18 -060053 // TokenID allows users to authenticate (possibly as another user) with an
54 // authentication token ID.
55 TokenID string
Ash Wilson70dfe0c2014-08-28 13:57:09 -040056}
Jon Perrittdb0ae142016-03-13 00:33:41 -060057
58// ToTokenV2CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder
59// interface in the v2 tokens package
60func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) {
61 v2Opts := AuthOptionsV2{
62 PasswordCredentials: &PasswordCredentialsV2{
63 Username: opts.Username,
64 Password: opts.Password,
65 },
66 TenantID: opts.TenantID,
67 TenantName: opts.TenantName,
68 TokenCredentials: &TokenCredentialsV2{
69 ID: opts.TokenID,
70 },
71 }
72
73 b, err := BuildRequestBody(v2Opts, "auth")
74 if err != nil {
75 return nil, err
76 }
Jon Perrittdb0ae142016-03-13 00:33:41 -060077 return b, nil
78}
79
80func (opts AuthOptions) ToTokenV3CreateMap(scope *ScopeOptsV3) (map[string]interface{}, error) {
Jon Perritt2be387a2016-03-31 09:31:58 -050081 type domainReq struct {
82 ID *string `json:"id,omitempty"`
83 Name *string `json:"name,omitempty"`
84 }
Jon Perritt397ade62016-03-15 06:55:02 -050085
Jon Perritt2be387a2016-03-31 09:31:58 -050086 type projectReq struct {
87 Domain *domainReq `json:"domain,omitempty"`
88 Name *string `json:"name,omitempty"`
89 ID *string `json:"id,omitempty"`
90 }
Jon Perritt397ade62016-03-15 06:55:02 -050091
Jon Perritt2be387a2016-03-31 09:31:58 -050092 type userReq struct {
93 ID *string `json:"id,omitempty"`
94 Name *string `json:"name,omitempty"`
95 Password string `json:"password"`
96 Domain *domainReq `json:"domain,omitempty"`
97 }
Jon Perritt397ade62016-03-15 06:55:02 -050098
Jon Perritt2be387a2016-03-31 09:31:58 -050099 type passwordReq struct {
100 User userReq `json:"user"`
101 }
Jon Perritt397ade62016-03-15 06:55:02 -0500102
Jon Perritt2be387a2016-03-31 09:31:58 -0500103 type tokenReq struct {
104 ID string `json:"id"`
105 }
Jon Perritt397ade62016-03-15 06:55:02 -0500106
Jon Perritt2be387a2016-03-31 09:31:58 -0500107 type identityReq struct {
108 Methods []string `json:"methods"`
109 Password *passwordReq `json:"password,omitempty"`
110 Token *tokenReq `json:"token,omitempty"`
111 }
Jon Perritt397ade62016-03-15 06:55:02 -0500112
Jon Perritt2be387a2016-03-31 09:31:58 -0500113 type scopeReq struct {
114 Domain *domainReq `json:"domain,omitempty"`
115 Project *projectReq `json:"project,omitempty"`
116 }
Jon Perritt397ade62016-03-15 06:55:02 -0500117
Jon Perritt2be387a2016-03-31 09:31:58 -0500118 type authReq struct {
119 Identity identityReq `json:"identity"`
120 Scope *scopeReq `json:"scope,omitempty"`
121 }
Jon Perritt397ade62016-03-15 06:55:02 -0500122
Jon Perritt2be387a2016-03-31 09:31:58 -0500123 type request struct {
124 Auth authReq `json:"auth"`
125 }
Jon Perritt397ade62016-03-15 06:55:02 -0500126
Jon Perritt2be387a2016-03-31 09:31:58 -0500127 // Populate the request structure based on the provided arguments. Create and return an error
128 // if insufficient or incompatible information is present.
129 var (
130 req request
131 err = ErrInvalidInput{}
132 )
Jon Perritt397ade62016-03-15 06:55:02 -0500133
Jon Perritt2be387a2016-03-31 09:31:58 -0500134 if opts.Password == "" {
135 if opts.TokenID != "" {
136 // Because we aren't using password authentication, it's an error to also provide any of the user-based authentication
137 // parameters.
138 if opts.Username != "" {
139 err.Info = "You may not provide a username when using token-based authentication."
140 return nil, err
141 //return createErr(ErrUsernameWithToken)
142 }
143 if opts.UserID != "" {
144 err.Info = "You may not provide a user ID when using token-based authentication."
145 return nil, err
146 }
147 if opts.DomainID != "" {
148 err.Info = "You may not provide a domain ID when using token-based authentication."
149 return nil, err
150 }
151 if opts.DomainName != "" {
152 err.Info = "You may not provide a domain name when using token-based authentication."
153 return nil, err
154 }
Jon Perritt397ade62016-03-15 06:55:02 -0500155
Jon Perritt2be387a2016-03-31 09:31:58 -0500156 // Configure the request for Token authentication.
157 req.Auth.Identity.Methods = []string{"token"}
158 req.Auth.Identity.Token = &tokenReq{
159 ID: opts.TokenID,
Jon Perritt397ade62016-03-15 06:55:02 -0500160 }
161 } else {
Jon Perritt2be387a2016-03-31 09:31:58 -0500162 // If no password or token ID are available, authentication can't continue.
163 err.Info = "You must provide either a password or a token."
164 return nil, err
Jon Perritt397ade62016-03-15 06:55:02 -0500165 }
Jon Perrittdb0ae142016-03-13 00:33:41 -0600166 } else {
Jon Perritt2be387a2016-03-31 09:31:58 -0500167 // Password authentication.
168 req.Auth.Identity.Methods = []string{"password"}
Jon Perrittdb0ae142016-03-13 00:33:41 -0600169
Jon Perritt2be387a2016-03-31 09:31:58 -0500170 // At least one of Username and UserID must be specified.
171 if opts.Username == "" && opts.UserID == "" {
172 err.Info = "You may not provide a username when using token-based authentication."
173 return nil, err
174 }
175
176 if opts.Username != "" {
177 // If Username is provided, UserID may not be provided.
178 if opts.UserID != "" {
179 err.Info = "One and only one of username and user ID may be provided for password-based authentication."
180 return nil, err
181 //return createErr(ErrUsernameOrUserID)
182 }
183
184 // Either DomainID or DomainName must also be specified.
185 if opts.DomainID == "" && opts.DomainName == "" {
186 err.Info = "You must provide exactly one of DomainID or DomainName to authenticate by Username"
187 return nil, err
188 //return createErr(ErrDomainIDOrDomainName)
189 }
190
191 if opts.DomainID != "" {
192 if opts.DomainName != "" {
193 err.Info = "You must provide exactly one of DomainID or DomainName to authenticate by Username"
194 return nil, err
195 //return createErr(ErrDomainIDOrDomainName)
196 }
197
198 // Configure the request for Username and Password authentication with a DomainID.
199 req.Auth.Identity.Password = &passwordReq{
200 User: userReq{
201 Name: &opts.Username,
202 Password: opts.Password,
203 Domain: &domainReq{ID: &opts.DomainID},
Jon Perrittdb0ae142016-03-13 00:33:41 -0600204 },
Jon Perritt2be387a2016-03-31 09:31:58 -0500205 }
206 }
Jon Perrittdb0ae142016-03-13 00:33:41 -0600207
Jon Perritt2be387a2016-03-31 09:31:58 -0500208 if opts.DomainName != "" {
209 // Configure the request for Username and Password authentication with a DomainName.
210 req.Auth.Identity.Password = &passwordReq{
211 User: userReq{
212 Name: &opts.Username,
213 Password: opts.Password,
214 Domain: &domainReq{Name: &opts.DomainName},
215 },
216 }
217 }
218 }
219
220 if opts.UserID != "" {
221 // If UserID is specified, neither DomainID nor DomainName may be.
222 if opts.DomainID != "" {
223 return createErr(ErrDomainIDWithUserID)
224 }
225 if opts.DomainName != "" {
226 return createErr(ErrDomainNameWithUserID)
227 }
228
229 // Configure the request for UserID and Password authentication.
230 req.Auth.Identity.Password = &passwordReq{
231 User: userReq{ID: &opts.UserID, Password: opts.Password},
232 }
Jon Perrittdb0ae142016-03-13 00:33:41 -0600233 }
234 }
235
Jon Perritt2be387a2016-03-31 09:31:58 -0500236 // Add a "scope" element if a Scope has been provided.
237 if scope != nil {
238 if scope.ProjectName != "" {
239 // ProjectName provided: either DomainID or DomainName must also be supplied.
240 // ProjectID may not be supplied.
241 if scope.DomainID == "" && scope.DomainName == "" {
242 return createErr(ErrScopeDomainIDOrDomainName)
243 }
244 if scope.ProjectID != "" {
245 return createErr(ErrScopeProjectIDOrProjectName)
246 }
Jon Perrittdb0ae142016-03-13 00:33:41 -0600247
Jon Perritt2be387a2016-03-31 09:31:58 -0500248 if scope.DomainID != "" {
249 // ProjectName + DomainID
250 req.Auth.Scope = &scopeReq{
251 Project: &projectReq{
252 Name: &scope.ProjectName,
253 Domain: &domainReq{ID: &scope.DomainID},
254 },
255 }
256 }
257
258 if scope.DomainName != "" {
259 // ProjectName + DomainName
260 req.Auth.Scope = &scopeReq{
261 Project: &projectReq{
262 Name: &scope.ProjectName,
263 Domain: &domainReq{Name: &scope.DomainName},
264 },
265 }
266 }
267 } else if scope.ProjectID != "" {
268 // ProjectID provided. ProjectName, DomainID, and DomainName may not be provided.
269 if scope.DomainID != "" {
270 return createErr(ErrScopeProjectIDAlone)
271 }
272 if scope.DomainName != "" {
273 return createErr(ErrScopeProjectIDAlone)
274 }
275
276 // ProjectID
277 req.Auth.Scope = &scopeReq{
278 Project: &projectReq{ID: &scope.ProjectID},
279 }
280 } else if scope.DomainID != "" {
281 // DomainID provided. ProjectID, ProjectName, and DomainName may not be provided.
282 if scope.DomainName != "" {
283 return createErr(ErrScopeDomainIDOrDomainName)
284 }
285
286 // DomainID
287 req.Auth.Scope = &scopeReq{
288 Domain: &domainReq{ID: &scope.DomainID},
289 }
290 } else if scope.DomainName != "" {
291 return createErr(ErrScopeDomainName)
292 } else {
293 return createErr(ErrScopeEmpty)
294 }
295 }
Jon Perrittdb0ae142016-03-13 00:33:41 -0600296}