| package tokens |
| |
| import ( |
| "errors" |
| "fmt" |
| |
| "github.com/racker/perigee" |
| "github.com/rackspace/gophercloud" |
| identity "github.com/rackspace/gophercloud/openstack/identity/v3" |
| ) |
| |
| func unacceptedAttributeErr(attribute string) error { |
| return fmt.Errorf("The base Identity V3 API does not accept authentication by %s", attribute) |
| } |
| |
| func redundantWithTokenErr(attribute string) error { |
| return fmt.Errorf("%s may not be provided when authenticating with a TokenID", attribute) |
| } |
| |
| func redundantWithUserID(attribute string) error { |
| return fmt.Errorf("%s may not be provided when authenticating with a UserID", attribute) |
| } |
| |
| var ( |
| // ErrAPIKeyProvided indicates that an APIKey was provided but can't be used. |
| ErrAPIKeyProvided = unacceptedAttributeErr("APIKey") |
| |
| // ErrTenantIDProvided indicates that a TenantID was provided but can't be used. |
| ErrTenantIDProvided = unacceptedAttributeErr("TenantID") |
| |
| // ErrTenantNameProvided indicates that a TenantName was provided but can't be used. |
| ErrTenantNameProvided = unacceptedAttributeErr("TenantName") |
| |
| // ErrUsernameWithToken indicates that a Username was provided, but token authentication is being used instead. |
| ErrUsernameWithToken = redundantWithTokenErr("Username") |
| |
| // ErrUserIDWithToken indicates that a UserID was provided, but token authentication is being used instead. |
| ErrUserIDWithToken = redundantWithTokenErr("UserID") |
| |
| // ErrDomainIDWithToken indicates that a DomainID was provided, but token authentication is being used instead. |
| ErrDomainIDWithToken = redundantWithTokenErr("DomainID") |
| |
| // ErrDomainNameWithToken indicates that a DomainName was provided, but token authentication is being used instead.s |
| ErrDomainNameWithToken = redundantWithTokenErr("DomainName") |
| |
| // ErrUsernameOrUserID indicates that neither username nor userID are specified, or both are at once. |
| ErrUsernameOrUserID = errors.New("Exactly one of Username and UserID must be provided for password authentication") |
| |
| // ErrDomainIDWithUserID indicates that a DomainID was provided, but unnecessary because a UserID is being used. |
| ErrDomainIDWithUserID = redundantWithUserID("DomainID") |
| |
| // ErrDomainNameWithUserID indicates that a DomainName was provided, but unnecessary because a UserID is being used. |
| ErrDomainNameWithUserID = redundantWithUserID("DomainName") |
| |
| // ErrDomainIDOrDomainName indicates that a username was provided, but no domain to scope it. |
| // It may also indicate that both a DomainID and a DomainName were provided at once. |
| ErrDomainIDOrDomainName = errors.New("You must provide exactly one of DomainID or DomainName to authenticate by Username") |
| |
| // ErrMissingPassword indicates that no password was provided and no token is available. |
| ErrMissingPassword = errors.New("You must provide a password to authenticate") |
| |
| // ErrScopeDomainIDOrDomainName indicates that a domain ID or Name was required in a Scope, but not present. |
| ErrScopeDomainIDOrDomainName = errors.New("You must provide exactly one of DomainID or DomainName in a Scope with ProjectName") |
| |
| // ErrScopeProjectIDOrProjectName indicates that both a ProjectID and a ProjectName were provided in a Scope. |
| ErrScopeProjectIDOrProjectName = errors.New("You must provide at most one of ProjectID or ProjectName in a Scope") |
| |
| // ErrScopeProjectIDAlone indicates that a ProjectID was provided with other constraints in a Scope. |
| ErrScopeProjectIDAlone = errors.New("ProjectID must be supplied alone in a Scope") |
| |
| // ErrScopeDomainName indicates that a DomainName was provided alone in a Scope. |
| ErrScopeDomainName = errors.New("DomainName must be supplied with a ProjectName or ProjectID in a Scope.") |
| |
| // ErrScopeEmpty indicates that no credentials were provided in a Scope. |
| ErrScopeEmpty = errors.New("You must provide either a Project or Domain in a Scope") |
| ) |
| |
| // TokenCreateResult contains the document structure returned from a Create call. |
| type TokenCreateResult map[string]interface{} |
| |
| // GetTokenID retrieves a token generated by a Create call from an token creation response. |
| func (r TokenCreateResult) GetTokenID() (string, error) { |
| return "", nil |
| } |
| |
| // Scope allows a created token to be limited to a specific domain or project. |
| type Scope struct { |
| ProjectID string |
| ProjectName string |
| DomainID string |
| DomainName string |
| } |
| |
| // Create authenticates and generates a new token. |
| func Create(c *identity.Client, ao gophercloud.AuthOptions, scope *Scope) (gophercloud.AuthResults, error) { |
| type domainReq struct { |
| ID *string `json:"id,omitempty"` |
| Name *string `json:"id,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"` |
| } |
| |
| type passwordReq struct { |
| User userReq `json:"user"` |
| } |
| |
| type tokenReq struct { |
| ID string `json:"id"` |
| } |
| |
| type identityReq struct { |
| Methods []string `json:"methods"` |
| Password *passwordReq `json:"token,omitempty"` |
| Token *tokenReq `json:"token,omitempty"` |
| } |
| |
| type scopeReq struct { |
| Domain *domainReq `json:"domain,omitempty"` |
| Project *projectReq `json:"project,omitempty"` |
| } |
| |
| type authReq struct { |
| Identity identityReq `json:"identity"` |
| Scope *scopeReq `json:"scope"` |
| } |
| |
| 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 |
| |
| // Test first for unrecognized arguments. |
| if ao.APIKey != "" { |
| return nil, ErrAPIKeyProvided |
| } |
| if ao.TenantID != "" { |
| return nil, ErrTenantIDProvided |
| } |
| if ao.TenantName != "" { |
| return nil, ErrTenantNameProvided |
| } |
| |
| if ao.Password == "" { |
| if c.TokenID != "" { |
| // Because we aren't using password authentication, it's an error to also provide any of the user-based authentication |
| // parameters. |
| if ao.Username != "" { |
| return nil, ErrUsernameWithToken |
| } |
| if ao.UserID != "" { |
| return nil, ErrUserIDWithToken |
| } |
| if ao.DomainID != "" { |
| return nil, ErrDomainIDWithToken |
| } |
| if ao.DomainName != "" { |
| return nil, ErrDomainNameWithToken |
| } |
| |
| // Configure the request for Token authentication. |
| req.Auth.Identity.Methods = []string{"token"} |
| req.Auth.Identity.Token = &tokenReq{ |
| ID: c.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 ao.Username == "" && ao.UserID == "" { |
| return nil, ErrUsernameOrUserID |
| } |
| |
| if ao.Username != "" { |
| // If Username is provided, UserID may not be provided. |
| if ao.UserID != "" { |
| return nil, ErrUsernameOrUserID |
| } |
| |
| // Either DomainID or DomainName must also be specified. |
| if ao.DomainID == "" && ao.DomainName == "" { |
| return nil, ErrDomainIDOrDomainName |
| } |
| |
| if ao.DomainID != "" { |
| if ao.DomainName != "" { |
| return nil, ErrDomainIDOrDomainName |
| } |
| |
| // Configure the request for Username and Password authentication with a DomainID. |
| req.Auth.Identity.Password = &passwordReq{ |
| User: userReq{ |
| Name: &ao.Username, |
| Password: ao.Password, |
| Domain: &domainReq{ID: &ao.DomainID}, |
| }, |
| } |
| } |
| |
| if ao.DomainName != "" { |
| // Configure the request for Username and Password authentication with a DomainName. |
| req.Auth.Identity.Password = &passwordReq{ |
| User: userReq{ |
| Name: &ao.Username, |
| Password: ao.Password, |
| Domain: &domainReq{Name: &ao.DomainName}, |
| }, |
| } |
| } |
| } |
| |
| if ao.UserID != "" { |
| // If UserID is specified, neither DomainID nor DomainName may be. |
| if ao.DomainID != "" { |
| return nil, ErrDomainIDWithUserID |
| } |
| if ao.DomainName != "" { |
| return nil, ErrDomainNameWithUserID |
| } |
| |
| // Configure the request for UserID and Password authentication. |
| req.Auth.Identity.Password = &passwordReq{ |
| User: userReq{ID: &ao.UserID}, |
| } |
| } |
| } |
| |
| // Add a "scope" element if a Scope has been provided. |
| if scope != nil { |
| if scope.ProjectName != "" { |
| // ProjectName provided: either DomainID or DomainName must also be supplied. |
| // ProjectID may not be supplied. |
| if scope.DomainID == "" && scope.DomainName == "" { |
| return nil, ErrScopeDomainIDOrDomainName |
| } |
| if scope.ProjectID != "" { |
| return nil, ErrScopeProjectIDOrProjectName |
| } |
| |
| if scope.DomainID != "" { |
| // ProjectName + DomainID |
| req.Auth.Scope = &scopeReq{ |
| Project: &projectReq{ |
| Name: &scope.ProjectName, |
| Domain: &domainReq{ID: &scope.DomainID}, |
| }, |
| } |
| } |
| |
| if scope.DomainName != "" { |
| // ProjectName + DomainName |
| req.Auth.Scope = &scopeReq{ |
| Project: &projectReq{ |
| Name: &scope.ProjectName, |
| Domain: &domainReq{Name: &scope.DomainName}, |
| }, |
| } |
| } |
| } else if scope.ProjectID != "" { |
| // ProjectID provided. ProjectName, DomainID, and DomainName may not be provided. |
| if scope.ProjectName != "" { |
| return nil, ErrScopeProjectIDOrProjectName |
| } |
| if scope.DomainID != "" { |
| return nil, ErrScopeProjectIDAlone |
| } |
| if scope.DomainName != "" { |
| return nil, ErrScopeProjectIDAlone |
| } |
| |
| // ProjectID |
| req.Auth.Scope = &scopeReq{ |
| Project: &projectReq{ID: &scope.ProjectID}, |
| } |
| } else if scope.DomainID != "" { |
| // DomainID provided. ProjectID, ProjectName, and DomainName may not be provided. |
| if scope.DomainName != "" { |
| return nil, ErrScopeDomainIDOrDomainName |
| } |
| |
| // DomainID |
| req.Auth.Scope = &scopeReq{ |
| Domain: &domainReq{ID: &scope.DomainID}, |
| } |
| } else if scope.DomainName != "" { |
| return nil, ErrScopeDomainName |
| } else { |
| return nil, ErrScopeEmpty |
| } |
| } |
| |
| var resp TokenCreateResult |
| perigee.Post(getTokenURL(c), perigee.Options{ |
| ReqBody: &req, |
| Results: &resp, |
| OkCodes: []int{201}, |
| }) |
| return &resp, nil |
| } |