Ash Wilson | 85d8265 | 2014-08-28 13:57:46 -0400 | [diff] [blame] | 1 | package tokens |
| 2 | |
jrperritt | 29ae6b3 | 2016-04-13 12:59:37 -0500 | [diff] [blame] | 3 | import "github.com/gophercloud/gophercloud" |
Ash Wilson | 2491b4c | 2015-02-12 16:13:39 -0500 | [diff] [blame] | 4 | |
jrperritt | 29ae6b3 | 2016-04-13 12:59:37 -0500 | [diff] [blame] | 5 | // Scope allows a created token to be limited to a specific domain or project. |
| 6 | type Scope struct { |
| 7 | ProjectID string `json:"scope.project.id,omitempty" not:"ProjectName,DomainID,DomainName"` |
| 8 | ProjectName string `json:"scope.project.name,omitempty"` |
| 9 | DomainID string `json:"scope.project.id,omitempty" not:"ProjectName,ProjectID,DomainName"` |
| 10 | DomainName string `json:"scope.project.id,omitempty"` |
| 11 | } |
Ash Wilson | 85d8265 | 2014-08-28 13:57:46 -0400 | [diff] [blame] | 12 | |
Jon Perritt | db0ae14 | 2016-03-13 00:33:41 -0600 | [diff] [blame] | 13 | // AuthOptionsBuilder describes any argument that may be passed to the Create call. |
| 14 | type AuthOptionsBuilder interface { |
| 15 | // ToTokenV3CreateMap assembles the Create request body, returning an error if parameters are |
| 16 | // missing or inconsistent. |
jrperritt | 0bc5578 | 2016-07-27 13:50:14 -0500 | [diff] [blame] | 17 | ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error) |
| 18 | ToTokenV3ScopeMap() (map[string]interface{}, error) |
| 19 | CanReauth() bool |
jrperritt | 29ae6b3 | 2016-04-13 12:59:37 -0500 | [diff] [blame] | 20 | } |
| 21 | |
| 22 | type AuthOptions struct { |
| 23 | // IdentityEndpoint specifies the HTTP endpoint that is required to work with |
| 24 | // the Identity API of the appropriate version. While it's ultimately needed by |
| 25 | // all of the identity services, it will often be populated by a provider-level |
| 26 | // function. |
| 27 | IdentityEndpoint string `json:"-"` |
| 28 | |
| 29 | // Username is required if using Identity V2 API. Consult with your provider's |
| 30 | // control panel to discover your account's username. In Identity V3, either |
| 31 | // UserID or a combination of Username and DomainID or DomainName are needed. |
| 32 | Username string `json:"username,omitempty"` |
| 33 | UserID string `json:"id,omitempty"` |
| 34 | |
| 35 | Password string `json:"password,omitempty"` |
| 36 | |
| 37 | // At most one of DomainID and DomainName must be provided if using Username |
| 38 | // with Identity V3. Otherwise, either are optional. |
| 39 | DomainID string `json:"id,omitempty"` |
| 40 | DomainName string `json:"name,omitempty"` |
| 41 | |
jrperritt | 29ae6b3 | 2016-04-13 12:59:37 -0500 | [diff] [blame] | 42 | // AllowReauth should be set to true if you grant permission for Gophercloud to |
| 43 | // cache your credentials in memory, and to allow Gophercloud to attempt to |
| 44 | // re-authenticate automatically if/when your token expires. If you set it to |
| 45 | // false, it will not cache these settings, but re-authentication will not be |
| 46 | // possible. This setting defaults to false. |
| 47 | AllowReauth bool `json:"-"` |
| 48 | |
| 49 | // TokenID allows users to authenticate (possibly as another user) with an |
| 50 | // authentication token ID. |
jrperritt | 0bc5578 | 2016-07-27 13:50:14 -0500 | [diff] [blame] | 51 | TokenID string `json:"-"` |
| 52 | |
| 53 | Scope Scope `json:"-"` |
jrperritt | 29ae6b3 | 2016-04-13 12:59:37 -0500 | [diff] [blame] | 54 | } |
| 55 | |
jrperritt | 0bc5578 | 2016-07-27 13:50:14 -0500 | [diff] [blame] | 56 | func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) { |
| 57 | gophercloudAuthOpts := gophercloud.AuthOptions{ |
| 58 | Username: opts.Username, |
| 59 | UserID: opts.UserID, |
| 60 | Password: opts.Password, |
| 61 | DomainID: opts.DomainID, |
| 62 | DomainName: opts.DomainName, |
| 63 | AllowReauth: opts.AllowReauth, |
| 64 | TokenID: opts.TokenID, |
jrperritt | 29ae6b3 | 2016-04-13 12:59:37 -0500 | [diff] [blame] | 65 | } |
| 66 | |
jrperritt | 0bc5578 | 2016-07-27 13:50:14 -0500 | [diff] [blame] | 67 | return gophercloudAuthOpts.ToTokenV3CreateMap(scope) |
| 68 | } |
jrperritt | 29ae6b3 | 2016-04-13 12:59:37 -0500 | [diff] [blame] | 69 | |
jrperritt | 0bc5578 | 2016-07-27 13:50:14 -0500 | [diff] [blame] | 70 | func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { |
| 71 | if opts.Scope.ProjectName != "" { |
| 72 | // ProjectName provided: either DomainID or DomainName must also be supplied. |
| 73 | // ProjectID may not be supplied. |
| 74 | if opts.Scope.DomainID == "" && opts.Scope.DomainName == "" { |
| 75 | return nil, gophercloud.ErrScopeDomainIDOrDomainName{} |
jrperritt | 29ae6b3 | 2016-04-13 12:59:37 -0500 | [diff] [blame] | 76 | } |
jrperritt | 0bc5578 | 2016-07-27 13:50:14 -0500 | [diff] [blame] | 77 | if opts.Scope.ProjectID != "" { |
| 78 | return nil, gophercloud.ErrScopeProjectIDOrProjectName{} |
jrperritt | 29ae6b3 | 2016-04-13 12:59:37 -0500 | [diff] [blame] | 79 | } |
| 80 | |
jrperritt | 0bc5578 | 2016-07-27 13:50:14 -0500 | [diff] [blame] | 81 | if opts.Scope.DomainID != "" { |
| 82 | // ProjectName + DomainID |
| 83 | return map[string]interface{}{ |
| 84 | "project": map[string]interface{}{ |
| 85 | "name": &opts.Scope.ProjectName, |
| 86 | "domain": map[string]interface{}{"id": &opts.Scope.DomainID}, |
| 87 | }, |
| 88 | }, nil |
jrperritt | 29ae6b3 | 2016-04-13 12:59:37 -0500 | [diff] [blame] | 89 | } |
| 90 | |
jrperritt | 0bc5578 | 2016-07-27 13:50:14 -0500 | [diff] [blame] | 91 | if opts.Scope.DomainName != "" { |
| 92 | // ProjectName + DomainName |
| 93 | return map[string]interface{}{ |
| 94 | "project": map[string]interface{}{ |
| 95 | "name": &opts.Scope.ProjectName, |
| 96 | "domain": map[string]interface{}{"name": &opts.Scope.DomainName}, |
| 97 | }, |
| 98 | }, nil |
jrperritt | 29ae6b3 | 2016-04-13 12:59:37 -0500 | [diff] [blame] | 99 | } |
jrperritt | 0bc5578 | 2016-07-27 13:50:14 -0500 | [diff] [blame] | 100 | } else if opts.Scope.ProjectID != "" { |
| 101 | // ProjectID provided. ProjectName, DomainID, and DomainName may not be provided. |
| 102 | if opts.Scope.DomainID != "" { |
| 103 | return nil, gophercloud.ErrScopeProjectIDAlone{} |
jrperritt | 29ae6b3 | 2016-04-13 12:59:37 -0500 | [diff] [blame] | 104 | } |
jrperritt | 0bc5578 | 2016-07-27 13:50:14 -0500 | [diff] [blame] | 105 | if opts.Scope.DomainName != "" { |
| 106 | return nil, gophercloud.ErrScopeProjectIDAlone{} |
| 107 | } |
| 108 | |
| 109 | // ProjectID |
| 110 | return map[string]interface{}{ |
| 111 | "project": map[string]interface{}{ |
| 112 | "id": &opts.Scope.ProjectID, |
| 113 | }, |
| 114 | }, nil |
| 115 | } else if opts.Scope.DomainID != "" { |
| 116 | // DomainID provided. ProjectID, ProjectName, and DomainName may not be provided. |
| 117 | if opts.Scope.DomainName != "" { |
| 118 | return nil, gophercloud.ErrScopeDomainIDOrDomainName{} |
| 119 | } |
| 120 | |
| 121 | // DomainID |
| 122 | return map[string]interface{}{ |
| 123 | "domain": map[string]interface{}{ |
| 124 | "id": &opts.Scope.DomainID, |
| 125 | }, |
| 126 | }, nil |
| 127 | } else if opts.Scope.DomainName != "" { |
| 128 | return nil, gophercloud.ErrScopeDomainName{} |
jrperritt | 29ae6b3 | 2016-04-13 12:59:37 -0500 | [diff] [blame] | 129 | } |
| 130 | |
jrperritt | 0bc5578 | 2016-07-27 13:50:14 -0500 | [diff] [blame] | 131 | return nil, nil |
| 132 | } |
| 133 | |
| 134 | func (opts *AuthOptions) CanReauth() bool { |
| 135 | return opts.AllowReauth |
Ash Wilson | 85d8265 | 2014-08-28 13:57:46 -0400 | [diff] [blame] | 136 | } |
| 137 | |
Ash Wilson | 6425a41 | 2014-08-29 12:30:35 -0400 | [diff] [blame] | 138 | func subjectTokenHeaders(c *gophercloud.ServiceClient, subjectToken string) map[string]string { |
Krzysztof Kwapisiewicz | baaaf3e | 2016-02-03 15:18:16 +0100 | [diff] [blame] | 139 | return map[string]string{ |
| 140 | "X-Subject-Token": subjectToken, |
| 141 | } |
Ash Wilson | 46d913f | 2014-08-29 11:00:11 -0400 | [diff] [blame] | 142 | } |
| 143 | |
Ash Wilson | e555086 | 2014-08-28 15:37:09 -0400 | [diff] [blame] | 144 | // Create authenticates and either generates a new token, or changes the Scope of an existing token. |
jrperritt | 0bc5578 | 2016-07-27 13:50:14 -0500 | [diff] [blame] | 145 | func Create(c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResult) { |
| 146 | scope, err := opts.ToTokenV3ScopeMap() |
Jon Perritt | db0ae14 | 2016-03-13 00:33:41 -0600 | [diff] [blame] | 147 | if err != nil { |
| 148 | r.Err = err |
Jon Perritt | 2be387a | 2016-03-31 09:31:58 -0500 | [diff] [blame] | 149 | return |
Ash Wilson | 85d8265 | 2014-08-28 13:57:46 -0400 | [diff] [blame] | 150 | } |
jrperritt | 0bc5578 | 2016-07-27 13:50:14 -0500 | [diff] [blame] | 151 | |
| 152 | b, err := opts.ToTokenV3CreateMap(scope) |
| 153 | if err != nil { |
| 154 | r.Err = err |
| 155 | return |
| 156 | } |
| 157 | |
jrperritt | 9b7b9e6 | 2016-07-11 22:30:50 -0500 | [diff] [blame] | 158 | resp, err := c.Post(tokenURL(c), b, &r.Body, &gophercloud.RequestOpts{ |
| 159 | MoreHeaders: map[string]string{"X-Auth-Token": ""}, |
| 160 | }) |
Eugene Yakubovich | 9152721 | 2016-09-30 10:17:32 -0700 | [diff] [blame] | 161 | r.Err = err |
Jon Perritt | db0ae14 | 2016-03-13 00:33:41 -0600 | [diff] [blame] | 162 | if resp != nil { |
| 163 | r.Header = resp.Header |
Ash Wilson | 85d8265 | 2014-08-28 13:57:46 -0400 | [diff] [blame] | 164 | } |
jrperritt | 29ae6b3 | 2016-04-13 12:59:37 -0500 | [diff] [blame] | 165 | return |
Ash Wilson | 85d8265 | 2014-08-28 13:57:46 -0400 | [diff] [blame] | 166 | } |
Ash Wilson | 46d913f | 2014-08-29 11:00:11 -0400 | [diff] [blame] | 167 | |
Ash Wilson | 5266e49 | 2014-09-09 15:44:30 -0400 | [diff] [blame] | 168 | // Get validates and retrieves information about another token. |
Jon Perritt | 2be387a | 2016-03-31 09:31:58 -0500 | [diff] [blame] | 169 | func Get(c *gophercloud.ServiceClient, token string) (r GetResult) { |
jrperritt | 29ae6b3 | 2016-04-13 12:59:37 -0500 | [diff] [blame] | 170 | resp, err := c.Get(tokenURL(c), &r.Body, &gophercloud.RequestOpts{ |
Jamie Hannaford | 562a7d5 | 2015-03-24 16:20:16 +0100 | [diff] [blame] | 171 | MoreHeaders: subjectTokenHeaders(c, token), |
| 172 | OkCodes: []int{200, 203}, |
Ash Wilson | 46d913f | 2014-08-29 11:00:11 -0400 | [diff] [blame] | 173 | }) |
Jon Perritt | db0ae14 | 2016-03-13 00:33:41 -0600 | [diff] [blame] | 174 | if resp != nil { |
jrperritt | 29ae6b3 | 2016-04-13 12:59:37 -0500 | [diff] [blame] | 175 | r.Err = err |
Jon Perritt | db0ae14 | 2016-03-13 00:33:41 -0600 | [diff] [blame] | 176 | r.Header = resp.Header |
Ash Wilson | 46d913f | 2014-08-29 11:00:11 -0400 | [diff] [blame] | 177 | } |
jrperritt | 29ae6b3 | 2016-04-13 12:59:37 -0500 | [diff] [blame] | 178 | return |
Ash Wilson | 46d913f | 2014-08-29 11:00:11 -0400 | [diff] [blame] | 179 | } |
| 180 | |
| 181 | // Validate determines if a specified token is valid or not. |
Ash Wilson | 6425a41 | 2014-08-29 12:30:35 -0400 | [diff] [blame] | 182 | func Validate(c *gophercloud.ServiceClient, token string) (bool, error) { |
jrperritt | 29ae6b3 | 2016-04-13 12:59:37 -0500 | [diff] [blame] | 183 | resp, err := c.Request("HEAD", tokenURL(c), &gophercloud.RequestOpts{ |
Ash Wilson | 46d913f | 2014-08-29 11:00:11 -0400 | [diff] [blame] | 184 | MoreHeaders: subjectTokenHeaders(c, token), |
| 185 | OkCodes: []int{204, 404}, |
| 186 | }) |
| 187 | if err != nil { |
| 188 | return false, err |
| 189 | } |
| 190 | |
jrperritt | 29ae6b3 | 2016-04-13 12:59:37 -0500 | [diff] [blame] | 191 | return resp.StatusCode == 204, nil |
Ash Wilson | 46d913f | 2014-08-29 11:00:11 -0400 | [diff] [blame] | 192 | } |
| 193 | |
| 194 | // Revoke immediately makes specified token invalid. |
Jon Perritt | 2be387a | 2016-03-31 09:31:58 -0500 | [diff] [blame] | 195 | func Revoke(c *gophercloud.ServiceClient, token string) (r RevokeResult) { |
Jon Perritt | db0ae14 | 2016-03-13 00:33:41 -0600 | [diff] [blame] | 196 | _, r.Err = c.Delete(tokenURL(c), &gophercloud.RequestOpts{ |
Ash Wilson | 46d913f | 2014-08-29 11:00:11 -0400 | [diff] [blame] | 197 | MoreHeaders: subjectTokenHeaders(c, token), |
Ash Wilson | 46d913f | 2014-08-29 11:00:11 -0400 | [diff] [blame] | 198 | }) |
jrperritt | 29ae6b3 | 2016-04-13 12:59:37 -0500 | [diff] [blame] | 199 | return |
Ash Wilson | 46d913f | 2014-08-29 11:00:11 -0400 | [diff] [blame] | 200 | } |