blob: db0fba75a5027e29425597b7aff299411dd09e22 [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 Wilsoncde68122014-08-28 16:15:43 -040015 setup()
16 defer teardown()
17
18 client := gophercloud.ServiceClient{
19 Endpoint: endpoint(),
Ash Wilson417d9222014-08-29 07:58:35 -040020 Options: options,
Ash Wilson053fcb02014-08-29 08:04:35 -040021 TokenID: "12345abcdef",
Ash Wilsoncde68122014-08-28 16:15:43 -040022 }
23
24 mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
25 testhelper.TestMethod(t, r, "POST")
26 testhelper.TestHeader(t, r, "Content-Type", "application/json")
27 testhelper.TestHeader(t, r, "Accept", "application/json")
Ash Wilson417d9222014-08-29 07:58:35 -040028 testhelper.TestJSONRequest(t, r, requestJSON)
Ash Wilsoncde68122014-08-28 16:15:43 -040029
Ash Wilson4a52e2a2014-08-29 09:28:00 -040030 w.WriteHeader(http.StatusCreated)
Ash Wilsoncde68122014-08-28 16:15:43 -040031 fmt.Fprintf(w, `{}`)
32 })
33
Ash Wilson417d9222014-08-29 07:58:35 -040034 _, err := Create(&client, scope)
Ash Wilsoncde68122014-08-28 16:15:43 -040035 if err != nil {
36 t.Errorf("Create returned an error: %v", err)
37 }
38}
Ash Wilson417d9222014-08-29 07:58:35 -040039
Ash Wilsona8855ff2014-08-29 08:26:29 -040040func authTokenPostErr(t *testing.T, options gophercloud.AuthOptions, scope *Scope, includeToken bool, expectedErr error) {
41 setup()
42 defer teardown()
43
44 client := gophercloud.ServiceClient{
45 Endpoint: endpoint(),
46 Options: options,
47 }
48 if includeToken {
49 client.TokenID = "abcdef123456"
50 }
51
52 _, err := Create(&client, scope)
53 if err == nil {
54 t.Errorf("Create did NOT return an error")
55 }
56 if err != expectedErr {
57 t.Errorf("Create returned an unexpected error: wanted %v, got %v", expectedErr, err)
58 }
59}
60
Ash Wilson417d9222014-08-29 07:58:35 -040061func TestCreateUserIDAndPassword(t *testing.T) {
62 authTokenPost(t, gophercloud.AuthOptions{UserID: "me", Password: "squirrel!"}, nil, `
63 {
64 "auth": {
65 "identity": {
66 "methods": ["password"],
67 "password": {
68 "user": { "id": "me", "password": "squirrel!" }
69 }
70 }
71 }
72 }
73 `)
74}
75
76func TestCreateUsernameDomainIDPassword(t *testing.T) {
77 authTokenPost(t, gophercloud.AuthOptions{Username: "fakey", Password: "notpassword", DomainID: "abc123"}, nil, `
78 {
79 "auth": {
80 "identity": {
81 "methods": ["password"],
82 "password": {
83 "user": {
84 "domain": {
85 "id": "abc123"
86 },
87 "name": "fakey",
88 "password": "notpassword"
89 }
90 }
91 }
92 }
93 }
94 `)
95}
Ash Wilsond8da9e42014-08-29 08:01:06 -040096
97func TestCreateUsernameDomainNamePassword(t *testing.T) {
98 authTokenPost(t, gophercloud.AuthOptions{Username: "frank", Password: "swordfish", DomainName: "spork.net"}, nil, `
99 {
100 "auth": {
101 "identity": {
102 "methods": ["password"],
103 "password": {
104 "user": {
105 "domain": {
106 "name": "spork.net"
107 },
108 "name": "frank",
109 "password": "swordfish"
110 }
111 }
112 }
113 }
114 }
115 `)
116}
Ash Wilson053fcb02014-08-29 08:04:35 -0400117
118func TestCreateTokenID(t *testing.T) {
119 authTokenPost(t, gophercloud.AuthOptions{}, nil, `
120 {
121 "auth": {
122 "identity": {
123 "methods": ["token"],
124 "token": {
125 "id": "12345abcdef"
126 }
127 }
128 }
129 }
130 `)
131}
Ash Wilson1fde6162014-08-29 08:13:06 -0400132
133func TestCreateProjectIDScope(t *testing.T) {
134 options := gophercloud.AuthOptions{UserID: "fenris", Password: "g0t0h311"}
135 scope := &Scope{ProjectID: "123456"}
136 authTokenPost(t, options, scope, `
137 {
138 "auth": {
139 "identity": {
140 "methods": ["password"],
141 "password": {
142 "user": {
143 "id": "fenris",
144 "password": "g0t0h311"
145 }
146 }
147 },
148 "scope": {
149 "project": {
150 "id": "123456"
151 }
152 }
153 }
154 }
155 `)
156}
157
158func TestCreateDomainIDScope(t *testing.T) {
159 options := gophercloud.AuthOptions{UserID: "fenris", Password: "g0t0h311"}
160 scope := &Scope{DomainID: "1000"}
161 authTokenPost(t, options, scope, `
162 {
163 "auth": {
164 "identity": {
165 "methods": ["password"],
166 "password": {
167 "user": {
168 "id": "fenris",
169 "password": "g0t0h311"
170 }
171 }
172 },
173 "scope": {
174 "domain": {
175 "id": "1000"
176 }
177 }
178 }
179 }
180 `)
181}
182
183func TestCreateProjectNameAndDomainIDScope(t *testing.T) {
184 options := gophercloud.AuthOptions{UserID: "fenris", Password: "g0t0h311"}
185 scope := &Scope{ProjectName: "world-domination", DomainID: "1000"}
186 authTokenPost(t, options, scope, `
187 {
188 "auth": {
189 "identity": {
190 "methods": ["password"],
191 "password": {
192 "user": {
193 "id": "fenris",
194 "password": "g0t0h311"
195 }
196 }
197 },
198 "scope": {
199 "project": {
200 "domain": {
201 "id": "1000"
202 },
203 "name": "world-domination"
204 }
205 }
206 }
207 }
208 `)
209}
210
211func TestCreateProjectNameAndDomainNameScope(t *testing.T) {
212 options := gophercloud.AuthOptions{UserID: "fenris", Password: "g0t0h311"}
213 scope := &Scope{ProjectName: "world-domination", DomainName: "evil-plans"}
214 authTokenPost(t, options, scope, `
215 {
216 "auth": {
217 "identity": {
218 "methods": ["password"],
219 "password": {
220 "user": {
221 "id": "fenris",
222 "password": "g0t0h311"
223 }
224 }
225 },
226 "scope": {
227 "project": {
228 "domain": {
229 "name": "evil-plans"
230 },
231 "name": "world-domination"
232 }
233 }
234 }
235 }
236 `)
237}
Ash Wilsona8855ff2014-08-29 08:26:29 -0400238
Ash Wilson4a52e2a2014-08-29 09:28:00 -0400239func TestCreateExtractsTokenFromResponse(t *testing.T) {
240 setup()
241 defer teardown()
242
243 client := gophercloud.ServiceClient{
244 Endpoint: endpoint(),
245 Options: gophercloud.AuthOptions{UserID: "me", Password: "shhh"},
246 }
247
248 mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
249 w.Header().Add("X-Subject-Token", "aaa111")
250
251 w.WriteHeader(http.StatusCreated)
252 fmt.Fprintf(w, `{}`)
253 })
254
255 result, err := Create(&client, nil)
256 if err != nil {
257 t.Errorf("Create returned an error: %v", err)
258 }
259
260 token, _ := result.TokenID()
261 if token != "aaa111" {
262 t.Errorf("Expected token to be aaa111, but was %s", token)
263 }
264}
265
Ash Wilsona8855ff2014-08-29 08:26:29 -0400266func TestCreateFailureEmptyAuth(t *testing.T) {
267 authTokenPostErr(t, gophercloud.AuthOptions{}, nil, false, ErrMissingPassword)
268}
269
270func TestCreateFailureAPIKey(t *testing.T) {
271 authTokenPostErr(t, gophercloud.AuthOptions{APIKey: "something"}, nil, false, ErrAPIKeyProvided)
272}
273
274func TestCreateFailureTenantID(t *testing.T) {
275 authTokenPostErr(t, gophercloud.AuthOptions{TenantID: "something"}, nil, false, ErrTenantIDProvided)
276}
277
278func TestCreateFailureTenantName(t *testing.T) {
279 authTokenPostErr(t, gophercloud.AuthOptions{TenantName: "something"}, nil, false, ErrTenantNameProvided)
280}
281
282func TestCreateFailureTokenIDUsername(t *testing.T) {
283 authTokenPostErr(t, gophercloud.AuthOptions{Username: "something"}, nil, true, ErrUsernameWithToken)
284}
285
286func TestCreateFailureTokenIDUserID(t *testing.T) {
287 authTokenPostErr(t, gophercloud.AuthOptions{UserID: "something"}, nil, true, ErrUserIDWithToken)
288}
289
290func TestCreateFailureTokenIDDomainID(t *testing.T) {
291 authTokenPostErr(t, gophercloud.AuthOptions{DomainID: "something"}, nil, true, ErrDomainIDWithToken)
292}
293
294func TestCreateFailureTokenIDDomainName(t *testing.T) {
295 authTokenPostErr(t, gophercloud.AuthOptions{DomainName: "something"}, nil, true, ErrDomainNameWithToken)
296}
Ash Wilsonaed3db42014-08-29 08:59:56 -0400297
298func TestCreateFailureMissingUser(t *testing.T) {
299 options := gophercloud.AuthOptions{Password: "supersecure"}
300 authTokenPostErr(t, options, nil, false, ErrUsernameOrUserID)
301}
302
303func TestCreateFailureBothUser(t *testing.T) {
304 options := gophercloud.AuthOptions{
305 Password: "supersecure",
306 Username: "oops",
307 UserID: "redundancy",
308 }
309 authTokenPostErr(t, options, nil, false, ErrUsernameOrUserID)
310}
311
312func TestCreateFailureMissingDomain(t *testing.T) {
313 options := gophercloud.AuthOptions{
314 Password: "supersecure",
315 Username: "notuniqueenough",
316 }
317 authTokenPostErr(t, options, nil, false, ErrDomainIDOrDomainName)
318}
319
320func TestCreateFailureBothDomain(t *testing.T) {
321 options := gophercloud.AuthOptions{
322 Password: "supersecure",
323 Username: "someone",
324 DomainID: "hurf",
325 DomainName: "durf",
326 }
327 authTokenPostErr(t, options, nil, false, ErrDomainIDOrDomainName)
328}
329
330func TestCreateFailureUserIDDomainID(t *testing.T) {
331 options := gophercloud.AuthOptions{
332 UserID: "100",
333 Password: "stuff",
334 DomainID: "oops",
335 }
336 authTokenPostErr(t, options, nil, false, ErrDomainIDWithUserID)
337}
338
339func TestCreateFailureUserIDDomainName(t *testing.T) {
340 options := gophercloud.AuthOptions{
341 UserID: "100",
342 Password: "sssh",
343 DomainName: "oops",
344 }
345 authTokenPostErr(t, options, nil, false, ErrDomainNameWithUserID)
346}
347
348func TestCreateFailureScopeProjectNameAlone(t *testing.T) {
349 options := gophercloud.AuthOptions{UserID: "myself", Password: "swordfish"}
350 scope := &Scope{ProjectName: "notenough"}
351 authTokenPostErr(t, options, scope, false, ErrScopeDomainIDOrDomainName)
352}
353
354func TestCreateFailureScopeProjectNameAndID(t *testing.T) {
355 options := gophercloud.AuthOptions{UserID: "myself", Password: "swordfish"}
356 scope := &Scope{ProjectName: "whoops", ProjectID: "toomuch", DomainID: "1234"}
357 authTokenPostErr(t, options, scope, false, ErrScopeProjectIDOrProjectName)
358}
359
360func TestCreateFailureScopeProjectIDAndDomainID(t *testing.T) {
361 options := gophercloud.AuthOptions{UserID: "myself", Password: "swordfish"}
362 scope := &Scope{ProjectID: "toomuch", DomainID: "notneeded"}
363 authTokenPostErr(t, options, scope, false, ErrScopeProjectIDAlone)
364}
365
366func TestCreateFailureScopeProjectIDAndDomainNAme(t *testing.T) {
367 options := gophercloud.AuthOptions{UserID: "myself", Password: "swordfish"}
368 scope := &Scope{ProjectID: "toomuch", DomainName: "notneeded"}
369 authTokenPostErr(t, options, scope, false, ErrScopeProjectIDAlone)
370}
371
372func TestCreateFailureScopeDomainIDAndDomainName(t *testing.T) {
373 options := gophercloud.AuthOptions{UserID: "myself", Password: "swordfish"}
374 scope := &Scope{DomainID: "toomuch", DomainName: "notneeded"}
375 authTokenPostErr(t, options, scope, false, ErrScopeDomainIDOrDomainName)
376}
377
378func TestCreateFailureScopeDomainNameAlone(t *testing.T) {
379 options := gophercloud.AuthOptions{UserID: "myself", Password: "swordfish"}
380 scope := &Scope{DomainName: "notenough"}
381 authTokenPostErr(t, options, scope, false, ErrScopeDomainName)
382}
383
384func TestCreateFailureEmptyScope(t *testing.T) {
385 options := gophercloud.AuthOptions{UserID: "myself", Password: "swordfish"}
386 scope := &Scope{}
387 authTokenPostErr(t, options, scope, false, ErrScopeEmpty)
388}
Ash Wilson46d913f2014-08-29 11:00:11 -0400389
390func TestInfoRequest(t *testing.T) {
391 setup()
392 defer teardown()
393
394 client := gophercloud.ServiceClient{
395 Endpoint: endpoint(),
396 TokenID: "12345abcdef",
397 }
398
399 mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
400 testhelper.TestMethod(t, r, "GET")
401 testhelper.TestHeader(t, r, "Content-Type", "application/json")
402 testhelper.TestHeader(t, r, "Accept", "application/json")
403 testhelper.TestHeader(t, r, "X-Auth-Token", "12345abcdef")
404 testhelper.TestHeader(t, r, "X-Subject-Token", "abcdef12345")
405
406 w.WriteHeader(http.StatusOK)
407 fmt.Fprintf(w, `
408 { "token": { "expires_at": "2014-08-29T13:10:01.000000Z" } }
409 `)
410 })
411
412 result, err := Info(&client, "abcdef12345")
413 if err != nil {
414 t.Errorf("Info returned an error: %v", err)
415 }
416
417 expires, err := result.ExpiresAt()
418 if err != nil {
419 t.Errorf("Error extracting token expiration time: %v", err)
420 }
421
422 expected, _ := time.Parse(time.UnixDate, "Fri Aug 29 13:10:01 UTC 2014")
423 if expires != expected {
424 t.Errorf("Expected expiration time %s, but was %s", expected.Format(time.UnixDate), expires.Format(time.UnixDate))
425 }
426}
427
428func prepareAuthTokenHandler(t *testing.T, expectedMethod string, status int) gophercloud.ServiceClient {
429 client := gophercloud.ServiceClient{
430 Endpoint: endpoint(),
431 TokenID: "12345abcdef",
432 }
433
434 mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
435 testhelper.TestMethod(t, r, expectedMethod)
436 testhelper.TestHeader(t, r, "Content-Type", "application/json")
437 testhelper.TestHeader(t, r, "Accept", "application/json")
438 testhelper.TestHeader(t, r, "X-Auth-Token", "12345abcdef")
439 testhelper.TestHeader(t, r, "X-Subject-Token", "abcdef12345")
440
441 w.WriteHeader(status)
442 })
443
444 return client
445}
446
447func TestValidateRequestSuccessful(t *testing.T) {
448 setup()
449 defer teardown()
450 client := prepareAuthTokenHandler(t, "HEAD", http.StatusNoContent)
451
452 ok, err := Validate(&client, "abcdef12345")
453 if err != nil {
454 t.Errorf("Unexpected error from Validate: %v", err)
455 }
456
457 if !ok {
458 t.Errorf("Validate returned false for a valid token")
459 }
460}
461
462func TestValidateRequestFailure(t *testing.T) {
463 setup()
464 defer teardown()
465 client := prepareAuthTokenHandler(t, "HEAD", http.StatusNotFound)
466
467 ok, err := Validate(&client, "abcdef12345")
468 if err != nil {
469 t.Errorf("Unexpected error from Validate: %v", err)
470 }
471
472 if ok {
473 t.Errorf("Validate returned true for an invalid token")
474 }
475}
476
477func TestValidateRequestError(t *testing.T) {
478 setup()
479 defer teardown()
480 client := prepareAuthTokenHandler(t, "HEAD", http.StatusUnauthorized)
481
482 _, err := Validate(&client, "abcdef12345")
483 if err == nil {
484 t.Errorf("Missing expected error from Validate")
485 }
486}
487
488func TestRevokeRequestSuccessful(t *testing.T) {
489 setup()
490 defer teardown()
491 client := prepareAuthTokenHandler(t, "DELETE", http.StatusNoContent)
492
493 err := Revoke(&client, "abcdef12345")
494 if err != nil {
495 t.Errorf("Unexpected error from Revoke: %v", err)
496 }
497}
498
499func TestRevokeRequestError(t *testing.T) {
500 setup()
501 defer teardown()
502 client := prepareAuthTokenHandler(t, "DELETE", http.StatusNotFound)
503
504 err := Revoke(&client, "abcdef12345")
505 if err == nil {
506 t.Errorf("Missing expected error from Revoke")
507 }
508}