blob: 97690a62388a4966a8544916b6713df2fa697c24 [file] [log] [blame]
Ash Wilsoncde68122014-08-28 16:15:43 -04001package tokens
2
3import (
4 "fmt"
5 "net/http"
6 "testing"
Ash Wilson46d913f2014-08-29 11:00:11 -04007 "time"
Ash Wilsoncde68122014-08-28 16:15:43 -04008
9 "github.com/rackspace/gophercloud"
10 "github.com/rackspace/gophercloud/testhelper"
11)
12
Ash Wilson417d9222014-08-29 07:58:35 -040013// authTokenPost verifies that providing certain AuthOptions and Scope results in an expected JSON structure.
14func authTokenPost(t *testing.T, options gophercloud.AuthOptions, scope *Scope, requestJSON string) {
Ash Wilson0ab4d612014-08-29 11:10:13 -040015 testhelper.SetupHTTP()
16 defer testhelper.TeardownHTTP()
Ash Wilsoncde68122014-08-28 16:15:43 -040017
Ash Wilson6425a412014-08-29 12:30:35 -040018 client := gophercloud.ServiceClient{
Ash Wilsona87ee062014-09-03 11:26:06 -040019 Provider: &gophercloud.ProviderClient{
Ash Wilson49f0f562014-09-03 08:58:20 -040020 TokenID: "12345abcdef",
Ash Wilson6425a412014-08-29 12:30:35 -040021 },
Ash Wilson0ab4d612014-08-29 11:10:13 -040022 Endpoint: testhelper.Endpoint(),
Ash Wilsoncde68122014-08-28 16:15:43 -040023 }
24
Ash Wilson0ab4d612014-08-29 11:10:13 -040025 testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
Ash Wilsoncde68122014-08-28 16:15:43 -040026 testhelper.TestMethod(t, r, "POST")
27 testhelper.TestHeader(t, r, "Content-Type", "application/json")
28 testhelper.TestHeader(t, r, "Accept", "application/json")
Ash Wilson417d9222014-08-29 07:58:35 -040029 testhelper.TestJSONRequest(t, r, requestJSON)
Ash Wilsoncde68122014-08-28 16:15:43 -040030
Ash Wilson4a52e2a2014-08-29 09:28:00 -040031 w.WriteHeader(http.StatusCreated)
Ash Wilsoncde68122014-08-28 16:15:43 -040032 fmt.Fprintf(w, `{}`)
33 })
34
Ash Wilsona87ee062014-09-03 11:26:06 -040035 _, err := Create(&client, options, scope)
Ash Wilsoncde68122014-08-28 16:15:43 -040036 if err != nil {
37 t.Errorf("Create returned an error: %v", err)
38 }
39}
Ash Wilson417d9222014-08-29 07:58:35 -040040
Ash Wilsona8855ff2014-08-29 08:26:29 -040041func authTokenPostErr(t *testing.T, options gophercloud.AuthOptions, scope *Scope, includeToken bool, expectedErr error) {
Ash Wilson0ab4d612014-08-29 11:10:13 -040042 testhelper.SetupHTTP()
43 defer testhelper.TeardownHTTP()
Ash Wilsona8855ff2014-08-29 08:26:29 -040044
Ash Wilson6425a412014-08-29 12:30:35 -040045 client := gophercloud.ServiceClient{
Ash Wilsona87ee062014-09-03 11:26:06 -040046 Provider: &gophercloud.ProviderClient{},
Ash Wilson0ab4d612014-08-29 11:10:13 -040047 Endpoint: testhelper.Endpoint(),
Ash Wilsona8855ff2014-08-29 08:26:29 -040048 }
49 if includeToken {
Ash Wilsona87ee062014-09-03 11:26:06 -040050 client.Provider.TokenID = "abcdef123456"
Ash Wilsona8855ff2014-08-29 08:26:29 -040051 }
52
Ash Wilsona87ee062014-09-03 11:26:06 -040053 _, err := Create(&client, options, scope)
Ash Wilsona8855ff2014-08-29 08:26:29 -040054 if err == nil {
55 t.Errorf("Create did NOT return an error")
56 }
57 if err != expectedErr {
58 t.Errorf("Create returned an unexpected error: wanted %v, got %v", expectedErr, err)
59 }
60}
61
Ash Wilson417d9222014-08-29 07:58:35 -040062func TestCreateUserIDAndPassword(t *testing.T) {
63 authTokenPost(t, gophercloud.AuthOptions{UserID: "me", Password: "squirrel!"}, nil, `
64 {
65 "auth": {
66 "identity": {
67 "methods": ["password"],
68 "password": {
69 "user": { "id": "me", "password": "squirrel!" }
70 }
71 }
72 }
73 }
74 `)
75}
76
77func TestCreateUsernameDomainIDPassword(t *testing.T) {
78 authTokenPost(t, gophercloud.AuthOptions{Username: "fakey", Password: "notpassword", DomainID: "abc123"}, nil, `
79 {
80 "auth": {
81 "identity": {
82 "methods": ["password"],
83 "password": {
84 "user": {
85 "domain": {
86 "id": "abc123"
87 },
88 "name": "fakey",
89 "password": "notpassword"
90 }
91 }
92 }
93 }
94 }
95 `)
96}
Ash Wilsond8da9e42014-08-29 08:01:06 -040097
98func TestCreateUsernameDomainNamePassword(t *testing.T) {
99 authTokenPost(t, gophercloud.AuthOptions{Username: "frank", Password: "swordfish", DomainName: "spork.net"}, nil, `
100 {
101 "auth": {
102 "identity": {
103 "methods": ["password"],
104 "password": {
105 "user": {
106 "domain": {
107 "name": "spork.net"
108 },
109 "name": "frank",
110 "password": "swordfish"
111 }
112 }
113 }
114 }
115 }
116 `)
117}
Ash Wilson053fcb02014-08-29 08:04:35 -0400118
119func TestCreateTokenID(t *testing.T) {
120 authTokenPost(t, gophercloud.AuthOptions{}, nil, `
121 {
122 "auth": {
123 "identity": {
124 "methods": ["token"],
125 "token": {
126 "id": "12345abcdef"
127 }
128 }
129 }
130 }
131 `)
132}
Ash Wilson1fde6162014-08-29 08:13:06 -0400133
134func TestCreateProjectIDScope(t *testing.T) {
135 options := gophercloud.AuthOptions{UserID: "fenris", Password: "g0t0h311"}
136 scope := &Scope{ProjectID: "123456"}
137 authTokenPost(t, options, scope, `
138 {
139 "auth": {
140 "identity": {
141 "methods": ["password"],
142 "password": {
143 "user": {
144 "id": "fenris",
145 "password": "g0t0h311"
146 }
147 }
148 },
149 "scope": {
150 "project": {
151 "id": "123456"
152 }
153 }
154 }
155 }
156 `)
157}
158
159func TestCreateDomainIDScope(t *testing.T) {
160 options := gophercloud.AuthOptions{UserID: "fenris", Password: "g0t0h311"}
161 scope := &Scope{DomainID: "1000"}
162 authTokenPost(t, options, scope, `
163 {
164 "auth": {
165 "identity": {
166 "methods": ["password"],
167 "password": {
168 "user": {
169 "id": "fenris",
170 "password": "g0t0h311"
171 }
172 }
173 },
174 "scope": {
175 "domain": {
176 "id": "1000"
177 }
178 }
179 }
180 }
181 `)
182}
183
184func TestCreateProjectNameAndDomainIDScope(t *testing.T) {
185 options := gophercloud.AuthOptions{UserID: "fenris", Password: "g0t0h311"}
186 scope := &Scope{ProjectName: "world-domination", DomainID: "1000"}
187 authTokenPost(t, options, scope, `
188 {
189 "auth": {
190 "identity": {
191 "methods": ["password"],
192 "password": {
193 "user": {
194 "id": "fenris",
195 "password": "g0t0h311"
196 }
197 }
198 },
199 "scope": {
200 "project": {
201 "domain": {
202 "id": "1000"
203 },
204 "name": "world-domination"
205 }
206 }
207 }
208 }
209 `)
210}
211
212func TestCreateProjectNameAndDomainNameScope(t *testing.T) {
213 options := gophercloud.AuthOptions{UserID: "fenris", Password: "g0t0h311"}
214 scope := &Scope{ProjectName: "world-domination", DomainName: "evil-plans"}
215 authTokenPost(t, options, scope, `
216 {
217 "auth": {
218 "identity": {
219 "methods": ["password"],
220 "password": {
221 "user": {
222 "id": "fenris",
223 "password": "g0t0h311"
224 }
225 }
226 },
227 "scope": {
228 "project": {
229 "domain": {
230 "name": "evil-plans"
231 },
232 "name": "world-domination"
233 }
234 }
235 }
236 }
237 `)
238}
Ash Wilsona8855ff2014-08-29 08:26:29 -0400239
Ash Wilson4a52e2a2014-08-29 09:28:00 -0400240func TestCreateExtractsTokenFromResponse(t *testing.T) {
Ash Wilson0ab4d612014-08-29 11:10:13 -0400241 testhelper.SetupHTTP()
242 defer testhelper.TeardownHTTP()
Ash Wilson4a52e2a2014-08-29 09:28:00 -0400243
Ash Wilson6425a412014-08-29 12:30:35 -0400244 client := gophercloud.ServiceClient{
Ash Wilsona87ee062014-09-03 11:26:06 -0400245 Provider: &gophercloud.ProviderClient{},
Ash Wilson0ab4d612014-08-29 11:10:13 -0400246 Endpoint: testhelper.Endpoint(),
Ash Wilson4a52e2a2014-08-29 09:28:00 -0400247 }
248
Ash Wilson0ab4d612014-08-29 11:10:13 -0400249 testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
Ash Wilson4a52e2a2014-08-29 09:28:00 -0400250 w.Header().Add("X-Subject-Token", "aaa111")
251
252 w.WriteHeader(http.StatusCreated)
253 fmt.Fprintf(w, `{}`)
254 })
255
Ash Wilsona87ee062014-09-03 11:26:06 -0400256 options := gophercloud.AuthOptions{UserID: "me", Password: "shhh"}
257 result, err := Create(&client, options, nil)
Ash Wilson4a52e2a2014-08-29 09:28:00 -0400258 if err != nil {
259 t.Errorf("Create returned an error: %v", err)
260 }
261
262 token, _ := result.TokenID()
263 if token != "aaa111" {
264 t.Errorf("Expected token to be aaa111, but was %s", token)
265 }
266}
267
Ash Wilsona8855ff2014-08-29 08:26:29 -0400268func TestCreateFailureEmptyAuth(t *testing.T) {
269 authTokenPostErr(t, gophercloud.AuthOptions{}, nil, false, ErrMissingPassword)
270}
271
272func TestCreateFailureAPIKey(t *testing.T) {
273 authTokenPostErr(t, gophercloud.AuthOptions{APIKey: "something"}, nil, false, ErrAPIKeyProvided)
274}
275
276func TestCreateFailureTenantID(t *testing.T) {
277 authTokenPostErr(t, gophercloud.AuthOptions{TenantID: "something"}, nil, false, ErrTenantIDProvided)
278}
279
280func TestCreateFailureTenantName(t *testing.T) {
281 authTokenPostErr(t, gophercloud.AuthOptions{TenantName: "something"}, nil, false, ErrTenantNameProvided)
282}
283
284func TestCreateFailureTokenIDUsername(t *testing.T) {
285 authTokenPostErr(t, gophercloud.AuthOptions{Username: "something"}, nil, true, ErrUsernameWithToken)
286}
287
288func TestCreateFailureTokenIDUserID(t *testing.T) {
289 authTokenPostErr(t, gophercloud.AuthOptions{UserID: "something"}, nil, true, ErrUserIDWithToken)
290}
291
292func TestCreateFailureTokenIDDomainID(t *testing.T) {
293 authTokenPostErr(t, gophercloud.AuthOptions{DomainID: "something"}, nil, true, ErrDomainIDWithToken)
294}
295
296func TestCreateFailureTokenIDDomainName(t *testing.T) {
297 authTokenPostErr(t, gophercloud.AuthOptions{DomainName: "something"}, nil, true, ErrDomainNameWithToken)
298}
Ash Wilsonaed3db42014-08-29 08:59:56 -0400299
300func TestCreateFailureMissingUser(t *testing.T) {
301 options := gophercloud.AuthOptions{Password: "supersecure"}
302 authTokenPostErr(t, options, nil, false, ErrUsernameOrUserID)
303}
304
305func TestCreateFailureBothUser(t *testing.T) {
306 options := gophercloud.AuthOptions{
307 Password: "supersecure",
308 Username: "oops",
309 UserID: "redundancy",
310 }
311 authTokenPostErr(t, options, nil, false, ErrUsernameOrUserID)
312}
313
314func TestCreateFailureMissingDomain(t *testing.T) {
315 options := gophercloud.AuthOptions{
316 Password: "supersecure",
317 Username: "notuniqueenough",
318 }
319 authTokenPostErr(t, options, nil, false, ErrDomainIDOrDomainName)
320}
321
322func TestCreateFailureBothDomain(t *testing.T) {
323 options := gophercloud.AuthOptions{
324 Password: "supersecure",
325 Username: "someone",
326 DomainID: "hurf",
327 DomainName: "durf",
328 }
329 authTokenPostErr(t, options, nil, false, ErrDomainIDOrDomainName)
330}
331
332func TestCreateFailureUserIDDomainID(t *testing.T) {
333 options := gophercloud.AuthOptions{
334 UserID: "100",
335 Password: "stuff",
336 DomainID: "oops",
337 }
338 authTokenPostErr(t, options, nil, false, ErrDomainIDWithUserID)
339}
340
341func TestCreateFailureUserIDDomainName(t *testing.T) {
342 options := gophercloud.AuthOptions{
343 UserID: "100",
344 Password: "sssh",
345 DomainName: "oops",
346 }
347 authTokenPostErr(t, options, nil, false, ErrDomainNameWithUserID)
348}
349
350func TestCreateFailureScopeProjectNameAlone(t *testing.T) {
351 options := gophercloud.AuthOptions{UserID: "myself", Password: "swordfish"}
352 scope := &Scope{ProjectName: "notenough"}
353 authTokenPostErr(t, options, scope, false, ErrScopeDomainIDOrDomainName)
354}
355
356func TestCreateFailureScopeProjectNameAndID(t *testing.T) {
357 options := gophercloud.AuthOptions{UserID: "myself", Password: "swordfish"}
358 scope := &Scope{ProjectName: "whoops", ProjectID: "toomuch", DomainID: "1234"}
359 authTokenPostErr(t, options, scope, false, ErrScopeProjectIDOrProjectName)
360}
361
362func TestCreateFailureScopeProjectIDAndDomainID(t *testing.T) {
363 options := gophercloud.AuthOptions{UserID: "myself", Password: "swordfish"}
364 scope := &Scope{ProjectID: "toomuch", DomainID: "notneeded"}
365 authTokenPostErr(t, options, scope, false, ErrScopeProjectIDAlone)
366}
367
368func TestCreateFailureScopeProjectIDAndDomainNAme(t *testing.T) {
369 options := gophercloud.AuthOptions{UserID: "myself", Password: "swordfish"}
370 scope := &Scope{ProjectID: "toomuch", DomainName: "notneeded"}
371 authTokenPostErr(t, options, scope, false, ErrScopeProjectIDAlone)
372}
373
374func TestCreateFailureScopeDomainIDAndDomainName(t *testing.T) {
375 options := gophercloud.AuthOptions{UserID: "myself", Password: "swordfish"}
376 scope := &Scope{DomainID: "toomuch", DomainName: "notneeded"}
377 authTokenPostErr(t, options, scope, false, ErrScopeDomainIDOrDomainName)
378}
379
380func TestCreateFailureScopeDomainNameAlone(t *testing.T) {
381 options := gophercloud.AuthOptions{UserID: "myself", Password: "swordfish"}
382 scope := &Scope{DomainName: "notenough"}
383 authTokenPostErr(t, options, scope, false, ErrScopeDomainName)
384}
385
386func TestCreateFailureEmptyScope(t *testing.T) {
387 options := gophercloud.AuthOptions{UserID: "myself", Password: "swordfish"}
388 scope := &Scope{}
389 authTokenPostErr(t, options, scope, false, ErrScopeEmpty)
390}
Ash Wilson46d913f2014-08-29 11:00:11 -0400391
392func TestInfoRequest(t *testing.T) {
Ash Wilson0ab4d612014-08-29 11:10:13 -0400393 testhelper.SetupHTTP()
394 defer testhelper.TeardownHTTP()
Ash Wilson46d913f2014-08-29 11:00:11 -0400395
Ash Wilson6425a412014-08-29 12:30:35 -0400396 client := gophercloud.ServiceClient{
Ash Wilsona87ee062014-09-03 11:26:06 -0400397 Provider: &gophercloud.ProviderClient{
Ash Wilson6425a412014-08-29 12:30:35 -0400398 TokenID: "12345abcdef",
399 },
Ash Wilson0ab4d612014-08-29 11:10:13 -0400400 Endpoint: testhelper.Endpoint(),
Ash Wilson46d913f2014-08-29 11:00:11 -0400401 }
402
Ash Wilson0ab4d612014-08-29 11:10:13 -0400403 testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
Ash Wilson46d913f2014-08-29 11:00:11 -0400404 testhelper.TestMethod(t, r, "GET")
405 testhelper.TestHeader(t, r, "Content-Type", "application/json")
406 testhelper.TestHeader(t, r, "Accept", "application/json")
407 testhelper.TestHeader(t, r, "X-Auth-Token", "12345abcdef")
408 testhelper.TestHeader(t, r, "X-Subject-Token", "abcdef12345")
409
410 w.WriteHeader(http.StatusOK)
411 fmt.Fprintf(w, `
412 { "token": { "expires_at": "2014-08-29T13:10:01.000000Z" } }
413 `)
414 })
415
416 result, err := Info(&client, "abcdef12345")
417 if err != nil {
418 t.Errorf("Info returned an error: %v", err)
419 }
420
421 expires, err := result.ExpiresAt()
422 if err != nil {
423 t.Errorf("Error extracting token expiration time: %v", err)
424 }
425
426 expected, _ := time.Parse(time.UnixDate, "Fri Aug 29 13:10:01 UTC 2014")
427 if expires != expected {
428 t.Errorf("Expected expiration time %s, but was %s", expected.Format(time.UnixDate), expires.Format(time.UnixDate))
429 }
430}
431
Ash Wilson6425a412014-08-29 12:30:35 -0400432func prepareAuthTokenHandler(t *testing.T, expectedMethod string, status int) gophercloud.ServiceClient {
433 client := gophercloud.ServiceClient{
Ash Wilsona87ee062014-09-03 11:26:06 -0400434 Provider: &gophercloud.ProviderClient{
Ash Wilson6425a412014-08-29 12:30:35 -0400435 TokenID: "12345abcdef",
436 },
Ash Wilson0ab4d612014-08-29 11:10:13 -0400437 Endpoint: testhelper.Endpoint(),
Ash Wilson46d913f2014-08-29 11:00:11 -0400438 }
439
Ash Wilson0ab4d612014-08-29 11:10:13 -0400440 testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
Ash Wilson46d913f2014-08-29 11:00:11 -0400441 testhelper.TestMethod(t, r, expectedMethod)
442 testhelper.TestHeader(t, r, "Content-Type", "application/json")
443 testhelper.TestHeader(t, r, "Accept", "application/json")
444 testhelper.TestHeader(t, r, "X-Auth-Token", "12345abcdef")
445 testhelper.TestHeader(t, r, "X-Subject-Token", "abcdef12345")
446
447 w.WriteHeader(status)
448 })
449
450 return client
451}
452
453func TestValidateRequestSuccessful(t *testing.T) {
Ash Wilson0ab4d612014-08-29 11:10:13 -0400454 testhelper.SetupHTTP()
455 defer testhelper.TeardownHTTP()
Ash Wilson46d913f2014-08-29 11:00:11 -0400456 client := prepareAuthTokenHandler(t, "HEAD", http.StatusNoContent)
457
458 ok, err := Validate(&client, "abcdef12345")
459 if err != nil {
460 t.Errorf("Unexpected error from Validate: %v", err)
461 }
462
463 if !ok {
464 t.Errorf("Validate returned false for a valid token")
465 }
466}
467
468func TestValidateRequestFailure(t *testing.T) {
Ash Wilson0ab4d612014-08-29 11:10:13 -0400469 testhelper.SetupHTTP()
470 defer testhelper.TeardownHTTP()
Ash Wilson46d913f2014-08-29 11:00:11 -0400471 client := prepareAuthTokenHandler(t, "HEAD", http.StatusNotFound)
472
473 ok, err := Validate(&client, "abcdef12345")
474 if err != nil {
475 t.Errorf("Unexpected error from Validate: %v", err)
476 }
477
478 if ok {
479 t.Errorf("Validate returned true for an invalid token")
480 }
481}
482
483func TestValidateRequestError(t *testing.T) {
Ash Wilson0ab4d612014-08-29 11:10:13 -0400484 testhelper.SetupHTTP()
485 defer testhelper.TeardownHTTP()
Ash Wilson46d913f2014-08-29 11:00:11 -0400486 client := prepareAuthTokenHandler(t, "HEAD", http.StatusUnauthorized)
487
488 _, err := Validate(&client, "abcdef12345")
489 if err == nil {
490 t.Errorf("Missing expected error from Validate")
491 }
492}
493
494func TestRevokeRequestSuccessful(t *testing.T) {
Ash Wilson0ab4d612014-08-29 11:10:13 -0400495 testhelper.SetupHTTP()
496 defer testhelper.TeardownHTTP()
Ash Wilson46d913f2014-08-29 11:00:11 -0400497 client := prepareAuthTokenHandler(t, "DELETE", http.StatusNoContent)
498
499 err := Revoke(&client, "abcdef12345")
500 if err != nil {
501 t.Errorf("Unexpected error from Revoke: %v", err)
502 }
503}
504
505func TestRevokeRequestError(t *testing.T) {
Ash Wilson0ab4d612014-08-29 11:10:13 -0400506 testhelper.SetupHTTP()
507 defer testhelper.TeardownHTTP()
Ash Wilson46d913f2014-08-29 11:00:11 -0400508 client := prepareAuthTokenHandler(t, "DELETE", http.StatusNotFound)
509
510 err := Revoke(&client, "abcdef12345")
511 if err == nil {
512 t.Errorf("Missing expected error from Revoke")
513 }
514}