github.com/huaweicloud/golangsdk@v0.0.0-20210831081626-d823fe11ceba/auth_options.go (about) 1 package golangsdk 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 := golangsdk.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 // AgencyNmae is the name of agnecy 86 AgencyName string `json:"-"` 87 88 // AgencyDomainName is the name of domain who created the agency 89 AgencyDomainName string `json:"-"` 90 91 // DelegatedProject is the name of delegated project 92 DelegatedProject string `json:"-"` 93 } 94 95 // ToTokenV2CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder 96 // interface in the v2 tokens package 97 func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { 98 // Populate the request map. 99 authMap := make(map[string]interface{}) 100 101 if opts.Username != "" { 102 if opts.Password != "" { 103 authMap["passwordCredentials"] = map[string]interface{}{ 104 "username": opts.Username, 105 "password": opts.Password, 106 } 107 } else { 108 return nil, ErrMissingInput{Argument: "Password"} 109 } 110 } else if opts.TokenID != "" { 111 authMap["token"] = map[string]interface{}{ 112 "id": opts.TokenID, 113 } 114 } else { 115 return nil, ErrMissingInput{Argument: "Username"} 116 } 117 118 if opts.TenantID != "" { 119 authMap["tenantId"] = opts.TenantID 120 } 121 if opts.TenantName != "" { 122 authMap["tenantName"] = opts.TenantName 123 } 124 125 return map[string]interface{}{"auth": authMap}, nil 126 } 127 128 func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) { 129 type domainReq struct { 130 ID *string `json:"id,omitempty"` 131 Name *string `json:"name,omitempty"` 132 } 133 134 type projectReq struct { 135 Domain *domainReq `json:"domain,omitempty"` 136 Name *string `json:"name,omitempty"` 137 ID *string `json:"id,omitempty"` 138 } 139 140 type userReq struct { 141 ID *string `json:"id,omitempty"` 142 Name *string `json:"name,omitempty"` 143 Password string `json:"password"` 144 Domain *domainReq `json:"domain,omitempty"` 145 } 146 147 type passwordReq struct { 148 User userReq `json:"user"` 149 } 150 151 type tokenReq struct { 152 ID string `json:"id"` 153 } 154 155 type identityReq struct { 156 Methods []string `json:"methods"` 157 Password *passwordReq `json:"password,omitempty"` 158 Token *tokenReq `json:"token,omitempty"` 159 } 160 161 type authReq struct { 162 Identity identityReq `json:"identity"` 163 } 164 165 type request struct { 166 Auth authReq `json:"auth"` 167 } 168 169 // Populate the request structure based on the provided arguments. Create and return an error 170 // if insufficient or incompatible information is present. 171 var req request 172 173 if opts.Password == "" { 174 if opts.TokenID != "" { 175 // Because we aren't using password authentication, it's an error to also provide any of the user-based authentication 176 // parameters. 177 if opts.Username != "" { 178 return nil, ErrUsernameWithToken{} 179 } 180 if opts.UserID != "" { 181 return nil, ErrUserIDWithToken{} 182 } 183 184 // Configure the request for Token authentication. 185 req.Auth.Identity.Methods = []string{"token"} 186 req.Auth.Identity.Token = &tokenReq{ 187 ID: opts.TokenID, 188 } 189 } else { 190 // If no password or token ID are available, authentication can't continue. 191 return nil, ErrMissingPassword{} 192 } 193 } else { 194 // Password authentication. 195 req.Auth.Identity.Methods = []string{"password"} 196 197 // At least one of Username and UserID must be specified. 198 if opts.Username == "" && opts.UserID == "" { 199 return nil, ErrUsernameOrUserID{} 200 } 201 202 if opts.Username != "" { 203 // If Username is provided, UserID may not be provided. 204 if opts.UserID != "" { 205 return nil, ErrUsernameOrUserID{} 206 } 207 208 // Either DomainID or DomainName must also be specified. 209 if opts.DomainID == "" && opts.DomainName == "" { 210 return nil, ErrDomainIDOrDomainName{} 211 } 212 213 if opts.DomainID != "" { 214 if opts.DomainName != "" { 215 return nil, ErrDomainIDOrDomainName{} 216 } 217 218 // Configure the request for Username and Password authentication with a DomainID. 219 req.Auth.Identity.Password = &passwordReq{ 220 User: userReq{ 221 Name: &opts.Username, 222 Password: opts.Password, 223 Domain: &domainReq{ID: &opts.DomainID}, 224 }, 225 } 226 } 227 228 if opts.DomainName != "" { 229 // Configure the request for Username and Password authentication with a DomainName. 230 req.Auth.Identity.Password = &passwordReq{ 231 User: userReq{ 232 Name: &opts.Username, 233 Password: opts.Password, 234 Domain: &domainReq{Name: &opts.DomainName}, 235 }, 236 } 237 } 238 } 239 240 if opts.UserID != "" { 241 // If UserID is specified, neither DomainID nor DomainName may be. 242 if opts.DomainID != "" { 243 return nil, ErrDomainIDWithUserID{} 244 } 245 if opts.DomainName != "" { 246 return nil, ErrDomainNameWithUserID{} 247 } 248 249 // Configure the request for UserID and Password authentication. 250 req.Auth.Identity.Password = &passwordReq{ 251 User: userReq{ID: &opts.UserID, Password: opts.Password}, 252 } 253 } 254 } 255 256 b, err := BuildRequestBody(req, "") 257 if err != nil { 258 return nil, err 259 } 260 261 if len(scope) != 0 { 262 b["auth"].(map[string]interface{})["scope"] = scope 263 } 264 265 return b, nil 266 } 267 268 func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { 269 var scope scopeInfo 270 271 if opts.TenantID != "" { 272 scope.ProjectID = opts.TenantID 273 } else { 274 if opts.TenantName != "" { 275 scope.ProjectName = opts.TenantName 276 scope.DomainID = opts.DomainID 277 scope.DomainName = opts.DomainName 278 } else { 279 // support scoping to domain 280 scope.DomainID = opts.DomainID 281 scope.DomainName = opts.DomainName 282 } 283 } 284 return scope.BuildTokenV3ScopeMap() 285 } 286 287 func (opts *AuthOptions) CanReauth() bool { 288 return opts.AllowReauth 289 } 290 291 func (opts *AuthOptions) AuthTokenID() string { 292 return "" 293 } 294 295 func (opts *AuthOptions) AuthHeaderDomainID() string { 296 return "" 297 } 298 299 // Implements the method of AuthOptionsProvider 300 func (opts AuthOptions) GetIdentityEndpoint() string { 301 return opts.IdentityEndpoint 302 } 303 304 type scopeInfo struct { 305 ProjectID string 306 ProjectName string 307 DomainID string 308 DomainName string 309 } 310 311 func (scope *scopeInfo) BuildTokenV3ScopeMap() (map[string]interface{}, error) { 312 if scope.ProjectName != "" { 313 // ProjectName provided: either DomainID or DomainName must also be supplied. 314 // ProjectID may not be supplied. 315 if scope.DomainID == "" && scope.DomainName == "" { 316 return nil, ErrScopeDomainIDOrDomainName{} 317 } 318 if scope.ProjectID != "" { 319 return nil, ErrScopeProjectIDOrProjectName{} 320 } 321 322 if scope.DomainID != "" { 323 // ProjectName + DomainID 324 return map[string]interface{}{ 325 "project": map[string]interface{}{ 326 "name": &scope.ProjectName, 327 "domain": map[string]interface{}{"id": &scope.DomainID}, 328 }, 329 }, nil 330 } 331 332 if scope.DomainName != "" { 333 // ProjectName + DomainName 334 return map[string]interface{}{ 335 "project": map[string]interface{}{ 336 "name": &scope.ProjectName, 337 "domain": map[string]interface{}{"name": &scope.DomainName}, 338 }, 339 }, nil 340 } 341 } else if scope.ProjectID != "" { 342 // ProjectID provided. ProjectName, DomainID, and DomainName may not be provided. 343 if scope.DomainID != "" { 344 return nil, ErrScopeProjectIDAlone{} 345 } 346 if scope.DomainName != "" { 347 return nil, ErrScopeProjectIDAlone{} 348 } 349 350 // ProjectID 351 return map[string]interface{}{ 352 "project": map[string]interface{}{ 353 "id": &scope.ProjectID, 354 }, 355 }, nil 356 } else if scope.DomainID != "" { 357 // DomainID provided. ProjectID, ProjectName, and DomainName may not be provided. 358 if scope.DomainName != "" { 359 return nil, ErrScopeDomainIDOrDomainName{} 360 } 361 362 // DomainID 363 return map[string]interface{}{ 364 "domain": map[string]interface{}{ 365 "id": &scope.DomainID, 366 }, 367 }, nil 368 } else if scope.DomainName != "" { 369 // DomainName 370 return map[string]interface{}{ 371 "domain": map[string]interface{}{ 372 "name": &scope.DomainName, 373 }, 374 }, nil 375 } 376 377 return nil, nil 378 } 379 380 type AgencyAuthOptions struct { 381 TokenID string 382 DomainID string 383 AgencyName string 384 AgencyDomainName string 385 DelegatedProject string 386 } 387 388 func (opts *AgencyAuthOptions) CanReauth() bool { 389 return false 390 } 391 392 func (opts *AgencyAuthOptions) AuthTokenID() string { 393 return opts.TokenID 394 } 395 396 func (opts *AgencyAuthOptions) AuthHeaderDomainID() string { 397 return opts.DomainID 398 } 399 400 func (opts *AgencyAuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { 401 scope := scopeInfo{ 402 ProjectName: opts.DelegatedProject, 403 DomainName: opts.AgencyDomainName, 404 } 405 406 return scope.BuildTokenV3ScopeMap() 407 } 408 409 func (opts *AgencyAuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) { 410 type assumeRoleReq struct { 411 DomainName string `json:"domain_name"` 412 AgencyName string `json:"xrole_name"` 413 } 414 415 type identityReq struct { 416 Methods []string `json:"methods"` 417 AssumeRole assumeRoleReq `json:"assume_role"` 418 } 419 420 type authReq struct { 421 Identity identityReq `json:"identity"` 422 } 423 424 var req authReq 425 req.Identity.Methods = []string{"assume_role"} 426 req.Identity.AssumeRole = assumeRoleReq{ 427 DomainName: opts.AgencyDomainName, 428 AgencyName: opts.AgencyName, 429 } 430 r, err := BuildRequestBody(req, "auth") 431 if err != nil { 432 return r, err 433 } 434 435 if len(scope) != 0 { 436 r["auth"].(map[string]interface{})["scope"] = scope 437 } 438 return r, nil 439 }