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