github.com/gophercloud/gophercloud@v1.11.0/openstack/identity/v3/tokens/testing/requests_test.go (about) 1 package testing 2 3 import ( 4 "fmt" 5 "net/http" 6 "testing" 7 "time" 8 9 "github.com/gophercloud/gophercloud" 10 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" 11 "github.com/gophercloud/gophercloud/testhelper" 12 ) 13 14 // authTokenPost verifies that providing certain AuthOptions and Scope results in an expected JSON structure. 15 func authTokenPost(t *testing.T, options tokens.AuthOptions, scope *tokens.Scope, requestJSON string) { 16 testhelper.SetupHTTP() 17 defer testhelper.TeardownHTTP() 18 19 client := gophercloud.ServiceClient{ 20 ProviderClient: &gophercloud.ProviderClient{}, 21 Endpoint: testhelper.Endpoint(), 22 } 23 24 testhelper.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") 28 testhelper.TestJSONRequest(t, r, requestJSON) 29 30 w.WriteHeader(http.StatusCreated) 31 fmt.Fprintf(w, `{ 32 "token": { 33 "expires_at": "2014-10-02T13:45:00.000000Z" 34 } 35 }`) 36 }) 37 38 if scope != nil { 39 options.Scope = *scope 40 } 41 42 expected := &tokens.Token{ 43 ExpiresAt: time.Date(2014, 10, 2, 13, 45, 0, 0, time.UTC), 44 } 45 actual, err := tokens.Create(&client, &options).Extract() 46 testhelper.AssertNoErr(t, err) 47 testhelper.CheckDeepEquals(t, expected, actual) 48 } 49 50 func authTokenPostErr(t *testing.T, options tokens.AuthOptions, scope *tokens.Scope, includeToken bool, expectedErr error) { 51 testhelper.SetupHTTP() 52 defer testhelper.TeardownHTTP() 53 54 client := gophercloud.ServiceClient{ 55 ProviderClient: &gophercloud.ProviderClient{}, 56 Endpoint: testhelper.Endpoint(), 57 } 58 if includeToken { 59 client.TokenID = "abcdef123456" 60 } 61 62 if scope != nil { 63 options.Scope = *scope 64 } 65 66 _, err := tokens.Create(&client, &options).Extract() 67 if err == nil { 68 t.Errorf("Create did NOT return an error") 69 } 70 if err != expectedErr { 71 t.Errorf("Create returned an unexpected error: wanted %v, got %v", expectedErr, err) 72 } 73 } 74 75 func TestCreateUserIDAndPassword(t *testing.T) { 76 authTokenPost(t, tokens.AuthOptions{UserID: "me", Password: "squirrel!"}, nil, ` 77 { 78 "auth": { 79 "identity": { 80 "methods": ["password"], 81 "password": { 82 "user": { "id": "me", "password": "squirrel!" } 83 } 84 } 85 } 86 } 87 `) 88 } 89 90 func TestCreateUsernameDomainIDPassword(t *testing.T) { 91 authTokenPost(t, tokens.AuthOptions{Username: "fakey", Password: "notpassword", DomainID: "abc123"}, nil, ` 92 { 93 "auth": { 94 "identity": { 95 "methods": ["password"], 96 "password": { 97 "user": { 98 "domain": { 99 "id": "abc123" 100 }, 101 "name": "fakey", 102 "password": "notpassword" 103 } 104 } 105 } 106 } 107 } 108 `) 109 } 110 111 func TestCreateUsernameDomainNamePassword(t *testing.T) { 112 authTokenPost(t, tokens.AuthOptions{Username: "frank", Password: "swordfish", DomainName: "spork.net"}, nil, ` 113 { 114 "auth": { 115 "identity": { 116 "methods": ["password"], 117 "password": { 118 "user": { 119 "domain": { 120 "name": "spork.net" 121 }, 122 "name": "frank", 123 "password": "swordfish" 124 } 125 } 126 } 127 } 128 } 129 `) 130 } 131 132 func TestCreateTokenID(t *testing.T) { 133 authTokenPost(t, tokens.AuthOptions{TokenID: "12345abcdef"}, nil, ` 134 { 135 "auth": { 136 "identity": { 137 "methods": ["token"], 138 "token": { 139 "id": "12345abcdef" 140 } 141 } 142 } 143 } 144 `) 145 } 146 147 func TestCreateProjectIDScope(t *testing.T) { 148 options := tokens.AuthOptions{UserID: "someuser", Password: "somepassword"} 149 scope := &tokens.Scope{ProjectID: "123456"} 150 authTokenPost(t, options, scope, ` 151 { 152 "auth": { 153 "identity": { 154 "methods": ["password"], 155 "password": { 156 "user": { 157 "id": "someuser", 158 "password": "somepassword" 159 } 160 } 161 }, 162 "scope": { 163 "project": { 164 "id": "123456" 165 } 166 } 167 } 168 } 169 `) 170 } 171 172 func TestCreateDomainIDScope(t *testing.T) { 173 options := tokens.AuthOptions{UserID: "someuser", Password: "somepassword"} 174 scope := &tokens.Scope{DomainID: "1000"} 175 authTokenPost(t, options, scope, ` 176 { 177 "auth": { 178 "identity": { 179 "methods": ["password"], 180 "password": { 181 "user": { 182 "id": "someuser", 183 "password": "somepassword" 184 } 185 } 186 }, 187 "scope": { 188 "domain": { 189 "id": "1000" 190 } 191 } 192 } 193 } 194 `) 195 } 196 197 func TestCreateDomainNameScope(t *testing.T) { 198 options := tokens.AuthOptions{UserID: "someuser", Password: "somepassword"} 199 scope := &tokens.Scope{DomainName: "evil-plans"} 200 authTokenPost(t, options, scope, ` 201 { 202 "auth": { 203 "identity": { 204 "methods": ["password"], 205 "password": { 206 "user": { 207 "id": "someuser", 208 "password": "somepassword" 209 } 210 } 211 }, 212 "scope": { 213 "domain": { 214 "name": "evil-plans" 215 } 216 } 217 } 218 } 219 `) 220 } 221 222 func TestCreateProjectNameAndDomainIDScope(t *testing.T) { 223 options := tokens.AuthOptions{UserID: "someuser", Password: "somepassword"} 224 scope := &tokens.Scope{ProjectName: "world-domination", DomainID: "1000"} 225 authTokenPost(t, options, scope, ` 226 { 227 "auth": { 228 "identity": { 229 "methods": ["password"], 230 "password": { 231 "user": { 232 "id": "someuser", 233 "password": "somepassword" 234 } 235 } 236 }, 237 "scope": { 238 "project": { 239 "domain": { 240 "id": "1000" 241 }, 242 "name": "world-domination" 243 } 244 } 245 } 246 } 247 `) 248 } 249 250 func TestCreateProjectNameAndDomainNameScope(t *testing.T) { 251 options := tokens.AuthOptions{UserID: "someuser", Password: "somepassword"} 252 scope := &tokens.Scope{ProjectName: "world-domination", DomainName: "evil-plans"} 253 authTokenPost(t, options, scope, ` 254 { 255 "auth": { 256 "identity": { 257 "methods": ["password"], 258 "password": { 259 "user": { 260 "id": "someuser", 261 "password": "somepassword" 262 } 263 } 264 }, 265 "scope": { 266 "project": { 267 "domain": { 268 "name": "evil-plans" 269 }, 270 "name": "world-domination" 271 } 272 } 273 } 274 } 275 `) 276 } 277 278 func TestCreateSystemScope(t *testing.T) { 279 options := tokens.AuthOptions{UserID: "someuser", Password: "somepassword"} 280 scope := &tokens.Scope{System: true} 281 authTokenPost(t, options, scope, ` 282 { 283 "auth": { 284 "identity": { 285 "methods": ["password"], 286 "password": { 287 "user": { 288 "id": "someuser", 289 "password": "somepassword" 290 } 291 } 292 }, 293 "scope": { 294 "system": { 295 "all": true 296 } 297 } 298 } 299 } 300 `) 301 } 302 303 func TestCreateApplicationCredentialIDAndSecret(t *testing.T) { 304 authTokenPost(t, tokens.AuthOptions{ApplicationCredentialID: "12345abcdef", ApplicationCredentialSecret: "mysecret"}, nil, ` 305 { 306 "auth": { 307 "identity": { 308 "application_credential": { 309 "id": "12345abcdef", 310 "secret": "mysecret" 311 }, 312 "methods": [ 313 "application_credential" 314 ] 315 } 316 } 317 } 318 `) 319 } 320 321 func TestCreateApplicationCredentialNameAndSecret(t *testing.T) { 322 authTokenPost(t, tokens.AuthOptions{ApplicationCredentialName: "myappcred", ApplicationCredentialSecret: "mysecret", Username: "someuser", DomainName: "evil-plans"}, nil, ` 323 { 324 "auth": { 325 "identity": { 326 "application_credential": { 327 "name": "myappcred", 328 "secret": "mysecret", 329 "user": { 330 "name": "someuser", 331 "domain": { 332 "name": "evil-plans" 333 } 334 } 335 }, 336 "methods": [ 337 "application_credential" 338 ] 339 } 340 } 341 } 342 `) 343 } 344 345 func TestCreateTOTPProjectNameAndDomainNameScope(t *testing.T) { 346 options := tokens.AuthOptions{UserID: "someuser", Passcode: "12345678"} 347 scope := &tokens.Scope{ProjectName: "world-domination", DomainName: "evil-plans"} 348 authTokenPost(t, options, scope, ` 349 { 350 "auth": { 351 "identity": { 352 "methods": ["totp"], 353 "totp": { 354 "user": { 355 "id": "someuser", 356 "passcode": "12345678" 357 } 358 } 359 }, 360 "scope": { 361 "project": { 362 "domain": { 363 "name": "evil-plans" 364 }, 365 "name": "world-domination" 366 } 367 } 368 } 369 } 370 `) 371 } 372 373 func TestCreatePasswordTOTPProjectNameAndDomainNameScope(t *testing.T) { 374 options := tokens.AuthOptions{UserID: "someuser", Password: "somepassword", Passcode: "12345678"} 375 scope := &tokens.Scope{ProjectName: "world-domination", DomainName: "evil-plans"} 376 authTokenPost(t, options, scope, ` 377 { 378 "auth": { 379 "identity": { 380 "methods": ["password","totp"], 381 "password": { 382 "user": { 383 "id": "someuser", 384 "password": "somepassword" 385 } 386 }, 387 "totp": { 388 "user": { 389 "id": "someuser", 390 "passcode": "12345678" 391 } 392 } 393 }, 394 "scope": { 395 "project": { 396 "domain": { 397 "name": "evil-plans" 398 }, 399 "name": "world-domination" 400 } 401 } 402 } 403 } 404 `) 405 } 406 407 func TestCreateExtractsTokenFromResponse(t *testing.T) { 408 testhelper.SetupHTTP() 409 defer testhelper.TeardownHTTP() 410 411 client := gophercloud.ServiceClient{ 412 ProviderClient: &gophercloud.ProviderClient{}, 413 Endpoint: testhelper.Endpoint(), 414 } 415 416 testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { 417 w.Header().Add("X-Subject-Token", "aaa111") 418 419 w.WriteHeader(http.StatusCreated) 420 fmt.Fprintf(w, `{ 421 "token": { 422 "expires_at": "2014-10-02T13:45:00.000000Z" 423 } 424 }`) 425 }) 426 427 options := tokens.AuthOptions{UserID: "me", Password: "shhh"} 428 token, err := tokens.Create(&client, &options).Extract() 429 if err != nil { 430 t.Fatalf("Create returned an error: %v", err) 431 } 432 433 if token.ID != "aaa111" { 434 t.Errorf("Expected token to be aaa111, but was %s", token.ID) 435 } 436 } 437 438 func TestCreateFailureEmptyAuth(t *testing.T) { 439 authTokenPostErr(t, tokens.AuthOptions{}, nil, false, gophercloud.ErrMissingPassword{}) 440 } 441 442 func TestCreateFailureTokenIDUsername(t *testing.T) { 443 authTokenPostErr(t, tokens.AuthOptions{Username: "something", TokenID: "12345"}, nil, true, gophercloud.ErrUsernameWithToken{}) 444 } 445 446 func TestCreateFailureTokenIDUserID(t *testing.T) { 447 authTokenPostErr(t, tokens.AuthOptions{UserID: "something", TokenID: "12345"}, nil, true, gophercloud.ErrUserIDWithToken{}) 448 } 449 450 func TestCreateFailureTokenIDDomainID(t *testing.T) { 451 authTokenPostErr(t, tokens.AuthOptions{DomainID: "something", TokenID: "12345"}, nil, true, gophercloud.ErrDomainIDWithToken{}) 452 } 453 454 func TestCreateFailureTokenIDDomainName(t *testing.T) { 455 authTokenPostErr(t, tokens.AuthOptions{DomainName: "something", TokenID: "12345"}, nil, true, gophercloud.ErrDomainNameWithToken{}) 456 } 457 458 func TestCreateFailureMissingUser(t *testing.T) { 459 options := tokens.AuthOptions{Password: "supersecure"} 460 authTokenPostErr(t, options, nil, false, gophercloud.ErrUsernameOrUserID{}) 461 } 462 463 func TestCreateFailureBothUser(t *testing.T) { 464 options := tokens.AuthOptions{ 465 Password: "supersecure", 466 Username: "oops", 467 UserID: "redundancy", 468 } 469 authTokenPostErr(t, options, nil, false, gophercloud.ErrUsernameOrUserID{}) 470 } 471 472 func TestCreateFailureMissingDomain(t *testing.T) { 473 options := tokens.AuthOptions{ 474 Password: "supersecure", 475 Username: "notuniqueenough", 476 } 477 authTokenPostErr(t, options, nil, false, gophercloud.ErrDomainIDOrDomainName{}) 478 } 479 480 func TestCreateFailureBothDomain(t *testing.T) { 481 options := tokens.AuthOptions{ 482 Password: "supersecure", 483 Username: "someone", 484 DomainID: "hurf", 485 DomainName: "durf", 486 } 487 authTokenPostErr(t, options, nil, false, gophercloud.ErrDomainIDOrDomainName{}) 488 } 489 490 func TestCreateFailureUserIDDomainID(t *testing.T) { 491 options := tokens.AuthOptions{ 492 UserID: "100", 493 Password: "stuff", 494 DomainID: "oops", 495 } 496 authTokenPostErr(t, options, nil, false, gophercloud.ErrDomainIDWithUserID{}) 497 } 498 499 func TestCreateFailureUserIDDomainName(t *testing.T) { 500 options := tokens.AuthOptions{ 501 UserID: "100", 502 Password: "sssh", 503 DomainName: "oops", 504 } 505 authTokenPostErr(t, options, nil, false, gophercloud.ErrDomainNameWithUserID{}) 506 } 507 508 func TestCreateFailureScopeProjectNameAlone(t *testing.T) { 509 options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"} 510 scope := &tokens.Scope{ProjectName: "notenough"} 511 authTokenPostErr(t, options, scope, false, gophercloud.ErrScopeDomainIDOrDomainName{}) 512 } 513 514 func TestCreateFailureScopeProjectNameAndID(t *testing.T) { 515 options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"} 516 scope := &tokens.Scope{ProjectName: "whoops", ProjectID: "toomuch", DomainID: "1234"} 517 authTokenPostErr(t, options, scope, false, gophercloud.ErrScopeProjectIDOrProjectName{}) 518 } 519 520 func TestCreateFailureScopeProjectIDAndDomainID(t *testing.T) { 521 options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"} 522 scope := &tokens.Scope{ProjectID: "toomuch", DomainID: "notneeded"} 523 authTokenPostErr(t, options, scope, false, gophercloud.ErrScopeProjectIDAlone{}) 524 } 525 526 func TestCreateFailureScopeProjectIDAndDomainNAme(t *testing.T) { 527 options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"} 528 scope := &tokens.Scope{ProjectID: "toomuch", DomainName: "notneeded"} 529 authTokenPostErr(t, options, scope, false, gophercloud.ErrScopeProjectIDAlone{}) 530 } 531 532 func TestCreateFailureScopeDomainIDAndDomainName(t *testing.T) { 533 options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"} 534 scope := &tokens.Scope{DomainID: "toomuch", DomainName: "notneeded"} 535 authTokenPostErr(t, options, scope, false, gophercloud.ErrScopeDomainIDOrDomainName{}) 536 } 537 538 /* 539 func TestCreateFailureEmptyScope(t *testing.T) { 540 options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"} 541 scope := &tokens.Scope{} 542 authTokenPostErr(t, options, scope, false, gophercloud.ErrScopeEmpty{}) 543 } 544 */ 545 546 func TestGetRequest(t *testing.T) { 547 testhelper.SetupHTTP() 548 defer testhelper.TeardownHTTP() 549 550 client := gophercloud.ServiceClient{ 551 ProviderClient: &gophercloud.ProviderClient{ 552 TokenID: "12345abcdef", 553 }, 554 Endpoint: testhelper.Endpoint(), 555 } 556 557 testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { 558 testhelper.TestMethod(t, r, "GET") 559 testhelper.TestHeaderUnset(t, r, "Content-Type") 560 testhelper.TestHeader(t, r, "Accept", "application/json") 561 testhelper.TestHeader(t, r, "X-Auth-Token", "12345abcdef") 562 testhelper.TestHeader(t, r, "X-Subject-Token", "abcdef12345") 563 564 w.WriteHeader(http.StatusOK) 565 fmt.Fprintf(w, ` 566 { "token": { "expires_at": "2014-08-29T13:10:01.000000Z" } } 567 `) 568 }) 569 570 token, err := tokens.Get(&client, "abcdef12345").Extract() 571 if err != nil { 572 t.Errorf("Info returned an error: %v", err) 573 } 574 575 expected, _ := time.Parse(time.UnixDate, "Fri Aug 29 13:10:01 UTC 2014") 576 if token.ExpiresAt != expected { 577 t.Errorf("Expected expiration time %s, but was %s", expected.Format(time.UnixDate), time.Time(token.ExpiresAt).Format(time.UnixDate)) 578 } 579 } 580 581 func prepareAuthTokenHandler(t *testing.T, expectedMethod string, status int) gophercloud.ServiceClient { 582 client := gophercloud.ServiceClient{ 583 ProviderClient: &gophercloud.ProviderClient{ 584 TokenID: "12345abcdef", 585 }, 586 Endpoint: testhelper.Endpoint(), 587 } 588 589 testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { 590 testhelper.TestMethod(t, r, expectedMethod) 591 testhelper.TestHeaderUnset(t, r, "Content-Type") 592 testhelper.TestHeader(t, r, "Accept", "application/json") 593 testhelper.TestHeader(t, r, "X-Auth-Token", "12345abcdef") 594 testhelper.TestHeader(t, r, "X-Subject-Token", "abcdef12345") 595 596 w.WriteHeader(status) 597 }) 598 599 return client 600 } 601 602 func TestValidateRequestSuccessful(t *testing.T) { 603 testhelper.SetupHTTP() 604 defer testhelper.TeardownHTTP() 605 client := prepareAuthTokenHandler(t, "HEAD", http.StatusNoContent) 606 607 ok, err := tokens.Validate(&client, "abcdef12345") 608 if err != nil { 609 t.Errorf("Unexpected error from Validate: %v", err) 610 } 611 612 if !ok { 613 t.Errorf("Validate returned false for a valid token") 614 } 615 } 616 617 func TestValidateRequestFailure(t *testing.T) { 618 testhelper.SetupHTTP() 619 defer testhelper.TeardownHTTP() 620 client := prepareAuthTokenHandler(t, "HEAD", http.StatusNotFound) 621 622 ok, err := tokens.Validate(&client, "abcdef12345") 623 if err != nil { 624 t.Errorf("Unexpected error from Validate: %v", err) 625 } 626 627 if ok { 628 t.Errorf("Validate returned true for an invalid token") 629 } 630 } 631 632 func TestValidateRequestError(t *testing.T) { 633 testhelper.SetupHTTP() 634 defer testhelper.TeardownHTTP() 635 client := prepareAuthTokenHandler(t, "HEAD", http.StatusMethodNotAllowed) 636 637 _, err := tokens.Validate(&client, "abcdef12345") 638 if err == nil { 639 t.Errorf("Missing expected error from Validate") 640 } 641 } 642 643 func TestRevokeRequestSuccessful(t *testing.T) { 644 testhelper.SetupHTTP() 645 defer testhelper.TeardownHTTP() 646 client := prepareAuthTokenHandler(t, "DELETE", http.StatusNoContent) 647 648 res := tokens.Revoke(&client, "abcdef12345") 649 testhelper.AssertNoErr(t, res.Err) 650 } 651 652 func TestRevokeRequestError(t *testing.T) { 653 testhelper.SetupHTTP() 654 defer testhelper.TeardownHTTP() 655 client := prepareAuthTokenHandler(t, "DELETE", http.StatusNotFound) 656 657 res := tokens.Revoke(&client, "abcdef12345") 658 if res.Err == nil { 659 t.Errorf("Missing expected error from Revoke") 660 } 661 } 662 663 func TestNoTokenInResponse(t *testing.T) { 664 testhelper.SetupHTTP() 665 defer testhelper.TeardownHTTP() 666 667 client := gophercloud.ServiceClient{ 668 ProviderClient: &gophercloud.ProviderClient{}, 669 Endpoint: testhelper.Endpoint(), 670 } 671 672 testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { 673 w.WriteHeader(http.StatusCreated) 674 fmt.Fprintf(w, `{}`) 675 }) 676 677 options := tokens.AuthOptions{UserID: "me", Password: "squirrel!"} 678 _, err := tokens.Create(&client, &options).Extract() 679 testhelper.AssertNoErr(t, err) 680 }