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