Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / github.com / gophercloud / gophercloud / auth_options.go
1 package gophercloud
2
3 /*
4 AuthOptions stores information needed to authenticate to an OpenStack Cloud.
5 You can populate one manually, or use a provider's AuthOptionsFromEnv() function
6 to read relevant information from the standard environment variables. Pass one
7 to a provider's AuthenticatedClient function to authenticate and obtain a
8 ProviderClient representing an active session on that provider.
9
10 Its fields are the union of those recognized by each identity implementation and
11 provider.
12
13 An example of manually providing authentication information:
14
15   opts := gophercloud.AuthOptions{
16     IdentityEndpoint: "https://openstack.example.com:5000/v2.0",
17     Username: "{username}",
18     Password: "{password}",
19     TenantID: "{tenant_id}",
20   }
21
22   provider, err := openstack.AuthenticatedClient(opts)
23
24 An example of using AuthOptionsFromEnv(), where the environment variables can
25 be read from a file, such as a standard openrc file:
26
27   opts, err := openstack.AuthOptionsFromEnv()
28   provider, err := openstack.AuthenticatedClient(opts)
29 */
30 type AuthOptions struct {
31         // IdentityEndpoint specifies the HTTP endpoint that is required to work with
32         // the Identity API of the appropriate version. While it's ultimately needed by
33         // all of the identity services, it will often be populated by a provider-level
34         // function.
35         //
36         // The IdentityEndpoint is typically referred to as the "auth_url" or
37         // "OS_AUTH_URL" in the information provided by the cloud operator.
38         IdentityEndpoint string `json:"-"`
39
40         // Username is required if using Identity V2 API. Consult with your provider's
41         // control panel to discover your account's username. In Identity V3, either
42         // UserID or a combination of Username and DomainID or DomainName are needed.
43         Username string `json:"username,omitempty"`
44         UserID   string `json:"-"`
45
46         Password string `json:"password,omitempty"`
47
48         // At most one of DomainID and DomainName must be provided if using Username
49         // with Identity V3. Otherwise, either are optional.
50         DomainID   string `json:"-"`
51         DomainName string `json:"name,omitempty"`
52
53         // The TenantID and TenantName fields are optional for the Identity V2 API.
54         // The same fields are known as project_id and project_name in the Identity
55         // V3 API, but are collected as TenantID and TenantName here in both cases.
56         // Some providers allow you to specify a TenantName instead of the TenantId.
57         // Some require both. Your provider's authentication policies will determine
58         // how these fields influence authentication.
59         // If DomainID or DomainName are provided, they will also apply to TenantName.
60         // It is not currently possible to authenticate with Username and a Domain
61         // and scope to a Project in a different Domain by using TenantName. To
62         // accomplish that, the ProjectID will need to be provided as the TenantID
63         // option.
64         TenantID   string `json:"tenantId,omitempty"`
65         TenantName string `json:"tenantName,omitempty"`
66
67         // AllowReauth should be set to true if you grant permission for Gophercloud to
68         // cache your credentials in memory, and to allow Gophercloud to attempt to
69         // re-authenticate automatically if/when your token expires.  If you set it to
70         // false, it will not cache these settings, but re-authentication will not be
71         // possible.  This setting defaults to false.
72         //
73         // NOTE: The reauth function will try to re-authenticate endlessly if left
74         // unchecked. The way to limit the number of attempts is to provide a custom
75         // HTTP client to the provider client and provide a transport that implements
76         // the RoundTripper interface and stores the number of failed retries. For an
77         // example of this, see here:
78         // https://github.com/rackspace/rack/blob/1.0.0/auth/clients.go#L311
79         AllowReauth bool `json:"-"`
80
81         // TokenID allows users to authenticate (possibly as another user) with an
82         // authentication token ID.
83         TokenID string `json:"-"`
84
85         // Scope determines the scoping of the authentication request.
86         Scope *AuthScope `json:"-"`
87
88         // Authentication through Application Credentials requires supplying name, project and secret
89         // For project we can use TenantID
90         ApplicationCredentialID     string `json:"-"`
91         ApplicationCredentialName   string `json:"-"`
92         ApplicationCredentialSecret string `json:"-"`
93 }
94
95 // AuthScope allows a created token to be limited to a specific domain or project.
96 type AuthScope struct {
97         ProjectID   string
98         ProjectName string
99         DomainID    string
100         DomainName  string
101 }
102
103 // ToTokenV2CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder
104 // interface in the v2 tokens package
105 func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) {
106         // Populate the request map.
107         authMap := make(map[string]interface{})
108
109         if opts.Username != "" {
110                 if opts.Password != "" {
111                         authMap["passwordCredentials"] = map[string]interface{}{
112                                 "username": opts.Username,
113                                 "password": opts.Password,
114                         }
115                 } else {
116                         return nil, ErrMissingInput{Argument: "Password"}
117                 }
118         } else if opts.TokenID != "" {
119                 authMap["token"] = map[string]interface{}{
120                         "id": opts.TokenID,
121                 }
122         } else {
123                 return nil, ErrMissingInput{Argument: "Username"}
124         }
125
126         if opts.TenantID != "" {
127                 authMap["tenantId"] = opts.TenantID
128         }
129         if opts.TenantName != "" {
130                 authMap["tenantName"] = opts.TenantName
131         }
132
133         return map[string]interface{}{"auth": authMap}, nil
134 }
135
136 func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) {
137         type domainReq struct {
138                 ID   *string `json:"id,omitempty"`
139                 Name *string `json:"name,omitempty"`
140         }
141
142         type projectReq struct {
143                 Domain *domainReq `json:"domain,omitempty"`
144                 Name   *string    `json:"name,omitempty"`
145                 ID     *string    `json:"id,omitempty"`
146         }
147
148         type userReq struct {
149                 ID       *string    `json:"id,omitempty"`
150                 Name     *string    `json:"name,omitempty"`
151                 Password string     `json:"password,omitempty"`
152                 Domain   *domainReq `json:"domain,omitempty"`
153         }
154
155         type passwordReq struct {
156                 User userReq `json:"user"`
157         }
158
159         type tokenReq struct {
160                 ID string `json:"id"`
161         }
162
163         type applicationCredentialReq struct {
164                 ID     *string  `json:"id,omitempty"`
165                 Name   *string  `json:"name,omitempty"`
166                 User   *userReq `json:"user,omitempty"`
167                 Secret *string  `json:"secret,omitempty"`
168         }
169
170         type identityReq struct {
171                 Methods               []string                  `json:"methods"`
172                 Password              *passwordReq              `json:"password,omitempty"`
173                 Token                 *tokenReq                 `json:"token,omitempty"`
174                 ApplicationCredential *applicationCredentialReq `json:"application_credential,omitempty"`
175         }
176
177         type authReq struct {
178                 Identity identityReq `json:"identity"`
179         }
180
181         type request struct {
182                 Auth authReq `json:"auth"`
183         }
184
185         // Populate the request structure based on the provided arguments. Create and return an error
186         // if insufficient or incompatible information is present.
187         var req request
188
189         if opts.Password == "" {
190                 if opts.TokenID != "" {
191                         // Because we aren't using password authentication, it's an error to also provide any of the user-based authentication
192                         // parameters.
193                         if opts.Username != "" {
194                                 return nil, ErrUsernameWithToken{}
195                         }
196                         if opts.UserID != "" {
197                                 return nil, ErrUserIDWithToken{}
198                         }
199                         if opts.DomainID != "" {
200                                 return nil, ErrDomainIDWithToken{}
201                         }
202                         if opts.DomainName != "" {
203                                 return nil, ErrDomainNameWithToken{}
204                         }
205
206                         // Configure the request for Token authentication.
207                         req.Auth.Identity.Methods = []string{"token"}
208                         req.Auth.Identity.Token = &tokenReq{
209                                 ID: opts.TokenID,
210                         }
211
212                 } else if opts.ApplicationCredentialID != "" {
213                         // Configure the request for ApplicationCredentialID authentication.
214                         // https://github.com/openstack/keystoneauth/blob/stable/rocky/keystoneauth1/identity/v3/application_credential.py#L48-L67
215                         // There are three kinds of possible application_credential requests
216                         // 1. application_credential id + secret
217                         // 2. application_credential name + secret + user_id
218                         // 3. application_credential name + secret + username + domain_id / domain_name
219                         if opts.ApplicationCredentialSecret == "" {
220                                 return nil, ErrAppCredMissingSecret{}
221                         }
222                         req.Auth.Identity.Methods = []string{"application_credential"}
223                         req.Auth.Identity.ApplicationCredential = &applicationCredentialReq{
224                                 ID:     &opts.ApplicationCredentialID,
225                                 Secret: &opts.ApplicationCredentialSecret,
226                         }
227                 } else if opts.ApplicationCredentialName != "" {
228                         if opts.ApplicationCredentialSecret == "" {
229                                 return nil, ErrAppCredMissingSecret{}
230                         }
231
232                         var userRequest *userReq
233
234                         if opts.UserID != "" {
235                                 // UserID could be used without the domain information
236                                 userRequest = &userReq{
237                                         ID: &opts.UserID,
238                                 }
239                         }
240
241                         if userRequest == nil && opts.Username == "" {
242                                 // Make sure that Username or UserID are provided
243                                 return nil, ErrUsernameOrUserID{}
244                         }
245
246                         if userRequest == nil && opts.DomainID != "" {
247                                 userRequest = &userReq{
248                                         Name:   &opts.Username,
249                                         Domain: &domainReq{ID: &opts.DomainID},
250                                 }
251                         }
252
253                         if userRequest == nil && opts.DomainName != "" {
254                                 userRequest = &userReq{
255                                         Name:   &opts.Username,
256                                         Domain: &domainReq{Name: &opts.DomainName},
257                                 }
258                         }
259
260                         // Make sure that DomainID or DomainName are provided among Username
261                         if userRequest == nil {
262                                 return nil, ErrDomainIDOrDomainName{}
263                         }
264
265                         req.Auth.Identity.Methods = []string{"application_credential"}
266                         req.Auth.Identity.ApplicationCredential = &applicationCredentialReq{
267                                 Name:   &opts.ApplicationCredentialName,
268                                 User:   userRequest,
269                                 Secret: &opts.ApplicationCredentialSecret,
270                         }
271                 } else {
272                         // If no password or token ID or ApplicationCredential are available, authentication can't continue.
273                         return nil, ErrMissingPassword{}
274                 }
275         } else {
276                 // Password authentication.
277                 req.Auth.Identity.Methods = []string{"password"}
278
279                 // At least one of Username and UserID must be specified.
280                 if opts.Username == "" && opts.UserID == "" {
281                         return nil, ErrUsernameOrUserID{}
282                 }
283
284                 if opts.Username != "" {
285                         // If Username is provided, UserID may not be provided.
286                         if opts.UserID != "" {
287                                 return nil, ErrUsernameOrUserID{}
288                         }
289
290                         // Either DomainID or DomainName must also be specified.
291                         if opts.DomainID == "" && opts.DomainName == "" {
292                                 return nil, ErrDomainIDOrDomainName{}
293                         }
294
295                         if opts.DomainID != "" {
296                                 if opts.DomainName != "" {
297                                         return nil, ErrDomainIDOrDomainName{}
298                                 }
299
300                                 // Configure the request for Username and Password authentication with a DomainID.
301                                 req.Auth.Identity.Password = &passwordReq{
302                                         User: userReq{
303                                                 Name:     &opts.Username,
304                                                 Password: opts.Password,
305                                                 Domain:   &domainReq{ID: &opts.DomainID},
306                                         },
307                                 }
308                         }
309
310                         if opts.DomainName != "" {
311                                 // Configure the request for Username and Password authentication with a DomainName.
312                                 req.Auth.Identity.Password = &passwordReq{
313                                         User: userReq{
314                                                 Name:     &opts.Username,
315                                                 Password: opts.Password,
316                                                 Domain:   &domainReq{Name: &opts.DomainName},
317                                         },
318                                 }
319                         }
320                 }
321
322                 if opts.UserID != "" {
323                         // If UserID is specified, neither DomainID nor DomainName may be.
324                         if opts.DomainID != "" {
325                                 return nil, ErrDomainIDWithUserID{}
326                         }
327                         if opts.DomainName != "" {
328                                 return nil, ErrDomainNameWithUserID{}
329                         }
330
331                         // Configure the request for UserID and Password authentication.
332                         req.Auth.Identity.Password = &passwordReq{
333                                 User: userReq{ID: &opts.UserID, Password: opts.Password},
334                         }
335                 }
336         }
337
338         b, err := BuildRequestBody(req, "")
339         if err != nil {
340                 return nil, err
341         }
342
343         if len(scope) != 0 {
344                 b["auth"].(map[string]interface{})["scope"] = scope
345         }
346
347         return b, nil
348 }
349
350 func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) {
351         // For backwards compatibility.
352         // If AuthOptions.Scope was not set, try to determine it.
353         // This works well for common scenarios.
354         if opts.Scope == nil {
355                 opts.Scope = new(AuthScope)
356                 if opts.TenantID != "" {
357                         opts.Scope.ProjectID = opts.TenantID
358                 } else {
359                         if opts.TenantName != "" {
360                                 opts.Scope.ProjectName = opts.TenantName
361                                 opts.Scope.DomainID = opts.DomainID
362                                 opts.Scope.DomainName = opts.DomainName
363                         }
364                 }
365         }
366
367         if opts.Scope.ProjectName != "" {
368                 // ProjectName provided: either DomainID or DomainName must also be supplied.
369                 // ProjectID may not be supplied.
370                 if opts.Scope.DomainID == "" && opts.Scope.DomainName == "" {
371                         return nil, ErrScopeDomainIDOrDomainName{}
372                 }
373                 if opts.Scope.ProjectID != "" {
374                         return nil, ErrScopeProjectIDOrProjectName{}
375                 }
376
377                 if opts.Scope.DomainID != "" {
378                         // ProjectName + DomainID
379                         return map[string]interface{}{
380                                 "project": map[string]interface{}{
381                                         "name":   &opts.Scope.ProjectName,
382                                         "domain": map[string]interface{}{"id": &opts.Scope.DomainID},
383                                 },
384                         }, nil
385                 }
386
387                 if opts.Scope.DomainName != "" {
388                         // ProjectName + DomainName
389                         return map[string]interface{}{
390                                 "project": map[string]interface{}{
391                                         "name":   &opts.Scope.ProjectName,
392                                         "domain": map[string]interface{}{"name": &opts.Scope.DomainName},
393                                 },
394                         }, nil
395                 }
396         } else if opts.Scope.ProjectID != "" {
397                 // ProjectID provided. ProjectName, DomainID, and DomainName may not be provided.
398                 if opts.Scope.DomainID != "" {
399                         return nil, ErrScopeProjectIDAlone{}
400                 }
401                 if opts.Scope.DomainName != "" {
402                         return nil, ErrScopeProjectIDAlone{}
403                 }
404
405                 // ProjectID
406                 return map[string]interface{}{
407                         "project": map[string]interface{}{
408                                 "id": &opts.Scope.ProjectID,
409                         },
410                 }, nil
411         } else if opts.Scope.DomainID != "" {
412                 // DomainID provided. ProjectID, ProjectName, and DomainName may not be provided.
413                 if opts.Scope.DomainName != "" {
414                         return nil, ErrScopeDomainIDOrDomainName{}
415                 }
416
417                 // DomainID
418                 return map[string]interface{}{
419                         "domain": map[string]interface{}{
420                                 "id": &opts.Scope.DomainID,
421                         },
422                 }, nil
423         } else if opts.Scope.DomainName != "" {
424                 // DomainName
425                 return map[string]interface{}{
426                         "domain": map[string]interface{}{
427                                 "name": &opts.Scope.DomainName,
428                         },
429                 }, nil
430         }
431
432         return nil, nil
433 }
434
435 func (opts AuthOptions) CanReauth() bool {
436         return opts.AllowReauth
437 }