github.com/anfernee/terraform@v0.6.16-0.20160430000239-06e5085a92f2/builtin/providers/aws/auth_helpers_test.go (about) 1 package aws 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io/ioutil" 8 "log" 9 "net/http" 10 "net/http/httptest" 11 "os" 12 "testing" 13 "time" 14 15 "github.com/aws/aws-sdk-go/aws" 16 "github.com/aws/aws-sdk-go/aws/awserr" 17 awsCredentials "github.com/aws/aws-sdk-go/aws/credentials" 18 "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds" 19 "github.com/aws/aws-sdk-go/aws/session" 20 "github.com/aws/aws-sdk-go/service/iam" 21 ) 22 23 func TestAWSGetAccountId_shouldBeValid_fromEC2Role(t *testing.T) { 24 resetEnv := unsetEnv(t) 25 defer resetEnv() 26 // capture the test server's close method, to call after the test returns 27 awsTs := awsEnv(t) 28 defer awsTs() 29 30 iamEndpoints := []*iamEndpoint{} 31 ts, iamConn := getMockedAwsIamApi(iamEndpoints) 32 defer ts() 33 34 id, err := GetAccountId(iamConn, ec2rolecreds.ProviderName) 35 if err != nil { 36 t.Fatalf("Getting account ID from EC2 metadata API failed: %s", err) 37 } 38 39 expectedAccountId := "123456789013" 40 if id != expectedAccountId { 41 t.Fatalf("Expected account ID: %s, given: %s", expectedAccountId, id) 42 } 43 } 44 45 func TestAWSGetAccountId_shouldBeValid_EC2RoleHasPriority(t *testing.T) { 46 resetEnv := unsetEnv(t) 47 defer resetEnv() 48 // capture the test server's close method, to call after the test returns 49 awsTs := awsEnv(t) 50 defer awsTs() 51 52 iamEndpoints := []*iamEndpoint{ 53 &iamEndpoint{ 54 Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, 55 Response: &iamResponse{200, iamResponse_GetUser_valid, "text/xml"}, 56 }, 57 } 58 ts, iamConn := getMockedAwsIamApi(iamEndpoints) 59 defer ts() 60 61 id, err := GetAccountId(iamConn, ec2rolecreds.ProviderName) 62 if err != nil { 63 t.Fatalf("Getting account ID from EC2 metadata API failed: %s", err) 64 } 65 66 expectedAccountId := "123456789013" 67 if id != expectedAccountId { 68 t.Fatalf("Expected account ID: %s, given: %s", expectedAccountId, id) 69 } 70 } 71 72 func TestAWSGetAccountId_shouldBeValid_fromIamUser(t *testing.T) { 73 iamEndpoints := []*iamEndpoint{ 74 &iamEndpoint{ 75 Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, 76 Response: &iamResponse{200, iamResponse_GetUser_valid, "text/xml"}, 77 }, 78 } 79 ts, iamConn := getMockedAwsIamApi(iamEndpoints) 80 defer ts() 81 82 id, err := GetAccountId(iamConn, "") 83 if err != nil { 84 t.Fatalf("Getting account ID via GetUser failed: %s", err) 85 } 86 87 expectedAccountId := "123456789012" 88 if id != expectedAccountId { 89 t.Fatalf("Expected account ID: %s, given: %s", expectedAccountId, id) 90 } 91 } 92 93 func TestAWSGetAccountId_shouldBeValid_fromIamListRoles(t *testing.T) { 94 iamEndpoints := []*iamEndpoint{ 95 &iamEndpoint{ 96 Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, 97 Response: &iamResponse{403, iamResponse_GetUser_unauthorized, "text/xml"}, 98 }, 99 &iamEndpoint{ 100 Request: &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"}, 101 Response: &iamResponse{200, iamResponse_ListRoles_valid, "text/xml"}, 102 }, 103 } 104 ts, iamConn := getMockedAwsIamApi(iamEndpoints) 105 defer ts() 106 107 id, err := GetAccountId(iamConn, "") 108 if err != nil { 109 t.Fatalf("Getting account ID via ListRoles failed: %s", err) 110 } 111 112 expectedAccountId := "123456789012" 113 if id != expectedAccountId { 114 t.Fatalf("Expected account ID: %s, given: %s", expectedAccountId, id) 115 } 116 } 117 118 func TestAWSGetAccountId_shouldBeValid_federatedRole(t *testing.T) { 119 iamEndpoints := []*iamEndpoint{ 120 &iamEndpoint{ 121 Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, 122 Response: &iamResponse{400, iamResponse_GetUser_federatedFailure, "text/xml"}, 123 }, 124 &iamEndpoint{ 125 Request: &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"}, 126 Response: &iamResponse{200, iamResponse_ListRoles_valid, "text/xml"}, 127 }, 128 } 129 ts, iamConn := getMockedAwsIamApi(iamEndpoints) 130 defer ts() 131 132 id, err := GetAccountId(iamConn, "") 133 if err != nil { 134 t.Fatalf("Getting account ID via ListRoles failed: %s", err) 135 } 136 137 expectedAccountId := "123456789012" 138 if id != expectedAccountId { 139 t.Fatalf("Expected account ID: %s, given: %s", expectedAccountId, id) 140 } 141 } 142 143 func TestAWSGetAccountId_shouldError_unauthorizedFromIam(t *testing.T) { 144 iamEndpoints := []*iamEndpoint{ 145 &iamEndpoint{ 146 Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, 147 Response: &iamResponse{403, iamResponse_GetUser_unauthorized, "text/xml"}, 148 }, 149 &iamEndpoint{ 150 Request: &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"}, 151 Response: &iamResponse{403, iamResponse_ListRoles_unauthorized, "text/xml"}, 152 }, 153 } 154 ts, iamConn := getMockedAwsIamApi(iamEndpoints) 155 defer ts() 156 157 id, err := GetAccountId(iamConn, "") 158 if err == nil { 159 t.Fatal("Expected error when getting account ID") 160 } 161 162 if id != "" { 163 t.Fatalf("Expected no account ID, given: %s", id) 164 } 165 } 166 167 func TestAWSParseAccountIdFromArn(t *testing.T) { 168 validArn := "arn:aws:iam::101636750127:instance-profile/aws-elasticbeanstalk-ec2-role" 169 expectedId := "101636750127" 170 id, err := parseAccountIdFromArn(validArn) 171 if err != nil { 172 t.Fatalf("Expected no error when parsing valid ARN: %s", err) 173 } 174 if id != expectedId { 175 t.Fatalf("Parsed id doesn't match with expected (%q != %q)", id, expectedId) 176 } 177 178 invalidArn := "blablah" 179 id, err = parseAccountIdFromArn(invalidArn) 180 if err == nil { 181 t.Fatalf("Expected error when parsing invalid ARN (%q)", invalidArn) 182 } 183 } 184 185 func TestAWSGetCredentials_shouldError(t *testing.T) { 186 resetEnv := unsetEnv(t) 187 defer resetEnv() 188 cfg := Config{} 189 190 c := GetCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename) 191 _, err := c.Get() 192 if awsErr, ok := err.(awserr.Error); ok { 193 if awsErr.Code() != "NoCredentialProviders" { 194 t.Fatalf("Expected NoCredentialProviders error") 195 } 196 } 197 if err == nil { 198 t.Fatalf("Expected an error with empty env, keys, and IAM in AWS Config") 199 } 200 } 201 202 func TestAWSGetCredentials_shouldBeStatic(t *testing.T) { 203 simple := []struct { 204 Key, Secret, Token string 205 }{ 206 { 207 Key: "test", 208 Secret: "secret", 209 }, { 210 Key: "test", 211 Secret: "test", 212 Token: "test", 213 }, 214 } 215 216 for _, c := range simple { 217 cfg := Config{ 218 AccessKey: c.Key, 219 SecretKey: c.Secret, 220 Token: c.Token, 221 } 222 223 creds := GetCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename) 224 if creds == nil { 225 t.Fatalf("Expected a static creds provider to be returned") 226 } 227 v, err := creds.Get() 228 if err != nil { 229 t.Fatalf("Error gettings creds: %s", err) 230 } 231 if v.AccessKeyID != c.Key { 232 t.Fatalf("AccessKeyID mismatch, expected: (%s), got (%s)", c.Key, v.AccessKeyID) 233 } 234 if v.SecretAccessKey != c.Secret { 235 t.Fatalf("SecretAccessKey mismatch, expected: (%s), got (%s)", c.Secret, v.SecretAccessKey) 236 } 237 if v.SessionToken != c.Token { 238 t.Fatalf("SessionToken mismatch, expected: (%s), got (%s)", c.Token, v.SessionToken) 239 } 240 } 241 } 242 243 // TestAWSGetCredentials_shouldIAM is designed to test the scenario of running Terraform 244 // from an EC2 instance, without environment variables or manually supplied 245 // credentials. 246 func TestAWSGetCredentials_shouldIAM(t *testing.T) { 247 // clear AWS_* environment variables 248 resetEnv := unsetEnv(t) 249 defer resetEnv() 250 251 // capture the test server's close method, to call after the test returns 252 ts := awsEnv(t) 253 defer ts() 254 255 // An empty config, no key supplied 256 cfg := Config{} 257 258 creds := GetCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename) 259 if creds == nil { 260 t.Fatalf("Expected a static creds provider to be returned") 261 } 262 263 v, err := creds.Get() 264 if err != nil { 265 t.Fatalf("Error gettings creds: %s", err) 266 } 267 if v.AccessKeyID != "somekey" { 268 t.Fatalf("AccessKeyID mismatch, expected: (somekey), got (%s)", v.AccessKeyID) 269 } 270 if v.SecretAccessKey != "somesecret" { 271 t.Fatalf("SecretAccessKey mismatch, expected: (somesecret), got (%s)", v.SecretAccessKey) 272 } 273 if v.SessionToken != "sometoken" { 274 t.Fatalf("SessionToken mismatch, expected: (sometoken), got (%s)", v.SessionToken) 275 } 276 } 277 278 // TestAWSGetCredentials_shouldIAM is designed to test the scenario of running Terraform 279 // from an EC2 instance, without environment variables or manually supplied 280 // credentials. 281 func TestAWSGetCredentials_shouldIgnoreIAM(t *testing.T) { 282 resetEnv := unsetEnv(t) 283 defer resetEnv() 284 // capture the test server's close method, to call after the test returns 285 ts := awsEnv(t) 286 defer ts() 287 simple := []struct { 288 Key, Secret, Token string 289 }{ 290 { 291 Key: "test", 292 Secret: "secret", 293 }, { 294 Key: "test", 295 Secret: "test", 296 Token: "test", 297 }, 298 } 299 300 for _, c := range simple { 301 cfg := Config{ 302 AccessKey: c.Key, 303 SecretKey: c.Secret, 304 Token: c.Token, 305 } 306 307 creds := GetCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename) 308 if creds == nil { 309 t.Fatalf("Expected a static creds provider to be returned") 310 } 311 v, err := creds.Get() 312 if err != nil { 313 t.Fatalf("Error gettings creds: %s", err) 314 } 315 if v.AccessKeyID != c.Key { 316 t.Fatalf("AccessKeyID mismatch, expected: (%s), got (%s)", c.Key, v.AccessKeyID) 317 } 318 if v.SecretAccessKey != c.Secret { 319 t.Fatalf("SecretAccessKey mismatch, expected: (%s), got (%s)", c.Secret, v.SecretAccessKey) 320 } 321 if v.SessionToken != c.Token { 322 t.Fatalf("SessionToken mismatch, expected: (%s), got (%s)", c.Token, v.SessionToken) 323 } 324 } 325 } 326 327 func TestAWSGetCredentials_shouldErrorWithInvalidEndpoint(t *testing.T) { 328 resetEnv := unsetEnv(t) 329 defer resetEnv() 330 // capture the test server's close method, to call after the test returns 331 ts := invalidAwsEnv(t) 332 defer ts() 333 334 creds := GetCredentials("", "", "", "", "") 335 v, err := creds.Get() 336 if err == nil { 337 t.Fatal("Expected error returned when getting creds w/ invalid EC2 endpoint") 338 } 339 340 if v.ProviderName != "" { 341 t.Fatalf("Expected provider name to be empty, %q given", v.ProviderName) 342 } 343 } 344 345 func TestAWSGetCredentials_shouldIgnoreInvalidEndpoint(t *testing.T) { 346 resetEnv := unsetEnv(t) 347 defer resetEnv() 348 // capture the test server's close method, to call after the test returns 349 ts := invalidAwsEnv(t) 350 defer ts() 351 352 creds := GetCredentials("accessKey", "secretKey", "", "", "") 353 v, err := creds.Get() 354 if err != nil { 355 t.Fatalf("Getting static credentials w/ invalid EC2 endpoint failed: %s", err) 356 } 357 358 if v.ProviderName != "StaticProvider" { 359 t.Fatalf("Expected provider name to be %q, %q given", "StaticProvider", v.ProviderName) 360 } 361 362 if v.AccessKeyID != "accessKey" { 363 t.Fatalf("Static Access Key %q doesn't match: %s", "accessKey", v.AccessKeyID) 364 } 365 366 if v.SecretAccessKey != "secretKey" { 367 t.Fatalf("Static Secret Key %q doesn't match: %s", "secretKey", v.SecretAccessKey) 368 } 369 } 370 371 func TestAWSGetCredentials_shouldCatchEC2RoleProvider(t *testing.T) { 372 resetEnv := unsetEnv(t) 373 defer resetEnv() 374 // capture the test server's close method, to call after the test returns 375 ts := awsEnv(t) 376 defer ts() 377 378 creds := GetCredentials("", "", "", "", "") 379 if creds == nil { 380 t.Fatalf("Expected an EC2Role creds provider to be returned") 381 } 382 v, err := creds.Get() 383 if err != nil { 384 t.Fatalf("Expected no error when getting creds: %s", err) 385 } 386 expectedProvider := "EC2RoleProvider" 387 if v.ProviderName != expectedProvider { 388 t.Fatalf("Expected provider name to be %q, %q given", 389 expectedProvider, v.ProviderName) 390 } 391 } 392 393 var credentialsFileContents = `[myprofile] 394 aws_access_key_id = accesskey 395 aws_secret_access_key = secretkey 396 ` 397 398 func TestAWSGetCredentials_shouldBeShared(t *testing.T) { 399 file, err := ioutil.TempFile(os.TempDir(), "terraform_aws_cred") 400 if err != nil { 401 t.Fatalf("Error writing temporary credentials file: %s", err) 402 } 403 _, err = file.WriteString(credentialsFileContents) 404 if err != nil { 405 t.Fatalf("Error writing temporary credentials to file: %s", err) 406 } 407 err = file.Close() 408 if err != nil { 409 t.Fatalf("Error closing temporary credentials file: %s", err) 410 } 411 412 defer os.Remove(file.Name()) 413 414 resetEnv := unsetEnv(t) 415 defer resetEnv() 416 417 if err := os.Setenv("AWS_PROFILE", "myprofile"); err != nil { 418 t.Fatalf("Error resetting env var AWS_PROFILE: %s", err) 419 } 420 if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", file.Name()); err != nil { 421 t.Fatalf("Error resetting env var AWS_SHARED_CREDENTIALS_FILE: %s", err) 422 } 423 424 creds := GetCredentials("", "", "", "myprofile", file.Name()) 425 if creds == nil { 426 t.Fatalf("Expected a provider chain to be returned") 427 } 428 v, err := creds.Get() 429 if err != nil { 430 t.Fatalf("Error gettings creds: %s", err) 431 } 432 433 if v.AccessKeyID != "accesskey" { 434 t.Fatalf("AccessKeyID mismatch, expected (%s), got (%s)", "accesskey", v.AccessKeyID) 435 } 436 437 if v.SecretAccessKey != "secretkey" { 438 t.Fatalf("SecretAccessKey mismatch, expected (%s), got (%s)", "accesskey", v.AccessKeyID) 439 } 440 } 441 442 func TestAWSGetCredentials_shouldBeENV(t *testing.T) { 443 // need to set the environment variables to a dummy string, as we don't know 444 // what they may be at runtime without hardcoding here 445 s := "some_env" 446 resetEnv := setEnv(s, t) 447 448 defer resetEnv() 449 450 cfg := Config{} 451 creds := GetCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename) 452 if creds == nil { 453 t.Fatalf("Expected a static creds provider to be returned") 454 } 455 v, err := creds.Get() 456 if err != nil { 457 t.Fatalf("Error gettings creds: %s", err) 458 } 459 if v.AccessKeyID != s { 460 t.Fatalf("AccessKeyID mismatch, expected: (%s), got (%s)", s, v.AccessKeyID) 461 } 462 if v.SecretAccessKey != s { 463 t.Fatalf("SecretAccessKey mismatch, expected: (%s), got (%s)", s, v.SecretAccessKey) 464 } 465 if v.SessionToken != s { 466 t.Fatalf("SessionToken mismatch, expected: (%s), got (%s)", s, v.SessionToken) 467 } 468 } 469 470 // unsetEnv unsets enviornment variables for testing a "clean slate" with no 471 // credentials in the environment 472 func unsetEnv(t *testing.T) func() { 473 // Grab any existing AWS keys and preserve. In some tests we'll unset these, so 474 // we need to have them and restore them after 475 e := getEnv() 476 if err := os.Unsetenv("AWS_ACCESS_KEY_ID"); err != nil { 477 t.Fatalf("Error unsetting env var AWS_ACCESS_KEY_ID: %s", err) 478 } 479 if err := os.Unsetenv("AWS_SECRET_ACCESS_KEY"); err != nil { 480 t.Fatalf("Error unsetting env var AWS_SECRET_ACCESS_KEY: %s", err) 481 } 482 if err := os.Unsetenv("AWS_SESSION_TOKEN"); err != nil { 483 t.Fatalf("Error unsetting env var AWS_SESSION_TOKEN: %s", err) 484 } 485 if err := os.Unsetenv("AWS_PROFILE"); err != nil { 486 t.Fatalf("Error unsetting env var AWS_PROFILE: %s", err) 487 } 488 if err := os.Unsetenv("AWS_SHARED_CREDENTIALS_FILE"); err != nil { 489 t.Fatalf("Error unsetting env var AWS_SHARED_CREDENTIALS_FILE: %s", err) 490 } 491 492 return func() { 493 // re-set all the envs we unset above 494 if err := os.Setenv("AWS_ACCESS_KEY_ID", e.Key); err != nil { 495 t.Fatalf("Error resetting env var AWS_ACCESS_KEY_ID: %s", err) 496 } 497 if err := os.Setenv("AWS_SECRET_ACCESS_KEY", e.Secret); err != nil { 498 t.Fatalf("Error resetting env var AWS_SECRET_ACCESS_KEY: %s", err) 499 } 500 if err := os.Setenv("AWS_SESSION_TOKEN", e.Token); err != nil { 501 t.Fatalf("Error resetting env var AWS_SESSION_TOKEN: %s", err) 502 } 503 if err := os.Setenv("AWS_PROFILE", e.Profile); err != nil { 504 t.Fatalf("Error resetting env var AWS_PROFILE: %s", err) 505 } 506 if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", e.CredsFilename); err != nil { 507 t.Fatalf("Error resetting env var AWS_SHARED_CREDENTIALS_FILE: %s", err) 508 } 509 } 510 } 511 512 func setEnv(s string, t *testing.T) func() { 513 e := getEnv() 514 // Set all the envs to a dummy value 515 if err := os.Setenv("AWS_ACCESS_KEY_ID", s); err != nil { 516 t.Fatalf("Error setting env var AWS_ACCESS_KEY_ID: %s", err) 517 } 518 if err := os.Setenv("AWS_SECRET_ACCESS_KEY", s); err != nil { 519 t.Fatalf("Error setting env var AWS_SECRET_ACCESS_KEY: %s", err) 520 } 521 if err := os.Setenv("AWS_SESSION_TOKEN", s); err != nil { 522 t.Fatalf("Error setting env var AWS_SESSION_TOKEN: %s", err) 523 } 524 if err := os.Setenv("AWS_PROFILE", s); err != nil { 525 t.Fatalf("Error setting env var AWS_PROFILE: %s", err) 526 } 527 if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", s); err != nil { 528 t.Fatalf("Error setting env var AWS_SHARED_CREDENTIALS_FLE: %s", err) 529 } 530 531 return func() { 532 // re-set all the envs we unset above 533 if err := os.Setenv("AWS_ACCESS_KEY_ID", e.Key); err != nil { 534 t.Fatalf("Error resetting env var AWS_ACCESS_KEY_ID: %s", err) 535 } 536 if err := os.Setenv("AWS_SECRET_ACCESS_KEY", e.Secret); err != nil { 537 t.Fatalf("Error resetting env var AWS_SECRET_ACCESS_KEY: %s", err) 538 } 539 if err := os.Setenv("AWS_SESSION_TOKEN", e.Token); err != nil { 540 t.Fatalf("Error resetting env var AWS_SESSION_TOKEN: %s", err) 541 } 542 if err := os.Setenv("AWS_PROFILE", e.Profile); err != nil { 543 t.Fatalf("Error setting env var AWS_PROFILE: %s", err) 544 } 545 if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", s); err != nil { 546 t.Fatalf("Error setting env var AWS_SHARED_CREDENTIALS_FLE: %s", err) 547 } 548 } 549 } 550 551 // awsEnv establishes a httptest server to mock out the internal AWS Metadata 552 // service. IAM Credentials are retrieved by the EC2RoleProvider, which makes 553 // API calls to this internal URL. By replacing the server with a test server, 554 // we can simulate an AWS environment 555 func awsEnv(t *testing.T) func() { 556 routes := routes{} 557 if err := json.Unmarshal([]byte(metadataApiRoutes), &routes); err != nil { 558 t.Fatalf("Failed to unmarshal JSON in AWS ENV test: %s", err) 559 } 560 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 561 w.Header().Set("Content-Type", "text/plain") 562 w.Header().Add("Server", "MockEC2") 563 log.Printf("[DEBUG] Mocker server received request to %q", r.RequestURI) 564 for _, e := range routes.Endpoints { 565 if r.RequestURI == e.Uri { 566 fmt.Fprintln(w, e.Body) 567 w.WriteHeader(200) 568 return 569 } 570 } 571 w.WriteHeader(400) 572 })) 573 574 os.Setenv("AWS_METADATA_URL", ts.URL+"/latest") 575 return ts.Close 576 } 577 578 // invalidAwsEnv establishes a httptest server to simulate behaviour 579 // when endpoint doesn't respond as expected 580 func invalidAwsEnv(t *testing.T) func() { 581 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 582 w.WriteHeader(400) 583 })) 584 585 os.Setenv("AWS_METADATA_URL", ts.URL+"/latest") 586 return ts.Close 587 } 588 589 // getMockedAwsIamApi establishes a httptest server to simulate behaviour 590 // of a real AWS' IAM server 591 func getMockedAwsIamApi(endpoints []*iamEndpoint) (func(), *iam.IAM) { 592 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 593 buf := new(bytes.Buffer) 594 buf.ReadFrom(r.Body) 595 requestBody := buf.String() 596 597 log.Printf("[DEBUG] Received IAM API %q request to %q: %s", 598 r.Method, r.RequestURI, requestBody) 599 600 for _, e := range endpoints { 601 if r.Method == e.Request.Method && r.RequestURI == e.Request.Uri && requestBody == e.Request.Body { 602 log.Printf("[DEBUG] Mock API responding with %d: %s", e.Response.StatusCode, e.Response.Body) 603 604 w.WriteHeader(e.Response.StatusCode) 605 w.Header().Set("Content-Type", e.Response.ContentType) 606 w.Header().Set("X-Amzn-Requestid", "1b206dd1-f9a8-11e5-becf-051c60f11c4a") 607 w.Header().Set("Date", time.Now().Format(time.RFC1123)) 608 609 fmt.Fprintln(w, e.Response.Body) 610 return 611 } 612 } 613 614 w.WriteHeader(400) 615 return 616 })) 617 618 sc := awsCredentials.NewStaticCredentials("accessKey", "secretKey", "") 619 620 sess := session.New(&aws.Config{ 621 Credentials: sc, 622 Region: aws.String("us-east-1"), 623 Endpoint: aws.String(ts.URL), 624 CredentialsChainVerboseErrors: aws.Bool(true), 625 }) 626 iamConn := iam.New(sess) 627 628 return ts.Close, iamConn 629 } 630 631 func getEnv() *currentEnv { 632 // Grab any existing AWS keys and preserve. In some tests we'll unset these, so 633 // we need to have them and restore them after 634 return ¤tEnv{ 635 Key: os.Getenv("AWS_ACCESS_KEY_ID"), 636 Secret: os.Getenv("AWS_SECRET_ACCESS_KEY"), 637 Token: os.Getenv("AWS_SESSION_TOKEN"), 638 Profile: os.Getenv("AWS_PROFILE"), 639 CredsFilename: os.Getenv("AWS_SHARED_CREDENTIALS_FILE"), 640 } 641 } 642 643 // struct to preserve the current environment 644 type currentEnv struct { 645 Key, Secret, Token, Profile, CredsFilename string 646 } 647 648 type routes struct { 649 Endpoints []*endpoint `json:"endpoints"` 650 } 651 type endpoint struct { 652 Uri string `json:"uri"` 653 Body string `json:"body"` 654 } 655 656 const metadataApiRoutes = ` 657 { 658 "endpoints": [ 659 { 660 "uri": "/latest/meta-data/instance-id", 661 "body": "mock-instance-id" 662 }, 663 { 664 "uri": "/latest/meta-data/iam/info", 665 "body": "{\"Code\": \"Success\",\"LastUpdated\": \"2016-03-17T12:27:32Z\",\"InstanceProfileArn\": \"arn:aws:iam::123456789013:instance-profile/my-instance-profile\",\"InstanceProfileId\": \"AIPAABCDEFGHIJKLMN123\"}" 666 }, 667 { 668 "uri": "/latest/meta-data/iam/security-credentials", 669 "body": "test_role" 670 }, 671 { 672 "uri": "/latest/meta-data/iam/security-credentials/test_role", 673 "body": "{\"Code\":\"Success\",\"LastUpdated\":\"2015-12-11T17:17:25Z\",\"Type\":\"AWS-HMAC\",\"AccessKeyId\":\"somekey\",\"SecretAccessKey\":\"somesecret\",\"Token\":\"sometoken\"}" 674 } 675 ] 676 } 677 ` 678 679 type iamEndpoint struct { 680 Request *iamRequest 681 Response *iamResponse 682 } 683 684 type iamRequest struct { 685 Method string 686 Uri string 687 Body string 688 } 689 690 type iamResponse struct { 691 StatusCode int 692 Body string 693 ContentType string 694 } 695 696 const iamResponse_GetUser_valid = `<GetUserResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/"> 697 <GetUserResult> 698 <User> 699 <UserId>AIDACKCEVSQ6C2EXAMPLE</UserId> 700 <Path>/division_abc/subdivision_xyz/</Path> 701 <UserName>Bob</UserName> 702 <Arn>arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/Bob</Arn> 703 <CreateDate>2013-10-02T17:01:44Z</CreateDate> 704 <PasswordLastUsed>2014-10-10T14:37:51Z</PasswordLastUsed> 705 </User> 706 </GetUserResult> 707 <ResponseMetadata> 708 <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId> 709 </ResponseMetadata> 710 </GetUserResponse>` 711 712 const iamResponse_GetUser_unauthorized = `<ErrorResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/"> 713 <Error> 714 <Type>Sender</Type> 715 <Code>AccessDenied</Code> 716 <Message>User: arn:aws:iam::123456789012:user/Bob is not authorized to perform: iam:GetUser on resource: arn:aws:iam::123456789012:user/Bob</Message> 717 </Error> 718 <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId> 719 </ErrorResponse>` 720 721 const iamResponse_GetUser_federatedFailure = `<ErrorResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/"> 722 <Error> 723 <Type>Sender</Type> 724 <Code>ValidationError</Code> 725 <Message>Must specify userName when calling with non-User credentials</Message> 726 </Error> 727 <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId> 728 </ErrorResponse>` 729 730 const iamResponse_ListRoles_valid = `<ListRolesResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/"> 731 <ListRolesResult> 732 <IsTruncated>true</IsTruncated> 733 <Marker>AWceSSsKsazQ4IEplT9o4hURCzBs00iavlEvEXAMPLE</Marker> 734 <Roles> 735 <member> 736 <Path>/</Path> 737 <AssumeRolePolicyDocument>%7B%22Version%22%3A%222008-10-17%22%2C%22Statement%22%3A%5B%7B%22Sid%22%3A%22%22%2C%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22Service%22%3A%22ec2.amazonaws.com%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%7D%5D%7D</AssumeRolePolicyDocument> 738 <RoleId>AROACKCEVSQ6C2EXAMPLE</RoleId> 739 <RoleName>elasticbeanstalk-role</RoleName> 740 <Arn>arn:aws:iam::123456789012:role/elasticbeanstalk-role</Arn> 741 <CreateDate>2013-10-02T17:01:44Z</CreateDate> 742 </member> 743 </Roles> 744 </ListRolesResult> 745 <ResponseMetadata> 746 <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId> 747 </ResponseMetadata> 748 </ListRolesResponse>` 749 750 const iamResponse_ListRoles_unauthorized = `<ErrorResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/"> 751 <Error> 752 <Type>Sender</Type> 753 <Code>AccessDenied</Code> 754 <Message>User: arn:aws:iam::123456789012:user/Bob is not authorized to perform: iam:ListRoles on resource: arn:aws:iam::123456789012:role/</Message> 755 </Error> 756 <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId> 757 </ErrorResponse>`