1package aws
2
3import (
4	"context"
5	"fmt"
6	"log"
7	"net/http"
8	"os"
9	"reflect"
10	"strings"
11	"sync"
12	"testing"
13	"time"
14
15	"github.com/aws/aws-sdk-go/aws"
16	"github.com/aws/aws-sdk-go/aws/awserr"
17	"github.com/aws/aws-sdk-go/aws/credentials"
18	"github.com/aws/aws-sdk-go/aws/session"
19	"github.com/aws/aws-sdk-go/service/dynamodb"
20	"github.com/aws/aws-sdk-go/service/ec2"
21	"github.com/aws/aws-sdk-go/service/iam"
22	"github.com/aws/aws-sdk-go/service/iam/iamiface"
23	"github.com/aws/aws-sdk-go/service/s3"
24	"github.com/aws/aws-sdk-go/service/sts"
25	cleanhttp "github.com/hashicorp/go-cleanhttp"
26	"github.com/hashicorp/vault/helper/testhelpers"
27	logicaltest "github.com/hashicorp/vault/helper/testhelpers/logical"
28	"github.com/hashicorp/vault/sdk/logical"
29	"github.com/mitchellh/mapstructure"
30)
31
32var initSetup sync.Once
33
34type mockIAMClient struct {
35	iamiface.IAMAPI
36}
37
38func (m *mockIAMClient) CreateUser(input *iam.CreateUserInput) (*iam.CreateUserOutput, error) {
39	return nil, awserr.New("Throttling", "", nil)
40}
41
42func getBackend(t *testing.T) logical.Backend {
43	be, _ := Factory(context.Background(), logical.TestBackendConfig())
44	return be
45}
46
47func TestBackend_basic(t *testing.T) {
48	t.Parallel()
49	logicaltest.Test(t, logicaltest.TestCase{
50		AcceptanceTest: true,
51		PreCheck:       func() { testAccPreCheck(t) },
52		LogicalBackend: getBackend(t),
53		Steps: []logicaltest.TestStep{
54			testAccStepConfig(t),
55			testAccStepWritePolicy(t, "test", testDynamoPolicy),
56			testAccStepRead(t, "creds", "test", []credentialTestFunc{listDynamoTablesTest}),
57		},
58	})
59}
60
61func TestBackend_IamUserWithPermissionsBoundary(t *testing.T) {
62	t.Parallel()
63	roleData := map[string]interface{}{
64		"credential_type":          iamUserCred,
65		"policy_arns":              adminAccessPolicyArn,
66		"permissions_boundary_arn": iamPolicyArn,
67	}
68	logicaltest.Test(t, logicaltest.TestCase{
69		AcceptanceTest: true,
70		PreCheck:       func() { testAccPreCheck(t) },
71		LogicalBackend: getBackend(t),
72		Steps: []logicaltest.TestStep{
73			testAccStepConfig(t),
74			testAccStepWriteRole(t, "test", roleData),
75			testAccStepRead(t, "creds", "test", []credentialTestFunc{listIamUsersTest, describeAzsTestUnauthorized}),
76		},
77	})
78}
79
80func TestBackend_basicSTS(t *testing.T) {
81	t.Parallel()
82	awsAccountID, err := getAccountID()
83	if err != nil {
84		t.Logf("Unable to retrive user via sts:GetCallerIdentity: %#v", err)
85		t.Skip("Could not determine AWS account ID from sts:GetCallerIdentity for acceptance tests, skipping")
86	}
87	roleName := generateUniqueName(t.Name())
88	userName := generateUniqueName(t.Name())
89	accessKey := &awsAccessKey{}
90	logicaltest.Test(t, logicaltest.TestCase{
91		AcceptanceTest: true,
92		PreCheck: func() {
93			testAccPreCheck(t)
94			createUser(t, userName, accessKey)
95			createRole(t, roleName, awsAccountID, []string{ec2PolicyArn})
96			// Sleep sometime because AWS is eventually consistent
97			// Both the createUser and createRole depend on this
98			log.Println("[WARN] Sleeping for 10 seconds waiting for AWS...")
99			time.Sleep(10 * time.Second)
100		},
101		LogicalBackend: getBackend(t),
102		Steps: []logicaltest.TestStep{
103			testAccStepConfigWithCreds(t, accessKey),
104			testAccStepRotateRoot(accessKey),
105			testAccStepWritePolicy(t, "test", testDynamoPolicy),
106			testAccStepRead(t, "sts", "test", []credentialTestFunc{listDynamoTablesTest}),
107			testAccStepWriteArnPolicyRef(t, "test", ec2PolicyArn),
108			testAccStepReadSTSWithArnPolicy(t, "test"),
109			testAccStepWriteArnRoleRef(t, "test2", roleName, awsAccountID),
110			testAccStepRead(t, "sts", "test2", []credentialTestFunc{describeInstancesTest}),
111		},
112		Teardown: func() error {
113			if err := deleteTestRole(roleName); err != nil {
114				return err
115			}
116			return deleteTestUser(accessKey, userName)
117		},
118	})
119}
120
121func TestBackend_policyCrud(t *testing.T) {
122	t.Parallel()
123	compacted, err := compactJSON(testDynamoPolicy)
124	if err != nil {
125		t.Fatalf("bad: %s", err)
126	}
127
128	logicaltest.Test(t, logicaltest.TestCase{
129		AcceptanceTest: true,
130		LogicalBackend: getBackend(t),
131		Steps: []logicaltest.TestStep{
132			testAccStepConfig(t),
133			testAccStepWritePolicy(t, "test", testDynamoPolicy),
134			testAccStepReadPolicy(t, "test", compacted),
135			testAccStepDeletePolicy(t, "test"),
136			testAccStepReadPolicy(t, "test", ""),
137		},
138	})
139}
140
141func TestBackend_throttled(t *testing.T) {
142	t.Parallel()
143	config := logical.TestBackendConfig()
144	config.StorageView = &logical.InmemStorage{}
145
146	b := Backend()
147	if err := b.Setup(context.Background(), config); err != nil {
148		t.Fatal(err)
149	}
150
151	connData := map[string]interface{}{
152		"credential_type": "iam_user",
153	}
154
155	confReq := &logical.Request{
156		Operation: logical.UpdateOperation,
157		Path:      "roles/something",
158		Storage:   config.StorageView,
159		Data:      connData,
160	}
161
162	resp, err := b.HandleRequest(context.Background(), confReq)
163	if err != nil || (resp != nil && resp.IsError()) {
164		t.Fatalf("failed to write configuration: resp:%#v err:%s", resp, err)
165	}
166
167	// Mock the IAM API call to return a throttled response to the CreateUser API
168	// call
169	b.iamClient = &mockIAMClient{}
170
171	credReq := &logical.Request{
172		Operation: logical.UpdateOperation,
173		Path:      "creds/something",
174		Storage:   config.StorageView,
175	}
176
177	credResp, err := b.HandleRequest(context.Background(), credReq)
178	if err == nil {
179		t.Fatalf("failed to trigger expected throttling error condition: resp:%#v", credResp)
180	}
181	rErr := credResp.Error()
182	expected := "Error creating IAM user: Throttling: "
183	if rErr.Error() != expected {
184		t.Fatalf("error message did not match, expected (%s), got (%s)", expected, rErr.Error())
185	}
186
187	// verify the error we got back is returned with a http.StatusBadGateway
188	code, err := logical.RespondErrorCommon(credReq, credResp, err)
189	if err == nil {
190		t.Fatal("expected error after running req/resp/err through RespondErrorCommon, got nil")
191	}
192	if code != http.StatusBadGateway {
193		t.Fatalf("expected HTTP status 'bad gateway', got: (%d)", code)
194	}
195}
196
197func testAccPreCheck(t *testing.T) {
198	initSetup.Do(func() {
199		if v := os.Getenv("AWS_DEFAULT_REGION"); v == "" {
200			log.Println("[INFO] Test: Using us-west-2 as test region")
201			os.Setenv("AWS_DEFAULT_REGION", "us-west-2")
202		}
203	})
204}
205
206func getAccountID() (string, error) {
207	awsConfig := &aws.Config{
208		Region:     aws.String("us-east-1"),
209		HTTPClient: cleanhttp.DefaultClient(),
210	}
211	sess, err := session.NewSession(awsConfig)
212	if err != nil {
213		return "", err
214	}
215	svc := sts.New(sess)
216
217	params := &sts.GetCallerIdentityInput{}
218	res, err := svc.GetCallerIdentity(params)
219	if err != nil {
220		return "", err
221	}
222	if res == nil {
223		return "", fmt.Errorf("got nil response from GetCallerIdentity")
224	}
225
226	return *res.Account, nil
227}
228
229func createRole(t *testing.T, roleName, awsAccountID string, policyARNs []string) {
230	const testRoleAssumePolicy = `{
231      "Version": "2012-10-17",
232      "Statement": [
233          {
234              "Effect":"Allow",
235              "Principal": {
236                  "AWS": "arn:aws:iam::%s:root"
237              },
238              "Action": "sts:AssumeRole"
239           }
240      ]
241}
242`
243	awsConfig := &aws.Config{
244		Region:     aws.String("us-east-1"),
245		HTTPClient: cleanhttp.DefaultClient(),
246	}
247	sess, err := session.NewSession(awsConfig)
248	if err != nil {
249		t.Fatal(err)
250	}
251	svc := iam.New(sess)
252	trustPolicy := fmt.Sprintf(testRoleAssumePolicy, awsAccountID)
253
254	params := &iam.CreateRoleInput{
255		AssumeRolePolicyDocument: aws.String(trustPolicy),
256		RoleName:                 aws.String(roleName),
257		Path:                     aws.String("/"),
258	}
259
260	log.Printf("[INFO] AWS CreateRole: %s", roleName)
261	if _, err := svc.CreateRole(params); err != nil {
262		t.Fatalf("AWS CreateRole failed: %v", err)
263	}
264
265	for _, policyARN := range policyARNs {
266		attachment := &iam.AttachRolePolicyInput{
267			PolicyArn: aws.String(policyARN),
268			RoleName:  aws.String(roleName), // Required
269		}
270		_, err = svc.AttachRolePolicy(attachment)
271
272		if err != nil {
273			t.Fatalf("AWS AttachRolePolicy failed: %v", err)
274		}
275	}
276}
277
278func createUser(t *testing.T, userName string, accessKey *awsAccessKey) {
279	// The sequence of user creation actions is carefully chosen to minimize
280	// impact of stolen IAM user credentials
281	// 1. Create user, without any permissions or credentials. At this point,
282	//	  nobody cares if creds compromised because this user can do nothing.
283	// 2. Attach the timebomb policy. This grants no access but puts a time limit
284	//	  on validity of compromised credentials. If this fails, nobody cares
285	//	  because the user has no permissions to do anything anyway
286	// 3. Attach the AdminAccess policy. The IAM user still has no credentials to
287	//	  do anything
288	// 4. Generate API creds to get an actual access key and secret key
289	timebombPolicyTemplate := `{
290		"Version": "2012-10-17",
291		"Statement": [
292			{
293				"Effect": "Deny",
294				"Action": "*",
295				"Resource": "*",
296				"Condition": {
297					"DateGreaterThan": {
298						"aws:CurrentTime": "%s"
299					}
300				}
301			}
302		]
303	}
304	`
305	validity := time.Duration(2 * time.Hour)
306	expiry := time.Now().Add(validity)
307	timebombPolicy := fmt.Sprintf(timebombPolicyTemplate, expiry.Format(time.RFC3339))
308	awsConfig := &aws.Config{
309		Region:     aws.String("us-east-1"),
310		HTTPClient: cleanhttp.DefaultClient(),
311	}
312	sess, err := session.NewSession(awsConfig)
313	if err != nil {
314		t.Fatal(err)
315	}
316	svc := iam.New(sess)
317	createUserInput := &iam.CreateUserInput{
318		UserName: aws.String(userName),
319	}
320	log.Printf("[INFO] AWS CreateUser: %s", userName)
321	if _, err := svc.CreateUser(createUserInput); err != nil {
322		t.Fatalf("AWS CreateUser failed: %v", err)
323	}
324
325	putPolicyInput := &iam.PutUserPolicyInput{
326		PolicyDocument: aws.String(timebombPolicy),
327		PolicyName:     aws.String("SelfDestructionTimebomb"),
328		UserName:       aws.String(userName),
329	}
330	_, err = svc.PutUserPolicy(putPolicyInput)
331	if err != nil {
332		t.Fatalf("AWS PutUserPolicy failed: %v", err)
333	}
334
335	attachUserPolicyInput := &iam.AttachUserPolicyInput{
336		PolicyArn: aws.String("arn:aws:iam::aws:policy/AdministratorAccess"),
337		UserName:  aws.String(userName),
338	}
339	_, err = svc.AttachUserPolicy(attachUserPolicyInput)
340	if err != nil {
341		t.Fatalf("AWS AttachUserPolicy failed, %v", err)
342	}
343
344	createAccessKeyInput := &iam.CreateAccessKeyInput{
345		UserName: aws.String(userName),
346	}
347	createAccessKeyOutput, err := svc.CreateAccessKey(createAccessKeyInput)
348	if err != nil {
349		t.Fatalf("AWS CreateAccessKey failed: %v", err)
350	}
351	if createAccessKeyOutput == nil {
352		t.Fatalf("AWS CreateAccessKey returned nil")
353	}
354	genAccessKey := createAccessKeyOutput.AccessKey
355
356	accessKey.AccessKeyID = *genAccessKey.AccessKeyId
357	accessKey.SecretAccessKey = *genAccessKey.SecretAccessKey
358}
359
360// Create an IAM Group and add an inline policy and managed policies if specified
361func createGroup(t *testing.T, groupName string, inlinePolicy string, managedPolicies []string) {
362	awsConfig := &aws.Config{
363		Region:     aws.String("us-east-1"),
364		HTTPClient: cleanhttp.DefaultClient(),
365	}
366	sess, err := session.NewSession(awsConfig)
367	if err != nil {
368		t.Fatal(err)
369	}
370	svc := iam.New(sess)
371	createGroupInput := &iam.CreateGroupInput{
372		GroupName: aws.String(groupName),
373	}
374	log.Printf("[INFO] AWS CreateGroup: %s", groupName)
375	if _, err := svc.CreateGroup(createGroupInput); err != nil {
376		t.Fatalf("AWS CreateGroup failed: %v", err)
377	}
378
379	if len(inlinePolicy) > 0 {
380		putPolicyInput := &iam.PutGroupPolicyInput{
381			PolicyDocument: aws.String(inlinePolicy),
382			PolicyName:     aws.String("InlinePolicy"),
383			GroupName:      aws.String(groupName),
384		}
385		_, err = svc.PutGroupPolicy(putPolicyInput)
386		if err != nil {
387			t.Fatalf("AWS PutGroupPolicy failed: %v", err)
388		}
389	}
390
391	for _, mp := range managedPolicies {
392		attachGroupPolicyInput := &iam.AttachGroupPolicyInput{
393			PolicyArn: aws.String(mp),
394			GroupName: aws.String(groupName),
395		}
396		_, err = svc.AttachGroupPolicy(attachGroupPolicyInput)
397		if err != nil {
398			t.Fatalf("AWS AttachGroupPolicy failed, %v", err)
399		}
400	}
401}
402
403func deleteTestRole(roleName string) error {
404	awsConfig := &aws.Config{
405		Region:     aws.String("us-east-1"),
406		HTTPClient: cleanhttp.DefaultClient(),
407	}
408	sess, err := session.NewSession(awsConfig)
409	if err != nil {
410		return err
411	}
412	svc := iam.New(sess)
413	listAttachmentsInput := &iam.ListAttachedRolePoliciesInput{
414		RoleName: aws.String(roleName),
415	}
416	detacher := func(result *iam.ListAttachedRolePoliciesOutput, lastPage bool) bool {
417		for _, policy := range result.AttachedPolicies {
418			detachInput := &iam.DetachRolePolicyInput{
419				PolicyArn: policy.PolicyArn,
420				RoleName:  aws.String(roleName), // Required
421			}
422			_, err := svc.DetachRolePolicy(detachInput)
423			if err != nil {
424				log.Printf("[WARN] AWS DetachRolePolicy failed for policy %s: %v", *policy.PolicyArn, err)
425			}
426		}
427		return true
428	}
429	if err := svc.ListAttachedRolePoliciesPages(listAttachmentsInput, detacher); err != nil {
430		log.Printf("[WARN] AWS DetachRolePolicy failed: %v", err)
431	}
432
433	params := &iam.DeleteRoleInput{
434		RoleName: aws.String(roleName),
435	}
436
437	log.Printf("[INFO] AWS DeleteRole: %s", roleName)
438	_, err = svc.DeleteRole(params)
439
440	if err != nil {
441		log.Printf("[WARN] AWS DeleteRole failed: %v", err)
442		return err
443	}
444	return nil
445}
446
447func deleteTestUser(accessKey *awsAccessKey, userName string) error {
448	awsConfig := &aws.Config{
449		Region:     aws.String("us-east-1"),
450		HTTPClient: cleanhttp.DefaultClient(),
451	}
452	sess, err := session.NewSession(awsConfig)
453	if err != nil {
454		return err
455	}
456	svc := iam.New(sess)
457	userDetachment := &iam.DetachUserPolicyInput{
458		PolicyArn: aws.String("arn:aws:iam::aws:policy/AdministratorAccess"),
459		UserName:  aws.String(userName),
460	}
461	if _, err := svc.DetachUserPolicy(userDetachment); err != nil {
462		log.Printf("[WARN] AWS DetachUserPolicy failed: %v", err)
463		return err
464	}
465
466	deleteAccessKeyInput := &iam.DeleteAccessKeyInput{
467		AccessKeyId: aws.String(accessKey.AccessKeyID),
468		UserName:    aws.String(userName),
469	}
470	_, err = svc.DeleteAccessKey(deleteAccessKeyInput)
471	if err != nil {
472		log.Printf("[WARN] AWS DeleteAccessKey failed: %v", err)
473		return err
474	}
475
476	deleteTestUserPolicyInput := &iam.DeleteUserPolicyInput{
477		PolicyName: aws.String("SelfDestructionTimebomb"),
478		UserName:   aws.String(userName),
479	}
480	_, err = svc.DeleteUserPolicy(deleteTestUserPolicyInput)
481	if err != nil {
482		log.Printf("[WARN] AWS DeleteUserPolicy failed: %v", err)
483		return err
484	}
485	deleteTestUserInput := &iam.DeleteUserInput{
486		UserName: aws.String(userName),
487	}
488	log.Printf("[INFO] AWS DeleteUser: %s", userName)
489	_, err = svc.DeleteUser(deleteTestUserInput)
490	if err != nil {
491		log.Printf("[WARN] AWS DeleteUser failed: %v", err)
492		return err
493	}
494
495	return nil
496}
497
498func deleteTestGroup(groupName string) error {
499	awsConfig := &aws.Config{
500		Region:     aws.String("us-east-1"),
501		HTTPClient: cleanhttp.DefaultClient(),
502	}
503	sess, err := session.NewSession(awsConfig)
504	if err != nil {
505		return err
506	}
507	svc := iam.New(sess)
508
509	// Detach any managed group policies
510	getGroupsInput := &iam.ListAttachedGroupPoliciesInput{
511		GroupName: aws.String(groupName),
512	}
513	getGroupsOutput, err := svc.ListAttachedGroupPolicies(getGroupsInput)
514	if err != nil {
515		log.Printf("[WARN] AWS ListAttachedGroupPolicies failed: %v", err)
516		return err
517	}
518	for _, g := range getGroupsOutput.AttachedPolicies {
519		detachGroupInput := &iam.DetachGroupPolicyInput{
520			GroupName: aws.String(groupName),
521			PolicyArn: g.PolicyArn,
522		}
523		if _, err := svc.DetachGroupPolicy(detachGroupInput); err != nil {
524			log.Printf("[WARN] AWS DetachGroupPolicy failed: %v", err)
525			return err
526		}
527	}
528
529	// Remove any inline policies
530	listGroupPoliciesInput := &iam.ListGroupPoliciesInput{
531		GroupName: aws.String(groupName),
532	}
533	listGroupPoliciesOutput, err := svc.ListGroupPolicies(listGroupPoliciesInput)
534	if err != nil {
535		log.Printf("[WARN] AWS ListGroupPolicies failed: %v", err)
536		return err
537	}
538	for _, g := range listGroupPoliciesOutput.PolicyNames {
539		deleteGroupPolicyInput := &iam.DeleteGroupPolicyInput{
540			GroupName:  aws.String(groupName),
541			PolicyName: g,
542		}
543		if _, err := svc.DeleteGroupPolicy(deleteGroupPolicyInput); err != nil {
544			log.Printf("[WARN] AWS DeleteGroupPolicy failed: %v", err)
545			return err
546		}
547	}
548
549	// Delete the group
550	deleteTestGroupInput := &iam.DeleteGroupInput{
551		GroupName: aws.String(groupName),
552	}
553	log.Printf("[INFO] AWS DeleteGroup: %s", groupName)
554	_, err = svc.DeleteGroup(deleteTestGroupInput)
555	if err != nil {
556		log.Printf("[WARN] AWS DeleteGroup failed: %v", err)
557		return err
558	}
559
560	return nil
561}
562
563func testAccStepConfig(t *testing.T) logicaltest.TestStep {
564	return logicaltest.TestStep{
565		Operation: logical.UpdateOperation,
566		Path:      "config/root",
567		Data: map[string]interface{}{
568			"region": os.Getenv("AWS_DEFAULT_REGION"),
569		},
570	}
571}
572
573func testAccStepConfigWithCreds(t *testing.T, accessKey *awsAccessKey) logicaltest.TestStep {
574	return logicaltest.TestStep{
575		Operation: logical.UpdateOperation,
576		Path:      "config/root",
577		Data: map[string]interface{}{
578			"region": os.Getenv("AWS_DEFAULT_REGION"),
579		},
580		PreFlight: func(req *logical.Request) error {
581			// Values in Data above get eagerly evaluated due to the testing framework.
582			// In particular, they get evaluated before accessKey gets set by CreateUser
583			// and thus would fail. By moving to a closure in a PreFlight, we ensure that
584			// the creds get evaluated lazily after they've been properly set
585			req.Data["access_key"] = accessKey.AccessKeyID
586			req.Data["secret_key"] = accessKey.SecretAccessKey
587			return nil
588		},
589	}
590}
591
592func testAccStepRotateRoot(oldAccessKey *awsAccessKey) logicaltest.TestStep {
593	return logicaltest.TestStep{
594		Operation: logical.UpdateOperation,
595		Path:      "config/rotate-root",
596		Check: func(resp *logical.Response) error {
597			if resp == nil {
598				return fmt.Errorf("received nil response from config/rotate-root")
599			}
600			newAccessKeyID := resp.Data["access_key"].(string)
601			if newAccessKeyID == oldAccessKey.AccessKeyID {
602				return fmt.Errorf("rotate-root didn't rotate access key")
603			}
604			awsConfig := &aws.Config{
605				Region:      aws.String("us-east-1"),
606				HTTPClient:  cleanhttp.DefaultClient(),
607				Credentials: credentials.NewStaticCredentials(oldAccessKey.AccessKeyID, oldAccessKey.SecretAccessKey, ""),
608			}
609			// sigh....
610			oldAccessKey.AccessKeyID = newAccessKeyID
611			log.Println("[WARN] Sleeping for 10 seconds waiting for AWS...")
612			time.Sleep(10 * time.Second)
613			sess, err := session.NewSession(awsConfig)
614			if err != nil {
615				return err
616			}
617			svc := sts.New(sess)
618			params := &sts.GetCallerIdentityInput{}
619			if _, err := svc.GetCallerIdentity(params); err == nil {
620				return fmt.Errorf("bad: old credentials succeeded after rotate")
621			}
622			if aerr, ok := err.(awserr.Error); ok {
623				if aerr.Code() != "InvalidClientTokenId" {
624					return fmt.Errorf("Unknown error returned from AWS: %#v", aerr)
625				}
626				return nil
627			}
628			return err
629		},
630	}
631}
632
633func testAccStepRead(t *testing.T, path, name string, credentialTests []credentialTestFunc) logicaltest.TestStep {
634	return logicaltest.TestStep{
635		Operation: logical.ReadOperation,
636		Path:      path + "/" + name,
637		Check: func(resp *logical.Response) error {
638			var d struct {
639				AccessKey string `mapstructure:"access_key"`
640				SecretKey string `mapstructure:"secret_key"`
641				STSToken  string `mapstructure:"security_token"`
642			}
643			if err := mapstructure.Decode(resp.Data, &d); err != nil {
644				return err
645			}
646			log.Printf("[WARN] Generated credentials: %v", d)
647			for _, test := range credentialTests {
648				err := test(d.AccessKey, d.SecretKey, d.STSToken)
649				if err != nil {
650					return err
651				}
652			}
653			return nil
654		},
655	}
656}
657
658func testAccStepReadTTL(name string, maximumTTL time.Duration) logicaltest.TestStep {
659	return logicaltest.TestStep{
660		Operation: logical.ReadOperation,
661		Path:      "creds/" + name,
662		Check: func(resp *logical.Response) error {
663			if resp.Secret == nil {
664				return fmt.Errorf("bad: nil Secret returned")
665			}
666			ttl := resp.Secret.TTL
667			if ttl > maximumTTL {
668				return fmt.Errorf("bad: ttl of %d greater than maximum of %d", ttl/time.Second, maximumTTL/time.Second)
669			}
670			return nil
671		},
672	}
673}
674
675func describeInstancesTest(accessKey, secretKey, token string) error {
676	creds := credentials.NewStaticCredentials(accessKey, secretKey, token)
677	awsConfig := &aws.Config{
678		Credentials: creds,
679		Region:      aws.String("us-east-1"),
680		HTTPClient:  cleanhttp.DefaultClient(),
681	}
682	sess, err := session.NewSession(awsConfig)
683	if err != nil {
684		return err
685	}
686	client := ec2.New(sess)
687	log.Printf("[WARN] Verifying that the generated credentials work with ec2:DescribeInstances...")
688	return retryUntilSuccess(func() error {
689		_, err := client.DescribeInstances(&ec2.DescribeInstancesInput{})
690		return err
691	})
692}
693
694func describeAzsTestUnauthorized(accessKey, secretKey, token string) error {
695	creds := credentials.NewStaticCredentials(accessKey, secretKey, token)
696	awsConfig := &aws.Config{
697		Credentials: creds,
698		Region:      aws.String("us-east-1"),
699		HTTPClient:  cleanhttp.DefaultClient(),
700	}
701	sess, err := session.NewSession(awsConfig)
702	if err != nil {
703		return err
704	}
705	client := ec2.New(sess)
706	log.Printf("[WARN] Verifying that the generated credentials don't work with ec2:DescribeAvailabilityZones...")
707	return retryUntilSuccess(func() error {
708		_, err := client.DescribeAvailabilityZones(&ec2.DescribeAvailabilityZonesInput{})
709		// Need to make sure AWS authenticates the generated credentials but does not authorize the operation
710		if err == nil {
711			return fmt.Errorf("operation succeeded when expected failure")
712		}
713		if aerr, ok := err.(awserr.Error); ok {
714			if aerr.Code() == "UnauthorizedOperation" {
715				return nil
716			}
717		}
718		return err
719	})
720}
721
722func assertCreatedIAMUser(accessKey, secretKey, token string) error {
723	creds := credentials.NewStaticCredentials(accessKey, secretKey, token)
724	awsConfig := &aws.Config{
725		Credentials: creds,
726		Region:      aws.String("us-east-1"),
727		HTTPClient:  cleanhttp.DefaultClient(),
728	}
729	sess, err := session.NewSession(awsConfig)
730	if err != nil {
731		return err
732	}
733	client := iam.New(sess)
734	log.Printf("[WARN] Checking if IAM User is created properly...")
735	userOutput, err := client.GetUser(&iam.GetUserInput{})
736	if err != nil {
737		return err
738	}
739
740	if *userOutput.User.Path != "/path/" {
741		return fmt.Errorf("bad: got: %#v\nexpected: %#v", userOutput.User.Path, "/path/")
742	}
743
744	return nil
745}
746
747func listIamUsersTest(accessKey, secretKey, token string) error {
748	creds := credentials.NewStaticCredentials(accessKey, secretKey, token)
749	awsConfig := &aws.Config{
750		Credentials: creds,
751		Region:      aws.String("us-east-1"),
752		HTTPClient:  cleanhttp.DefaultClient(),
753	}
754	sess, err := session.NewSession(awsConfig)
755	if err != nil {
756		return err
757	}
758	client := iam.New(sess)
759	log.Printf("[WARN] Verifying that the generated credentials work with iam:ListUsers...")
760	return retryUntilSuccess(func() error {
761		_, err := client.ListUsers(&iam.ListUsersInput{})
762		return err
763	})
764}
765
766func listDynamoTablesTest(accessKey, secretKey, token string) error {
767	creds := credentials.NewStaticCredentials(accessKey, secretKey, token)
768	awsConfig := &aws.Config{
769		Credentials: creds,
770		Region:      aws.String("us-east-1"),
771		HTTPClient:  cleanhttp.DefaultClient(),
772	}
773	sess, err := session.NewSession(awsConfig)
774	if err != nil {
775		return err
776	}
777	client := dynamodb.New(sess)
778	log.Printf("[WARN] Verifying that the generated credentials work with dynamodb:ListTables...")
779	return retryUntilSuccess(func() error {
780		_, err := client.ListTables(&dynamodb.ListTablesInput{})
781		return err
782	})
783}
784
785func listS3BucketsTest(accessKey, secretKey, token string) error {
786	creds := credentials.NewStaticCredentials(accessKey, secretKey, token)
787	awsConfig := &aws.Config{
788		Credentials: creds,
789		Region:      aws.String("us-east-1"),
790		HTTPClient:  cleanhttp.DefaultClient(),
791	}
792	sess, err := session.NewSession(awsConfig)
793	if err != nil {
794		return err
795	}
796	client := s3.New(sess)
797	log.Printf("[WARN] Verifying that the generated credentials work with s3:ListBuckets...")
798	return retryUntilSuccess(func() error {
799		_, err := client.ListBuckets(&s3.ListBucketsInput{})
800		return err
801	})
802}
803
804func retryUntilSuccess(op func() error) error {
805	retryCount := 0
806	success := false
807	var err error
808	for !success && retryCount < 10 {
809		err = op()
810		if err == nil {
811			return nil
812		}
813		time.Sleep(time.Second)
814		retryCount++
815	}
816	return err
817}
818
819func testAccStepReadSTSWithArnPolicy(t *testing.T, name string) logicaltest.TestStep {
820	return logicaltest.TestStep{
821		Operation: logical.ReadOperation,
822		Path:      "sts/" + name,
823		ErrorOk:   true,
824		Check: func(resp *logical.Response) error {
825			if resp.Data["error"] !=
826				"attempted to retrieve iam_user credentials through the sts path; this is not allowed for legacy roles" {
827				t.Fatalf("bad: %v", resp)
828			}
829			return nil
830		},
831	}
832}
833
834func testAccStepWritePolicy(t *testing.T, name string, policy string) logicaltest.TestStep {
835	return logicaltest.TestStep{
836		Operation: logical.UpdateOperation,
837		Path:      "roles/" + name,
838		Data: map[string]interface{}{
839			"policy": policy,
840		},
841	}
842}
843
844func testAccStepDeletePolicy(t *testing.T, n string) logicaltest.TestStep {
845	return logicaltest.TestStep{
846		Operation: logical.DeleteOperation,
847		Path:      "roles/" + n,
848	}
849}
850
851func testAccStepReadPolicy(t *testing.T, name string, value string) logicaltest.TestStep {
852	return logicaltest.TestStep{
853		Operation: logical.ReadOperation,
854		Path:      "roles/" + name,
855		Check: func(resp *logical.Response) error {
856			if resp == nil {
857				if value == "" {
858					return nil
859				}
860
861				return fmt.Errorf("bad: %#v", resp)
862			}
863
864			expected := map[string]interface{}{
865				"policy_arns":              []string(nil),
866				"role_arns":                []string(nil),
867				"policy_document":          value,
868				"credential_type":          strings.Join([]string{iamUserCred, federationTokenCred}, ","),
869				"default_sts_ttl":          int64(0),
870				"max_sts_ttl":              int64(0),
871				"user_path":                "",
872				"permissions_boundary_arn": "",
873				"iam_groups":               []string(nil),
874			}
875			if !reflect.DeepEqual(resp.Data, expected) {
876				return fmt.Errorf("bad: got: %#v\nexpected: %#v", resp.Data, expected)
877			}
878			return nil
879		},
880	}
881}
882
883const testDynamoPolicy = `{
884    "Version": "2012-10-17",
885    "Statement": [
886        {
887            "Sid": "Stmt1426528957000",
888            "Effect": "Allow",
889            "Action": [
890                "dynamodb:List*"
891            ],
892            "Resource": [
893                "*"
894            ]
895        }
896    ]
897}
898`
899
900const testS3Policy = `{
901    "Version": "2012-10-17",
902    "Statement": [
903        {
904            "Effect": "Allow",
905            "Action": [
906                "s3:Get*",
907                "s3:List*"
908            ],
909            "Resource": "*"
910        }
911    ]
912}`
913
914const (
915	adminAccessPolicyArn = "arn:aws:iam::aws:policy/AdministratorAccess"
916	ec2PolicyArn         = "arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess"
917	iamPolicyArn         = "arn:aws:iam::aws:policy/IAMReadOnlyAccess"
918	dynamoPolicyArn      = "arn:aws:iam::aws:policy/AmazonDynamoDBReadOnlyAccess"
919)
920
921func testAccStepWriteRole(t *testing.T, name string, data map[string]interface{}) logicaltest.TestStep {
922	return logicaltest.TestStep{
923		Operation: logical.UpdateOperation,
924		Path:      "roles/" + name,
925		Data:      data,
926	}
927}
928
929func testAccStepReadRole(t *testing.T, name string, expected map[string]interface{}) logicaltest.TestStep {
930	return logicaltest.TestStep{
931		Operation: logical.ReadOperation,
932		Path:      "roles/" + name,
933		Check: func(resp *logical.Response) error {
934			if resp == nil {
935				if expected == nil {
936					return nil
937				}
938				return fmt.Errorf("bad: nil response")
939			}
940			if !reflect.DeepEqual(resp.Data, expected) {
941				return fmt.Errorf("bad: got %#v\nexpected: %#v", resp.Data, expected)
942			}
943			return nil
944		},
945	}
946}
947
948func testAccStepWriteArnPolicyRef(t *testing.T, name string, arn string) logicaltest.TestStep {
949	return logicaltest.TestStep{
950		Operation: logical.UpdateOperation,
951		Path:      "roles/" + name,
952		Data: map[string]interface{}{
953			"arn": ec2PolicyArn,
954		},
955	}
956}
957
958func TestBackend_basicPolicyArnRef(t *testing.T) {
959	t.Parallel()
960	logicaltest.Test(t, logicaltest.TestCase{
961		AcceptanceTest: true,
962		PreCheck:       func() { testAccPreCheck(t) },
963		LogicalBackend: getBackend(t),
964		Steps: []logicaltest.TestStep{
965			testAccStepConfig(t),
966			testAccStepWriteArnPolicyRef(t, "test", ec2PolicyArn),
967			testAccStepRead(t, "creds", "test", []credentialTestFunc{describeInstancesTest}),
968		},
969	})
970}
971
972func TestBackend_iamUserManagedInlinePoliciesGroups(t *testing.T) {
973	t.Parallel()
974	compacted, err := compactJSON(testDynamoPolicy)
975	if err != nil {
976		t.Fatalf("bad: %#v", err)
977	}
978	groupName := generateUniqueName(t.Name())
979	roleData := map[string]interface{}{
980		"policy_document": testDynamoPolicy,
981		"policy_arns":     []string{ec2PolicyArn, iamPolicyArn},
982		"iam_groups":      []string{groupName},
983		"credential_type": iamUserCred,
984		"user_path":       "/path/",
985	}
986	expectedRoleData := map[string]interface{}{
987		"policy_document":          compacted,
988		"policy_arns":              []string{ec2PolicyArn, iamPolicyArn},
989		"credential_type":          iamUserCred,
990		"role_arns":                []string(nil),
991		"default_sts_ttl":          int64(0),
992		"max_sts_ttl":              int64(0),
993		"user_path":                "/path/",
994		"permissions_boundary_arn": "",
995		"iam_groups":               []string{groupName},
996	}
997
998	logicaltest.Test(t, logicaltest.TestCase{
999		AcceptanceTest: true,
1000		PreCheck: func() {
1001			testAccPreCheck(t)
1002			createGroup(t, groupName, testS3Policy, []string{})
1003		},
1004		LogicalBackend: getBackend(t),
1005		Steps: []logicaltest.TestStep{
1006			testAccStepConfig(t),
1007			testAccStepWriteRole(t, "test", roleData),
1008			testAccStepReadRole(t, "test", expectedRoleData),
1009			testAccStepRead(t, "creds", "test", []credentialTestFunc{describeInstancesTest, listIamUsersTest, listDynamoTablesTest, assertCreatedIAMUser, listS3BucketsTest}),
1010			testAccStepRead(t, "sts", "test", []credentialTestFunc{describeInstancesTest, listIamUsersTest, listDynamoTablesTest, listS3BucketsTest}),
1011		},
1012		Teardown: func() error {
1013			return deleteTestGroup(groupName)
1014		},
1015	})
1016}
1017
1018// Similar to TestBackend_iamUserManagedInlinePoliciesGroups() but managing
1019// policies only with groups
1020func TestBackend_iamUserGroups(t *testing.T) {
1021	t.Parallel()
1022	group1Name := generateUniqueName(t.Name())
1023	group2Name := generateUniqueName(t.Name())
1024	roleData := map[string]interface{}{
1025		"iam_groups":      []string{group1Name, group2Name},
1026		"credential_type": iamUserCred,
1027		"user_path":       "/path/",
1028	}
1029	expectedRoleData := map[string]interface{}{
1030		"policy_document":          "",
1031		"policy_arns":              []string(nil),
1032		"credential_type":          iamUserCred,
1033		"role_arns":                []string(nil),
1034		"default_sts_ttl":          int64(0),
1035		"max_sts_ttl":              int64(0),
1036		"user_path":                "/path/",
1037		"permissions_boundary_arn": "",
1038		"iam_groups":               []string{group1Name, group2Name},
1039	}
1040
1041	logicaltest.Test(t, logicaltest.TestCase{
1042		AcceptanceTest: true,
1043		PreCheck: func() {
1044			testAccPreCheck(t)
1045			createGroup(t, group1Name, testS3Policy, []string{ec2PolicyArn, iamPolicyArn})
1046			createGroup(t, group2Name, testDynamoPolicy, []string{})
1047		},
1048		LogicalBackend: getBackend(t),
1049		Steps: []logicaltest.TestStep{
1050			testAccStepConfig(t),
1051			testAccStepWriteRole(t, "test", roleData),
1052			testAccStepReadRole(t, "test", expectedRoleData),
1053			testAccStepRead(t, "creds", "test", []credentialTestFunc{describeInstancesTest, listIamUsersTest, listDynamoTablesTest, assertCreatedIAMUser, listS3BucketsTest}),
1054			testAccStepRead(t, "sts", "test", []credentialTestFunc{describeInstancesTest, listIamUsersTest, listDynamoTablesTest, listS3BucketsTest}),
1055		},
1056		Teardown: func() error {
1057			if err := deleteTestGroup(group1Name); err != nil {
1058				return err
1059			}
1060			return deleteTestGroup(group2Name)
1061		},
1062	})
1063}
1064
1065func TestBackend_AssumedRoleWithPolicyDoc(t *testing.T) {
1066	t.Parallel()
1067	roleName := generateUniqueName(t.Name())
1068	// This looks a bit curious. The policy document and the role document act
1069	// as a logical intersection of policies. The role allows ec2:Describe*
1070	// (among other permissions). This policy allows everything BUT
1071	// ec2:DescribeAvailabilityZones. Thus, the logical intersection of the two
1072	// is all ec2:Describe* EXCEPT ec2:DescribeAvailabilityZones, and so the
1073	// describeAZs call should fail
1074	allowAllButDescribeAzs := `
1075{
1076	"Version": "2012-10-17",
1077	"Statement": [{
1078			"Effect": "Allow",
1079			"NotAction": "ec2:DescribeAvailabilityZones",
1080			"Resource": "*"
1081	}]
1082}
1083`
1084	awsAccountID, err := getAccountID()
1085	if err != nil {
1086		t.Logf("Unable to retrive user via sts:GetCallerIdentity: %#v", err)
1087		t.Skip("Could not determine AWS account ID from sts:GetCallerIdentity for acceptance tests, skipping")
1088	}
1089	roleData := map[string]interface{}{
1090		"policy_document": allowAllButDescribeAzs,
1091		"role_arns":       []string{fmt.Sprintf("arn:aws:iam::%s:role/%s", awsAccountID, roleName)},
1092		"credential_type": assumedRoleCred,
1093	}
1094	logicaltest.Test(t, logicaltest.TestCase{
1095		AcceptanceTest: true,
1096		PreCheck: func() {
1097			testAccPreCheck(t)
1098			createRole(t, roleName, awsAccountID, []string{ec2PolicyArn})
1099			// Sleep sometime because AWS is eventually consistent
1100			log.Println("[WARN] Sleeping for 10 seconds waiting for AWS...")
1101			time.Sleep(10 * time.Second)
1102		},
1103		LogicalBackend: getBackend(t),
1104		Steps: []logicaltest.TestStep{
1105			testAccStepConfig(t),
1106			testAccStepWriteRole(t, "test", roleData),
1107			testAccStepRead(t, "sts", "test", []credentialTestFunc{describeInstancesTest, describeAzsTestUnauthorized}),
1108			testAccStepRead(t, "creds", "test", []credentialTestFunc{describeInstancesTest, describeAzsTestUnauthorized}),
1109		},
1110		Teardown: func() error {
1111			return deleteTestRole(roleName)
1112		},
1113	})
1114}
1115
1116func TestBackend_AssumedRoleWithPolicyARN(t *testing.T) {
1117	t.Parallel()
1118	roleName := generateUniqueName(t.Name())
1119
1120	awsAccountID, err := getAccountID()
1121	if err != nil {
1122		t.Logf("Unable to retrive user via sts:GetCallerIdentity: %#v", err)
1123		t.Skip("Could not determine AWS account ID from sts:GetCallerIdentity for acceptance tests, skipping")
1124	}
1125	roleData := map[string]interface{}{
1126		"policy_arns":     iamPolicyArn,
1127		"role_arns":       []string{fmt.Sprintf("arn:aws:iam::%s:role/%s", awsAccountID, roleName)},
1128		"credential_type": assumedRoleCred,
1129	}
1130	logicaltest.Test(t, logicaltest.TestCase{
1131		AcceptanceTest: true,
1132		PreCheck: func() {
1133			testAccPreCheck(t)
1134			createRole(t, roleName, awsAccountID, []string{ec2PolicyArn, iamPolicyArn})
1135			log.Printf("[WARN] Sleeping for 10 seconds waiting for AWS...")
1136			time.Sleep(10 * time.Second)
1137		},
1138		LogicalBackend: getBackend(t),
1139		Steps: []logicaltest.TestStep{
1140			testAccStepConfig(t),
1141			testAccStepWriteRole(t, "test", roleData),
1142			testAccStepRead(t, "sts", "test", []credentialTestFunc{listIamUsersTest, describeAzsTestUnauthorized}),
1143			testAccStepRead(t, "creds", "test", []credentialTestFunc{listIamUsersTest, describeAzsTestUnauthorized}),
1144		},
1145		Teardown: func() error {
1146			return deleteTestRole(roleName)
1147		},
1148	})
1149}
1150
1151func TestBackend_AssumedRoleWithGroups(t *testing.T) {
1152	t.Parallel()
1153	roleName := generateUniqueName(t.Name())
1154	groupName := generateUniqueName(t.Name())
1155	// This looks a bit curious. The policy document and the role document act
1156	// as a logical intersection of policies. The role allows ec2:Describe*
1157	// (among other permissions). This policy allows everything BUT
1158	// ec2:DescribeAvailabilityZones. Thus, the logical intersection of the two
1159	// is all ec2:Describe* EXCEPT ec2:DescribeAvailabilityZones, and so the
1160	// describeAZs call should fail
1161	allowAllButDescribeAzs := `{
1162	"Version": "2012-10-17",
1163	"Statement": [
1164		{
1165			"Effect": "Allow",
1166			"NotAction": "ec2:DescribeAvailabilityZones",
1167			"Resource": "*"
1168		}
1169	]
1170}`
1171	awsAccountID, err := getAccountID()
1172	if err != nil {
1173		t.Logf("Unable to retrive user via sts:GetCallerIdentity: %#v", err)
1174		t.Skip("Could not determine AWS account ID from sts:GetCallerIdentity for acceptance tests, skipping")
1175	}
1176
1177	roleData := map[string]interface{}{
1178		"iam_groups":      []string{groupName},
1179		"role_arns":       []string{fmt.Sprintf("arn:aws:iam::%s:role/%s", awsAccountID, roleName)},
1180		"credential_type": assumedRoleCred,
1181	}
1182	logicaltest.Test(t, logicaltest.TestCase{
1183		AcceptanceTest: true,
1184		PreCheck: func() {
1185			testAccPreCheck(t)
1186			createRole(t, roleName, awsAccountID, []string{ec2PolicyArn})
1187			createGroup(t, groupName, allowAllButDescribeAzs, []string{})
1188			// Sleep sometime because AWS is eventually consistent
1189			log.Println("[WARN] Sleeping for 10 seconds waiting for AWS...")
1190			time.Sleep(10 * time.Second)
1191		},
1192		LogicalBackend: getBackend(t),
1193		Steps: []logicaltest.TestStep{
1194			testAccStepConfig(t),
1195			testAccStepWriteRole(t, "test", roleData),
1196			testAccStepRead(t, "sts", "test", []credentialTestFunc{describeInstancesTest, describeAzsTestUnauthorized}),
1197			testAccStepRead(t, "creds", "test", []credentialTestFunc{describeInstancesTest, describeAzsTestUnauthorized}),
1198		},
1199		Teardown: func() error {
1200			if err := deleteTestGroup(groupName); err != nil {
1201				return err
1202			}
1203			return deleteTestRole(roleName)
1204		},
1205	})
1206}
1207
1208func TestBackend_FederationTokenWithPolicyARN(t *testing.T) {
1209	t.Parallel()
1210	userName := generateUniqueName(t.Name())
1211	accessKey := &awsAccessKey{}
1212
1213	roleData := map[string]interface{}{
1214		"policy_arns":     dynamoPolicyArn,
1215		"credential_type": federationTokenCred,
1216	}
1217	logicaltest.Test(t, logicaltest.TestCase{
1218		AcceptanceTest: true,
1219		PreCheck: func() {
1220			testAccPreCheck(t)
1221			createUser(t, userName, accessKey)
1222			// Sleep sometime because AWS is eventually consistent
1223			log.Println("[WARN] Sleeping for 10 seconds waiting for AWS...")
1224			time.Sleep(10 * time.Second)
1225		},
1226		LogicalBackend: getBackend(t),
1227		Steps: []logicaltest.TestStep{
1228			testAccStepConfigWithCreds(t, accessKey),
1229			testAccStepWriteRole(t, "test", roleData),
1230			testAccStepRead(t, "sts", "test", []credentialTestFunc{listDynamoTablesTest, describeAzsTestUnauthorized}),
1231			testAccStepRead(t, "creds", "test", []credentialTestFunc{listDynamoTablesTest, describeAzsTestUnauthorized}),
1232		},
1233		Teardown: func() error {
1234			return deleteTestUser(accessKey, userName)
1235		},
1236	})
1237}
1238
1239func TestBackend_FederationTokenWithGroups(t *testing.T) {
1240	t.Parallel()
1241	userName := generateUniqueName(t.Name())
1242	groupName := generateUniqueName(t.Name())
1243	accessKey := &awsAccessKey{}
1244
1245	// IAM policy where Statement is a single element, not a list
1246	iamSingleStatementPolicy := `{
1247		"Version": "2012-10-17",
1248		"Statement": {
1249			"Effect": "Allow",
1250			"Action": [
1251				"s3:Get*",
1252				"s3:List*"
1253			],
1254			"Resource": "*"
1255		}
1256	}`
1257
1258	roleData := map[string]interface{}{
1259		"iam_groups":      []string{groupName},
1260		"policy_document": iamSingleStatementPolicy,
1261		"credential_type": federationTokenCred,
1262	}
1263	logicaltest.Test(t, logicaltest.TestCase{
1264		AcceptanceTest: true,
1265		PreCheck: func() {
1266			testAccPreCheck(t)
1267			createUser(t, userName, accessKey)
1268			createGroup(t, groupName, "", []string{dynamoPolicyArn})
1269			// Sleep sometime because AWS is eventually consistent
1270			log.Println("[WARN] Sleeping for 10 seconds waiting for AWS...")
1271			time.Sleep(10 * time.Second)
1272		},
1273		LogicalBackend: getBackend(t),
1274		Steps: []logicaltest.TestStep{
1275			testAccStepConfigWithCreds(t, accessKey),
1276			testAccStepWriteRole(t, "test", roleData),
1277			testAccStepRead(t, "sts", "test", []credentialTestFunc{listDynamoTablesTest, describeAzsTestUnauthorized, listS3BucketsTest}),
1278			testAccStepRead(t, "creds", "test", []credentialTestFunc{listDynamoTablesTest, describeAzsTestUnauthorized, listS3BucketsTest}),
1279		},
1280		Teardown: func() error {
1281			if err := deleteTestGroup(groupName); err != nil {
1282				return err
1283			}
1284			return deleteTestUser(accessKey, userName)
1285		},
1286	})
1287}
1288
1289func TestBackend_RoleDefaultSTSTTL(t *testing.T) {
1290	t.Parallel()
1291	roleName := generateUniqueName(t.Name())
1292	minAwsAssumeRoleDuration := 900
1293	awsAccountID, err := getAccountID()
1294	if err != nil {
1295		t.Logf("Unable to retrive user via sts:GetCallerIdentity: %#v", err)
1296		t.Skip("Could not determine AWS account ID from sts:GetCallerIdentity for acceptance tests, skipping")
1297	}
1298	roleData := map[string]interface{}{
1299		"role_arns":       []string{fmt.Sprintf("arn:aws:iam::%s:role/%s", awsAccountID, roleName)},
1300		"credential_type": assumedRoleCred,
1301		"default_sts_ttl": minAwsAssumeRoleDuration,
1302		"max_sts_ttl":     minAwsAssumeRoleDuration,
1303	}
1304	logicaltest.Test(t, logicaltest.TestCase{
1305		AcceptanceTest: true,
1306		PreCheck: func() {
1307			testAccPreCheck(t)
1308			createRole(t, roleName, awsAccountID, []string{ec2PolicyArn})
1309			log.Println("[WARN] Sleeping for 10 seconds waiting for AWS...")
1310			time.Sleep(10 * time.Second)
1311		},
1312		LogicalBackend: getBackend(t),
1313		Steps: []logicaltest.TestStep{
1314			testAccStepConfig(t),
1315			testAccStepWriteRole(t, "test", roleData),
1316			testAccStepReadTTL("test", time.Duration(minAwsAssumeRoleDuration)*time.Second), // allow a little slack
1317		},
1318		Teardown: func() error {
1319			return deleteTestRole(roleName)
1320		},
1321	})
1322}
1323
1324func TestBackend_policyArnCrud(t *testing.T) {
1325	t.Parallel()
1326	logicaltest.Test(t, logicaltest.TestCase{
1327		AcceptanceTest: true,
1328		LogicalBackend: getBackend(t),
1329		Steps: []logicaltest.TestStep{
1330			testAccStepConfig(t),
1331			testAccStepWriteArnPolicyRef(t, "test", ec2PolicyArn),
1332			testAccStepReadArnPolicy(t, "test", ec2PolicyArn),
1333			testAccStepDeletePolicy(t, "test"),
1334			testAccStepReadArnPolicy(t, "test", ""),
1335		},
1336	})
1337}
1338
1339func testAccStepReadArnPolicy(t *testing.T, name string, value string) logicaltest.TestStep {
1340	return logicaltest.TestStep{
1341		Operation: logical.ReadOperation,
1342		Path:      "roles/" + name,
1343		Check: func(resp *logical.Response) error {
1344			if resp == nil {
1345				if value == "" {
1346					return nil
1347				}
1348
1349				return fmt.Errorf("bad: %#v", resp)
1350			}
1351
1352			expected := map[string]interface{}{
1353				"policy_arns":              []string{value},
1354				"role_arns":                []string(nil),
1355				"policy_document":          "",
1356				"credential_type":          iamUserCred,
1357				"default_sts_ttl":          int64(0),
1358				"max_sts_ttl":              int64(0),
1359				"user_path":                "",
1360				"permissions_boundary_arn": "",
1361				"iam_groups":               []string(nil),
1362			}
1363			if !reflect.DeepEqual(resp.Data, expected) {
1364				return fmt.Errorf("bad: got: %#v\nexpected: %#v", resp.Data, expected)
1365			}
1366
1367			return nil
1368		},
1369	}
1370}
1371
1372func testAccStepWriteArnRoleRef(t *testing.T, vaultRoleName, awsRoleName, awsAccountID string) logicaltest.TestStep {
1373	return logicaltest.TestStep{
1374		Operation: logical.UpdateOperation,
1375		Path:      "roles/" + vaultRoleName,
1376		Data: map[string]interface{}{
1377			"arn": fmt.Sprintf("arn:aws:iam::%s:role/%s", awsAccountID, awsRoleName),
1378		},
1379	}
1380}
1381
1382func TestBackend_iamGroupsCrud(t *testing.T) {
1383	t.Parallel()
1384	logicaltest.Test(t, logicaltest.TestCase{
1385		AcceptanceTest: true,
1386		LogicalBackend: getBackend(t),
1387		Steps: []logicaltest.TestStep{
1388			testAccStepConfig(t),
1389			testAccStepWriteIamGroups(t, "test", []string{"group1", "group2"}),
1390			testAccStepReadIamGroups(t, "test", []string{"group1", "group2"}),
1391			testAccStepDeletePolicy(t, "test"),
1392			testAccStepReadIamGroups(t, "test", []string{}),
1393		},
1394	})
1395}
1396
1397func testAccStepWriteIamGroups(t *testing.T, name string, groups []string) logicaltest.TestStep {
1398	return logicaltest.TestStep{
1399		Operation: logical.UpdateOperation,
1400		Path:      "roles/" + name,
1401		Data: map[string]interface{}{
1402			"credential_type": iamUserCred,
1403			"iam_groups":      groups,
1404		},
1405	}
1406}
1407
1408func testAccStepReadIamGroups(t *testing.T, name string, groups []string) logicaltest.TestStep {
1409	return logicaltest.TestStep{
1410		Operation: logical.ReadOperation,
1411		Path:      "roles/" + name,
1412		Check: func(resp *logical.Response) error {
1413			if resp == nil {
1414				if len(groups) == 0 {
1415					return nil
1416				}
1417
1418				return fmt.Errorf("bad: %#v", resp)
1419			}
1420
1421			expected := map[string]interface{}{
1422				"policy_arns":              []string(nil),
1423				"role_arns":                []string(nil),
1424				"policy_document":          "",
1425				"credential_type":          iamUserCred,
1426				"default_sts_ttl":          int64(0),
1427				"max_sts_ttl":              int64(0),
1428				"user_path":                "",
1429				"permissions_boundary_arn": "",
1430				"iam_groups":               groups,
1431			}
1432			if !reflect.DeepEqual(resp.Data, expected) {
1433				return fmt.Errorf("bad: got: %#v\nexpected: %#v", resp.Data, expected)
1434			}
1435
1436			return nil
1437		},
1438	}
1439}
1440
1441func TestBackend_iamTagsCrud(t *testing.T) {
1442	logicaltest.Test(t, logicaltest.TestCase{
1443		AcceptanceTest: true,
1444		LogicalBackend: getBackend(t),
1445		Steps: []logicaltest.TestStep{
1446			testAccStepConfig(t),
1447			testAccStepWriteIamTags(t, "test", map[string]string{"key1": "value1", "key2": "value2"}),
1448			testAccStepReadIamTags(t, "test", map[string]string{"key1": "value1", "key2": "value2"}),
1449			testAccStepDeletePolicy(t, "test"),
1450			testAccStepReadIamTags(t, "test", map[string]string{}),
1451		},
1452	})
1453}
1454
1455func testAccStepWriteIamTags(t *testing.T, name string, tags map[string]string) logicaltest.TestStep {
1456	return logicaltest.TestStep{
1457		Operation: logical.UpdateOperation,
1458		Path:      "roles/" + name,
1459		Data: map[string]interface{}{
1460			"credential_type": iamUserCred,
1461			"iam_tags":        tags,
1462		},
1463	}
1464}
1465
1466func testAccStepReadIamTags(t *testing.T, name string, tags map[string]string) logicaltest.TestStep {
1467	return logicaltest.TestStep{
1468		Operation: logical.ReadOperation,
1469		Path:      "roles/" + name,
1470		Check: func(resp *logical.Response) error {
1471			if resp == nil {
1472				if len(tags) == 0 {
1473					return nil
1474				}
1475
1476				return fmt.Errorf("vault response not received")
1477			}
1478
1479			expected := map[string]interface{}{
1480				"policy_arns":              []string(nil),
1481				"role_arns":                []string(nil),
1482				"policy_document":          "",
1483				"credential_type":          iamUserCred,
1484				"default_sts_ttl":          int64(0),
1485				"max_sts_ttl":              int64(0),
1486				"user_path":                "",
1487				"permissions_boundary_arn": "",
1488				"iam_groups":               []string(nil),
1489				"iam_tags":                 tags,
1490			}
1491			if !reflect.DeepEqual(resp.Data, expected) {
1492				return fmt.Errorf("bad: got: %#v\nexpected: %#v", resp.Data, expected)
1493			}
1494
1495			return nil
1496		},
1497	}
1498}
1499
1500func generateUniqueName(prefix string) string {
1501	return testhelpers.RandomWithPrefix(prefix)
1502}
1503
1504type awsAccessKey struct {
1505	AccessKeyID     string
1506	SecretAccessKey string
1507}
1508
1509type credentialTestFunc func(string, string, string) error
1510