|  | package tokens | 
|  |  | 
|  | import ( | 
|  | "github.com/racker/perigee" | 
|  | "github.com/rackspace/gophercloud" | 
|  | ) | 
|  |  | 
|  | // 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 | 
|  | } | 
|  |  | 
|  | func subjectTokenHeaders(c *gophercloud.ServiceClient, subjectToken string) map[string]string { | 
|  | h := c.Provider.AuthenticatedHeaders() | 
|  | h["X-Subject-Token"] = subjectToken | 
|  | return h | 
|  | } | 
|  |  | 
|  | // Create authenticates and either generates a new token, or changes the Scope of an existing token. | 
|  | func Create(c *gophercloud.ServiceClient, options gophercloud.AuthOptions, scope *Scope) CreateResult { | 
|  | 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 scopeReq struct { | 
|  | Domain  *domainReq  `json:"domain,omitempty"` | 
|  | Project *projectReq `json:"project,omitempty"` | 
|  | } | 
|  |  | 
|  | type authReq struct { | 
|  | Identity identityReq `json:"identity"` | 
|  | Scope    *scopeReq   `json:"scope,omitempty"` | 
|  | } | 
|  |  | 
|  | 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 options.APIKey != "" { | 
|  | return createErr(ErrAPIKeyProvided) | 
|  | } | 
|  | if options.TenantID != "" { | 
|  | return createErr(ErrTenantIDProvided) | 
|  | } | 
|  | if options.TenantName != "" { | 
|  | return createErr(ErrTenantNameProvided) | 
|  | } | 
|  |  | 
|  | if options.Password == "" { | 
|  | if c.Provider.TokenID != "" { | 
|  | // Because we aren't using password authentication, it's an error to also provide any of the user-based authentication | 
|  | // parameters. | 
|  | if options.Username != "" { | 
|  | return createErr(ErrUsernameWithToken) | 
|  | } | 
|  | if options.UserID != "" { | 
|  | return createErr(ErrUserIDWithToken) | 
|  | } | 
|  | if options.DomainID != "" { | 
|  | return createErr(ErrDomainIDWithToken) | 
|  | } | 
|  | if options.DomainName != "" { | 
|  | return createErr(ErrDomainNameWithToken) | 
|  | } | 
|  |  | 
|  | // Configure the request for Token authentication. | 
|  | req.Auth.Identity.Methods = []string{"token"} | 
|  | req.Auth.Identity.Token = &tokenReq{ | 
|  | ID: c.Provider.TokenID, | 
|  | } | 
|  | } else { | 
|  | // If no password or token ID are available, authentication can't continue. | 
|  | return createErr(ErrMissingPassword) | 
|  | } | 
|  | } else { | 
|  | // Password authentication. | 
|  | req.Auth.Identity.Methods = []string{"password"} | 
|  |  | 
|  | // At least one of Username and UserID must be specified. | 
|  | if options.Username == "" && options.UserID == "" { | 
|  | return createErr(ErrUsernameOrUserID) | 
|  | } | 
|  |  | 
|  | if options.Username != "" { | 
|  | // If Username is provided, UserID may not be provided. | 
|  | if options.UserID != "" { | 
|  | return createErr(ErrUsernameOrUserID) | 
|  | } | 
|  |  | 
|  | // Either DomainID or DomainName must also be specified. | 
|  | if options.DomainID == "" && options.DomainName == "" { | 
|  | return createErr(ErrDomainIDOrDomainName) | 
|  | } | 
|  |  | 
|  | if options.DomainID != "" { | 
|  | if options.DomainName != "" { | 
|  | return createErr(ErrDomainIDOrDomainName) | 
|  | } | 
|  |  | 
|  | // Configure the request for Username and Password authentication with a DomainID. | 
|  | req.Auth.Identity.Password = &passwordReq{ | 
|  | User: userReq{ | 
|  | Name:     &options.Username, | 
|  | Password: options.Password, | 
|  | Domain:   &domainReq{ID: &options.DomainID}, | 
|  | }, | 
|  | } | 
|  | } | 
|  |  | 
|  | if options.DomainName != "" { | 
|  | // Configure the request for Username and Password authentication with a DomainName. | 
|  | req.Auth.Identity.Password = &passwordReq{ | 
|  | User: userReq{ | 
|  | Name:     &options.Username, | 
|  | Password: options.Password, | 
|  | Domain:   &domainReq{Name: &options.DomainName}, | 
|  | }, | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if options.UserID != "" { | 
|  | // If UserID is specified, neither DomainID nor DomainName may be. | 
|  | if options.DomainID != "" { | 
|  | return createErr(ErrDomainIDWithUserID) | 
|  | } | 
|  | if options.DomainName != "" { | 
|  | return createErr(ErrDomainNameWithUserID) | 
|  | } | 
|  |  | 
|  | // Configure the request for UserID and Password authentication. | 
|  | req.Auth.Identity.Password = &passwordReq{ | 
|  | User: userReq{ID: &options.UserID, Password: options.Password}, | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // 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 createErr(ErrScopeDomainIDOrDomainName) | 
|  | } | 
|  | if scope.ProjectID != "" { | 
|  | return createErr(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.DomainID != "" { | 
|  | return createErr(ErrScopeProjectIDAlone) | 
|  | } | 
|  | if scope.DomainName != "" { | 
|  | return createErr(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 createErr(ErrScopeDomainIDOrDomainName) | 
|  | } | 
|  |  | 
|  | // DomainID | 
|  | req.Auth.Scope = &scopeReq{ | 
|  | Domain: &domainReq{ID: &scope.DomainID}, | 
|  | } | 
|  | } else if scope.DomainName != "" { | 
|  | return createErr(ErrScopeDomainName) | 
|  | } else { | 
|  | return createErr(ErrScopeEmpty) | 
|  | } | 
|  | } | 
|  |  | 
|  | var result CreateResult | 
|  | var response *perigee.Response | 
|  | response, result.Err = perigee.Request("POST", tokenURL(c), perigee.Options{ | 
|  | ReqBody: &req, | 
|  | Results: &result.Resp, | 
|  | OkCodes: []int{201}, | 
|  | }) | 
|  | if result.Err != nil { | 
|  | return result | 
|  | } | 
|  | result.header = response.HttpResponse.Header | 
|  | return result | 
|  | } | 
|  |  | 
|  | // Get validates and retrieves information about another token. | 
|  | func Get(c *gophercloud.ServiceClient, token string) GetResult { | 
|  | var result GetResult | 
|  | var response *perigee.Response | 
|  | response, result.Err = perigee.Request("GET", tokenURL(c), perigee.Options{ | 
|  | MoreHeaders: subjectTokenHeaders(c, token), | 
|  | Results:     &result.Resp, | 
|  | OkCodes:     []int{200, 203}, | 
|  | }) | 
|  | if result.Err != nil { | 
|  | return result | 
|  | } | 
|  | result.header = response.HttpResponse.Header | 
|  | return result | 
|  | } | 
|  |  | 
|  | // Validate determines if a specified token is valid or not. | 
|  | func Validate(c *gophercloud.ServiceClient, token string) (bool, error) { | 
|  | response, err := perigee.Request("HEAD", tokenURL(c), perigee.Options{ | 
|  | MoreHeaders: subjectTokenHeaders(c, token), | 
|  | OkCodes:     []int{204, 404}, | 
|  | }) | 
|  | if err != nil { | 
|  | return false, err | 
|  | } | 
|  |  | 
|  | return response.StatusCode == 204, nil | 
|  | } | 
|  |  | 
|  | // Revoke immediately makes specified token invalid. | 
|  | func Revoke(c *gophercloud.ServiceClient, token string) error { | 
|  | _, err := perigee.Request("DELETE", tokenURL(c), perigee.Options{ | 
|  | MoreHeaders: subjectTokenHeaders(c, token), | 
|  | OkCodes:     []int{204}, | 
|  | }) | 
|  | return err | 
|  | } |