blob: ba4363b2b928a50ec57c4f612d1001a1db7b1f5f [file] [log] [blame]
Ash Wilson85d82652014-08-28 13:57:46 -04001package tokens
2
jrperritt29ae6b32016-04-13 12:59:37 -05003import "github.com/gophercloud/gophercloud"
Ash Wilson2491b4c2015-02-12 16:13:39 -05004
jrperritt29ae6b32016-04-13 12:59:37 -05005// Scope allows a created token to be limited to a specific domain or project.
6type 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 Wilson85d82652014-08-28 13:57:46 -040012
Jon Perrittdb0ae142016-03-13 00:33:41 -060013// AuthOptionsBuilder describes any argument that may be passed to the Create call.
14type AuthOptionsBuilder interface {
15 // ToTokenV3CreateMap assembles the Create request body, returning an error if parameters are
16 // missing or inconsistent.
jrperritt0bc55782016-07-27 13:50:14 -050017 ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error)
18 ToTokenV3ScopeMap() (map[string]interface{}, error)
19 CanReauth() bool
jrperritt29ae6b32016-04-13 12:59:37 -050020}
21
22type 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
jrperritt29ae6b32016-04-13 12:59:37 -050042 // 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.
jrperritt0bc55782016-07-27 13:50:14 -050051 TokenID string `json:"-"`
52
53 Scope Scope `json:"-"`
jrperritt29ae6b32016-04-13 12:59:37 -050054}
55
jrperritt0bc55782016-07-27 13:50:14 -050056func (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,
jrperritt29ae6b32016-04-13 12:59:37 -050065 }
66
jrperritt0bc55782016-07-27 13:50:14 -050067 return gophercloudAuthOpts.ToTokenV3CreateMap(scope)
68}
jrperritt29ae6b32016-04-13 12:59:37 -050069
jrperritt0bc55782016-07-27 13:50:14 -050070func (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{}
jrperritt29ae6b32016-04-13 12:59:37 -050076 }
jrperritt0bc55782016-07-27 13:50:14 -050077 if opts.Scope.ProjectID != "" {
78 return nil, gophercloud.ErrScopeProjectIDOrProjectName{}
jrperritt29ae6b32016-04-13 12:59:37 -050079 }
80
jrperritt0bc55782016-07-27 13:50:14 -050081 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
jrperritt29ae6b32016-04-13 12:59:37 -050089 }
90
jrperritt0bc55782016-07-27 13:50:14 -050091 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
jrperritt29ae6b32016-04-13 12:59:37 -050099 }
jrperritt0bc55782016-07-27 13:50:14 -0500100 } 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{}
jrperritt29ae6b32016-04-13 12:59:37 -0500104 }
jrperritt0bc55782016-07-27 13:50:14 -0500105 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{}
jrperritt29ae6b32016-04-13 12:59:37 -0500129 }
130
jrperritt0bc55782016-07-27 13:50:14 -0500131 return nil, nil
132}
133
134func (opts *AuthOptions) CanReauth() bool {
135 return opts.AllowReauth
Ash Wilson85d82652014-08-28 13:57:46 -0400136}
137
Ash Wilson6425a412014-08-29 12:30:35 -0400138func subjectTokenHeaders(c *gophercloud.ServiceClient, subjectToken string) map[string]string {
Krzysztof Kwapisiewiczbaaaf3e2016-02-03 15:18:16 +0100139 return map[string]string{
140 "X-Subject-Token": subjectToken,
141 }
Ash Wilson46d913f2014-08-29 11:00:11 -0400142}
143
Ash Wilsone5550862014-08-28 15:37:09 -0400144// Create authenticates and either generates a new token, or changes the Scope of an existing token.
jrperritt0bc55782016-07-27 13:50:14 -0500145func Create(c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResult) {
146 scope, err := opts.ToTokenV3ScopeMap()
Jon Perrittdb0ae142016-03-13 00:33:41 -0600147 if err != nil {
148 r.Err = err
Jon Perritt2be387a2016-03-31 09:31:58 -0500149 return
Ash Wilson85d82652014-08-28 13:57:46 -0400150 }
jrperritt0bc55782016-07-27 13:50:14 -0500151
152 b, err := opts.ToTokenV3CreateMap(scope)
153 if err != nil {
154 r.Err = err
155 return
156 }
157
jrperritt9b7b9e62016-07-11 22:30:50 -0500158 resp, err := c.Post(tokenURL(c), b, &r.Body, &gophercloud.RequestOpts{
159 MoreHeaders: map[string]string{"X-Auth-Token": ""},
160 })
Eugene Yakubovich91527212016-09-30 10:17:32 -0700161 r.Err = err
Jon Perrittdb0ae142016-03-13 00:33:41 -0600162 if resp != nil {
163 r.Header = resp.Header
Ash Wilson85d82652014-08-28 13:57:46 -0400164 }
jrperritt29ae6b32016-04-13 12:59:37 -0500165 return
Ash Wilson85d82652014-08-28 13:57:46 -0400166}
Ash Wilson46d913f2014-08-29 11:00:11 -0400167
Ash Wilson5266e492014-09-09 15:44:30 -0400168// Get validates and retrieves information about another token.
Jon Perritt2be387a2016-03-31 09:31:58 -0500169func Get(c *gophercloud.ServiceClient, token string) (r GetResult) {
jrperritt29ae6b32016-04-13 12:59:37 -0500170 resp, err := c.Get(tokenURL(c), &r.Body, &gophercloud.RequestOpts{
Jamie Hannaford562a7d52015-03-24 16:20:16 +0100171 MoreHeaders: subjectTokenHeaders(c, token),
172 OkCodes: []int{200, 203},
Ash Wilson46d913f2014-08-29 11:00:11 -0400173 })
Jon Perrittdb0ae142016-03-13 00:33:41 -0600174 if resp != nil {
jrperritt29ae6b32016-04-13 12:59:37 -0500175 r.Err = err
Jon Perrittdb0ae142016-03-13 00:33:41 -0600176 r.Header = resp.Header
Ash Wilson46d913f2014-08-29 11:00:11 -0400177 }
jrperritt29ae6b32016-04-13 12:59:37 -0500178 return
Ash Wilson46d913f2014-08-29 11:00:11 -0400179}
180
181// Validate determines if a specified token is valid or not.
Ash Wilson6425a412014-08-29 12:30:35 -0400182func Validate(c *gophercloud.ServiceClient, token string) (bool, error) {
jrperritt29ae6b32016-04-13 12:59:37 -0500183 resp, err := c.Request("HEAD", tokenURL(c), &gophercloud.RequestOpts{
Ash Wilson46d913f2014-08-29 11:00:11 -0400184 MoreHeaders: subjectTokenHeaders(c, token),
185 OkCodes: []int{204, 404},
186 })
187 if err != nil {
188 return false, err
189 }
190
jrperritt29ae6b32016-04-13 12:59:37 -0500191 return resp.StatusCode == 204, nil
Ash Wilson46d913f2014-08-29 11:00:11 -0400192}
193
194// Revoke immediately makes specified token invalid.
Jon Perritt2be387a2016-03-31 09:31:58 -0500195func Revoke(c *gophercloud.ServiceClient, token string) (r RevokeResult) {
Jon Perrittdb0ae142016-03-13 00:33:41 -0600196 _, r.Err = c.Delete(tokenURL(c), &gophercloud.RequestOpts{
Ash Wilson46d913f2014-08-29 11:00:11 -0400197 MoreHeaders: subjectTokenHeaders(c, token),
Ash Wilson46d913f2014-08-29 11:00:11 -0400198 })
jrperritt29ae6b32016-04-13 12:59:37 -0500199 return
Ash Wilson46d913f2014-08-29 11:00:11 -0400200}