123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364 |
- package gophercloud
- /*
- AuthOptions stores information needed to authenticate to an OpenStack Cloud.
- You can populate one manually, or use a provider's AuthOptionsFromEnv() function
- to read relevant information from the standard environment variables. Pass one
- to a provider's AuthenticatedClient function to authenticate and obtain a
- ProviderClient representing an active session on that provider.
- Its fields are the union of those recognized by each identity implementation and
- provider.
- An example of manually providing authentication information:
- opts := gophercloud.AuthOptions{
- IdentityEndpoint: "https://openstack.example.com:5000/v2.0",
- Username: "{username}",
- Password: "{password}",
- TenantID: "{tenant_id}",
- }
- provider, err := openstack.AuthenticatedClient(opts)
- An example of using AuthOptionsFromEnv(), where the environment variables can
- be read from a file, such as a standard openrc file:
- opts, err := openstack.AuthOptionsFromEnv()
- provider, err := openstack.AuthenticatedClient(opts)
- */
- type AuthOptions struct {
- // IdentityEndpoint specifies the HTTP endpoint that is required to work with
- // the Identity API of the appropriate version. While it's ultimately needed by
- // all of the identity services, it will often be populated by a provider-level
- // function.
- //
- // The IdentityEndpoint is typically referred to as the "auth_url" or
- // "OS_AUTH_URL" in the information provided by the cloud operator.
- IdentityEndpoint string `json:"-"`
- // Username is required if using Identity V2 API. Consult with your provider's
- // control panel to discover your account's username. In Identity V3, either
- // UserID or a combination of Username and DomainID or DomainName are needed.
- Username string `json:"username,omitempty"`
- UserID string `json:"-"`
- Password string `json:"password,omitempty"`
- // At most one of DomainID and DomainName must be provided if using Username
- // with Identity V3. Otherwise, either are optional.
- DomainID string `json:"-"`
- DomainName string `json:"name,omitempty"`
- // The TenantID and TenantName fields are optional for the Identity V2 API.
- // The same fields are known as project_id and project_name in the Identity
- // V3 API, but are collected as TenantID and TenantName here in both cases.
- // Some providers allow you to specify a TenantName instead of the TenantId.
- // Some require both. Your provider's authentication policies will determine
- // how these fields influence authentication.
- // If DomainID or DomainName are provided, they will also apply to TenantName.
- // It is not currently possible to authenticate with Username and a Domain
- // and scope to a Project in a different Domain by using TenantName. To
- // accomplish that, the ProjectID will need to be provided as the TenantID
- // option.
- TenantID string `json:"tenantId,omitempty"`
- TenantName string `json:"tenantName,omitempty"`
- // AllowReauth should be set to true if you grant permission for Gophercloud to
- // cache your credentials in memory, and to allow Gophercloud to attempt to
- // re-authenticate automatically if/when your token expires. If you set it to
- // false, it will not cache these settings, but re-authentication will not be
- // possible. This setting defaults to false.
- //
- // NOTE: The reauth function will try to re-authenticate endlessly if left
- // unchecked. The way to limit the number of attempts is to provide a custom
- // HTTP client to the provider client and provide a transport that implements
- // the RoundTripper interface and stores the number of failed retries. For an
- // example of this, see here:
- // https://github.com/rackspace/rack/blob/1.0.0/auth/clients.go#L311
- AllowReauth bool `json:"-"`
- // TokenID allows users to authenticate (possibly as another user) with an
- // authentication token ID.
- TokenID string `json:"-"`
- // Scope determines the scoping of the authentication request.
- Scope *AuthScope `json:"-"`
- }
- // AuthScope allows a created token to be limited to a specific domain or project.
- type AuthScope struct {
- ProjectID string
- ProjectName string
- DomainID string
- DomainName string
- }
- // ToTokenV2CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder
- // interface in the v2 tokens package
- func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) {
- // Populate the request map.
- authMap := make(map[string]interface{})
- if opts.Username != "" {
- if opts.Password != "" {
- authMap["passwordCredentials"] = map[string]interface{}{
- "username": opts.Username,
- "password": opts.Password,
- }
- } else {
- return nil, ErrMissingInput{Argument: "Password"}
- }
- } else if opts.TokenID != "" {
- authMap["token"] = map[string]interface{}{
- "id": opts.TokenID,
- }
- } else {
- return nil, ErrMissingInput{Argument: "Username"}
- }
- if opts.TenantID != "" {
- authMap["tenantId"] = opts.TenantID
- }
- if opts.TenantName != "" {
- authMap["tenantName"] = opts.TenantName
- }
- return map[string]interface{}{"auth": authMap}, nil
- }
- func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) {
- type domainReq struct {
- ID *string `json:"id,omitempty"`
- Name *string `json:"name,omitempty"`
- }
- type projectReq struct {
- Domain *domainReq `json:"domain,omitempty"`
- Name *string `json:"name,omitempty"`
- ID *string `json:"id,omitempty"`
- }
- type userReq struct {
- ID *string `json:"id,omitempty"`
- Name *string `json:"name,omitempty"`
- Password string `json:"password"`
- Domain *domainReq `json:"domain,omitempty"`
- }
- type passwordReq struct {
- User userReq `json:"user"`
- }
- type tokenReq struct {
- ID string `json:"id"`
- }
- type identityReq struct {
- Methods []string `json:"methods"`
- Password *passwordReq `json:"password,omitempty"`
- Token *tokenReq `json:"token,omitempty"`
- }
- type authReq struct {
- Identity identityReq `json:"identity"`
- }
- type request struct {
- Auth authReq `json:"auth"`
- }
- // Populate the request structure based on the provided arguments. Create and return an error
- // if insufficient or incompatible information is present.
- var req request
- if opts.Password == "" {
- if opts.TokenID != "" {
- // Because we aren't using password authentication, it's an error to also provide any of the user-based authentication
- // parameters.
- if opts.Username != "" {
- return nil, ErrUsernameWithToken{}
- }
- if opts.UserID != "" {
- return nil, ErrUserIDWithToken{}
- }
- if opts.DomainID != "" {
- return nil, ErrDomainIDWithToken{}
- }
- if opts.DomainName != "" {
- return nil, ErrDomainNameWithToken{}
- }
- // Configure the request for Token authentication.
- req.Auth.Identity.Methods = []string{"token"}
- req.Auth.Identity.Token = &tokenReq{
- ID: opts.TokenID,
- }
- } else {
- // If no password or token ID are available, authentication can't continue.
- return nil, ErrMissingPassword{}
- }
- } else {
- // Password authentication.
- req.Auth.Identity.Methods = []string{"password"}
- // At least one of Username and UserID must be specified.
- if opts.Username == "" && opts.UserID == "" {
- return nil, ErrUsernameOrUserID{}
- }
- if opts.Username != "" {
- // If Username is provided, UserID may not be provided.
- if opts.UserID != "" {
- return nil, ErrUsernameOrUserID{}
- }
- // Either DomainID or DomainName must also be specified.
- if opts.DomainID == "" && opts.DomainName == "" {
- return nil, ErrDomainIDOrDomainName{}
- }
- if opts.DomainID != "" {
- if opts.DomainName != "" {
- return nil, ErrDomainIDOrDomainName{}
- }
- // Configure the request for Username and Password authentication with a DomainID.
- req.Auth.Identity.Password = &passwordReq{
- User: userReq{
- Name: &opts.Username,
- Password: opts.Password,
- Domain: &domainReq{ID: &opts.DomainID},
- },
- }
- }
- if opts.DomainName != "" {
- // Configure the request for Username and Password authentication with a DomainName.
- req.Auth.Identity.Password = &passwordReq{
- User: userReq{
- Name: &opts.Username,
- Password: opts.Password,
- Domain: &domainReq{Name: &opts.DomainName},
- },
- }
- }
- }
- if opts.UserID != "" {
- // If UserID is specified, neither DomainID nor DomainName may be.
- if opts.DomainID != "" {
- return nil, ErrDomainIDWithUserID{}
- }
- if opts.DomainName != "" {
- return nil, ErrDomainNameWithUserID{}
- }
- // Configure the request for UserID and Password authentication.
- req.Auth.Identity.Password = &passwordReq{
- User: userReq{ID: &opts.UserID, Password: opts.Password},
- }
- }
- }
- b, err := BuildRequestBody(req, "")
- if err != nil {
- return nil, err
- }
- if len(scope) != 0 {
- b["auth"].(map[string]interface{})["scope"] = scope
- }
- return b, nil
- }
- func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) {
- // For backwards compatibility.
- // If AuthOptions.Scope was not set, try to determine it.
- // This works well for common scenarios.
- if opts.Scope == nil {
- opts.Scope = new(AuthScope)
- if opts.TenantID != "" {
- opts.Scope.ProjectID = opts.TenantID
- } else {
- if opts.TenantName != "" {
- opts.Scope.ProjectName = opts.TenantName
- opts.Scope.DomainID = opts.DomainID
- opts.Scope.DomainName = opts.DomainName
- }
- }
- }
- if opts.Scope.ProjectName != "" {
- // ProjectName provided: either DomainID or DomainName must also be supplied.
- // ProjectID may not be supplied.
- if opts.Scope.DomainID == "" && opts.Scope.DomainName == "" {
- return nil, ErrScopeDomainIDOrDomainName{}
- }
- if opts.Scope.ProjectID != "" {
- return nil, ErrScopeProjectIDOrProjectName{}
- }
- if opts.Scope.DomainID != "" {
- // ProjectName + DomainID
- return map[string]interface{}{
- "project": map[string]interface{}{
- "name": &opts.Scope.ProjectName,
- "domain": map[string]interface{}{"id": &opts.Scope.DomainID},
- },
- }, nil
- }
- if opts.Scope.DomainName != "" {
- // ProjectName + DomainName
- return map[string]interface{}{
- "project": map[string]interface{}{
- "name": &opts.Scope.ProjectName,
- "domain": map[string]interface{}{"name": &opts.Scope.DomainName},
- },
- }, nil
- }
- } else if opts.Scope.ProjectID != "" {
- // ProjectID provided. ProjectName, DomainID, and DomainName may not be provided.
- if opts.Scope.DomainID != "" {
- return nil, ErrScopeProjectIDAlone{}
- }
- if opts.Scope.DomainName != "" {
- return nil, ErrScopeProjectIDAlone{}
- }
- // ProjectID
- return map[string]interface{}{
- "project": map[string]interface{}{
- "id": &opts.Scope.ProjectID,
- },
- }, nil
- } else if opts.Scope.DomainID != "" {
- // DomainID provided. ProjectID, ProjectName, and DomainName may not be provided.
- if opts.Scope.DomainName != "" {
- return nil, ErrScopeDomainIDOrDomainName{}
- }
- // DomainID
- return map[string]interface{}{
- "domain": map[string]interface{}{
- "id": &opts.Scope.DomainID,
- },
- }, nil
- } else if opts.Scope.DomainName != "" {
- // DomainName
- return map[string]interface{}{
- "domain": map[string]interface{}{
- "name": &opts.Scope.DomainName,
- },
- }, nil
- }
- return nil, nil
- }
- func (opts AuthOptions) CanReauth() bool {
- return opts.AllowReauth
- }
|