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/sts"
24	cleanhttp "github.com/hashicorp/go-cleanhttp"
25	"github.com/hashicorp/vault/helper/testhelpers"
26	logicaltest "github.com/hashicorp/vault/helper/testhelpers/logical"
27	"github.com/hashicorp/vault/sdk/logical"
28	"github.com/mitchellh/mapstructure"
29)
30
31var initSetup sync.Once
32
33type mockIAMClient struct {
34	iamiface.IAMAPI
35}
36
37func (m *mockIAMClient) CreateUser(input *iam.CreateUserInput) (*iam.CreateUserOutput, error) {
38	return nil, awserr.New("Throttling", "", nil)
39}
40
41func getBackend(t *testing.T) logical.Backend {
42	be, _ := Factory(context.Background(), logical.TestBackendConfig())
43	return be
44}
45
46func TestBackend_basic(t *testing.T) {
47	t.Parallel()
48	logicaltest.Test(t, logicaltest.TestCase{
49		AcceptanceTest: true,
50		PreCheck:       func() { testAccPreCheck(t) },
51		LogicalBackend: getBackend(t),
52		Steps: []logicaltest.TestStep{
53			testAccStepConfig(t),
54			testAccStepWritePolicy(t, "test", testDynamoPolicy),
55			testAccStepRead(t, "creds", "test", []credentialTestFunc{listDynamoTablesTest}),
56		},
57	})
58}
59
60func TestBackend_IamUserWithPermissionsBoundary(t *testing.T) {
61	t.Parallel()
62	roleData := map[string]interface{}{
63		"credential_type":          iamUserCred,
64		"policy_arns":              adminAccessPolicyArn,
65		"permissions_boundary_arn": iamPolicyArn,
66	}
67	logicaltest.Test(t, logicaltest.TestCase{
68		AcceptanceTest: true,
69		PreCheck:       func() { testAccPreCheck(t) },
70		LogicalBackend: getBackend(t),
71		Steps: []logicaltest.TestStep{
72			testAccStepConfig(t),
73			testAccStepWriteRole(t, "test", roleData),
74			testAccStepRead(t, "creds", "test", []credentialTestFunc{listIamUsersTest, describeAzsTestUnauthorized}),
75		},
76	})
77}
78
79func TestBackend_basicSTS(t *testing.T) {
80	t.Parallel()
81	awsAccountID, err := getAccountID()
82	if err != nil {
83		t.Logf("Unable to retrive user via sts:GetCallerIdentity: %#v", err)
84		t.Skip("Could not determine AWS account ID from sts:GetCallerIdentity for acceptance tests, skipping")
85	}
86	roleName := generateUniqueName(t.Name())
87	userName := generateUniqueName(t.Name())
88	accessKey := &awsAccessKey{}
89	logicaltest.Test(t, logicaltest.TestCase{
90		AcceptanceTest: true,
91		PreCheck: func() {
92			testAccPreCheck(t)
93			createUser(t, userName, accessKey)
94			createRole(t, roleName, awsAccountID, []string{ec2PolicyArn})
95			// Sleep sometime because AWS is eventually consistent
96			// Both the createUser and createRole depend on this
97			log.Println("[WARN] Sleeping for 10 seconds waiting for AWS...")
98			time.Sleep(10 * time.Second)
99		},
100		LogicalBackend: getBackend(t),
101		Steps: []logicaltest.TestStep{
102			testAccStepConfigWithCreds(t, accessKey),
103			testAccStepRotateRoot(accessKey),
104			testAccStepWritePolicy(t, "test", testDynamoPolicy),
105			testAccStepRead(t, "sts", "test", []credentialTestFunc{listDynamoTablesTest}),
106			testAccStepWriteArnPolicyRef(t, "test", ec2PolicyArn),
107			testAccStepReadSTSWithArnPolicy(t, "test"),
108			testAccStepWriteArnRoleRef(t, "test2", roleName, awsAccountID),
109			testAccStepRead(t, "sts", "test2", []credentialTestFunc{describeInstancesTest}),
110		},
111		Teardown: func() error {
112			if err := deleteTestRole(roleName); err != nil {
113				return err
114			}
115			return deleteTestUser(accessKey, userName)
116		},
117	})
118}
119
120func TestBackend_policyCrud(t *testing.T) {
121	t.Parallel()
122	compacted, err := compactJSON(testDynamoPolicy)
123	if err != nil {
124		t.Fatalf("bad: %s", err)
125	}
126
127	logicaltest.Test(t, logicaltest.TestCase{
128		AcceptanceTest: true,
129		LogicalBackend: getBackend(t),
130		Steps: []logicaltest.TestStep{
131			testAccStepConfig(t),
132			testAccStepWritePolicy(t, "test", testDynamoPolicy),
133			testAccStepReadPolicy(t, "test", compacted),
134			testAccStepDeletePolicy(t, "test"),
135			testAccStepReadPolicy(t, "test", ""),
136		},
137	})
138}
139
140func TestBackend_throttled(t *testing.T) {
141	t.Parallel()
142	config := logical.TestBackendConfig()
143	config.StorageView = &logical.InmemStorage{}
144
145	b := Backend()
146	if err := b.Setup(context.Background(), config); err != nil {
147		t.Fatal(err)
148	}
149
150	connData := map[string]interface{}{
151		"credential_type": "iam_user",
152	}
153
154	confReq := &logical.Request{
155		Operation: logical.UpdateOperation,
156		Path:      "roles/something",
157		Storage:   config.StorageView,
158		Data:      connData,
159	}
160
161	resp, err := b.HandleRequest(context.Background(), confReq)
162	if err != nil || (resp != nil && resp.IsError()) {
163		t.Fatalf("failed to write configuration: resp:%#v err:%s", resp, err)
164	}
165
166	// Mock the IAM API call to return a throttled response to the CreateUser API
167	// call
168	b.iamClient = &mockIAMClient{}
169
170	credReq := &logical.Request{
171		Operation: logical.UpdateOperation,
172		Path:      "creds/something",
173		Storage:   config.StorageView,
174	}
175
176	credResp, err := b.HandleRequest(context.Background(), credReq)
177	if err == nil {
178		t.Fatalf("failed to trigger expected throttling error condition: resp:%#v", credResp)
179	}
180	rErr := credResp.Error()
181	expected := "Error creating IAM user: Throttling: "
182	if rErr.Error() != expected {
183		t.Fatalf("error message did not match, expected (%s), got (%s)", expected, rErr.Error())
184	}
185
186	// verify the error we got back is returned with a http.StatusBadGateway
187	code, err := logical.RespondErrorCommon(credReq, credResp, err)
188	if err == nil {
189		t.Fatal("expected error after running req/resp/err through RespondErrorCommon, got nil")
190	}
191	if code != http.StatusBadGateway {
192		t.Fatalf("expected HTTP status 'bad gateway', got: (%d)", code)
193	}
194}
195
196func testAccPreCheck(t *testing.T) {
197	initSetup.Do(func() {
198		if v := os.Getenv("AWS_DEFAULT_REGION"); v == "" {
199			log.Println("[INFO] Test: Using us-west-2 as test region")
200			os.Setenv("AWS_DEFAULT_REGION", "us-west-2")
201		}
202	})
203}
204
205func getAccountID() (string, error) {
206	awsConfig := &aws.Config{
207		Region:     aws.String("us-east-1"),
208		HTTPClient: cleanhttp.DefaultClient(),
209	}
210	svc := sts.New(session.New(awsConfig))
211
212	params := &sts.GetCallerIdentityInput{}
213	res, err := svc.GetCallerIdentity(params)
214
215	if err != nil {
216		return "", err
217	}
218	if res == nil {
219		return "", fmt.Errorf("got nil response from GetCallerIdentity")
220	}
221
222	return *res.Account, nil
223}
224
225func createRole(t *testing.T, roleName, awsAccountID string, policyARNs []string) {
226	const testRoleAssumePolicy = `{
227      "Version": "2012-10-17",
228      "Statement": [
229          {
230              "Effect":"Allow",
231              "Principal": {
232                  "AWS": "arn:aws:iam::%s:root"
233              },
234              "Action": "sts:AssumeRole"
235           }
236      ]
237}
238`
239	awsConfig := &aws.Config{
240		Region:     aws.String("us-east-1"),
241		HTTPClient: cleanhttp.DefaultClient(),
242	}
243	svc := iam.New(session.New(awsConfig))
244	trustPolicy := fmt.Sprintf(testRoleAssumePolicy, awsAccountID)
245
246	params := &iam.CreateRoleInput{
247		AssumeRolePolicyDocument: aws.String(trustPolicy),
248		RoleName:                 aws.String(roleName),
249		Path:                     aws.String("/"),
250	}
251
252	log.Printf("[INFO] AWS CreateRole: %s", roleName)
253	_, err := svc.CreateRole(params)
254
255	if err != nil {
256		t.Fatalf("AWS CreateRole failed: %v", err)
257	}
258
259	for _, policyARN := range policyARNs {
260		attachment := &iam.AttachRolePolicyInput{
261			PolicyArn: aws.String(policyARN),
262			RoleName:  aws.String(roleName), // Required
263		}
264		_, err = svc.AttachRolePolicy(attachment)
265
266		if err != nil {
267			t.Fatalf("AWS AttachRolePolicy failed: %v", err)
268		}
269	}
270}
271
272func createUser(t *testing.T, userName string, accessKey *awsAccessKey) {
273	// The sequence of user creation actions is carefully chosen to minimize
274	// impact of stolen IAM user credentials
275	// 1. Create user, without any permissions or credentials. At this point,
276	//	  nobody cares if creds compromised because this user can do nothing.
277	// 2. Attach the timebomb policy. This grants no access but puts a time limit
278	//	  on validity of compromised credentials. If this fails, nobody cares
279	//	  because the user has no permissions to do anything anyway
280	// 3. Attach the AdminAccess policy. The IAM user still has no credentials to
281	//	  do anything
282	// 4. Generate API creds to get an actual access key and secret key
283	timebombPolicyTemplate := `{
284		"Version": "2012-10-17",
285		"Statement": [
286			{
287				"Effect": "Deny",
288				"Action": "*",
289				"Resource": "*",
290				"Condition": {
291					"DateGreaterThan": {
292						"aws:CurrentTime": "%s"
293					}
294				}
295			}
296		]
297	}
298	`
299	validity := time.Duration(2 * time.Hour)
300	expiry := time.Now().Add(validity)
301	timebombPolicy := fmt.Sprintf(timebombPolicyTemplate, expiry.Format(time.RFC3339))
302	awsConfig := &aws.Config{
303		Region:     aws.String("us-east-1"),
304		HTTPClient: cleanhttp.DefaultClient(),
305	}
306	svc := iam.New(session.New(awsConfig))
307
308	createUserInput := &iam.CreateUserInput{
309		UserName: aws.String(userName),
310	}
311	log.Printf("[INFO] AWS CreateUser: %s", userName)
312	_, err := svc.CreateUser(createUserInput)
313	if err != nil {
314		t.Fatalf("AWS CreateUser failed: %v", err)
315	}
316
317	putPolicyInput := &iam.PutUserPolicyInput{
318		PolicyDocument: aws.String(timebombPolicy),
319		PolicyName:     aws.String("SelfDestructionTimebomb"),
320		UserName:       aws.String(userName),
321	}
322	_, err = svc.PutUserPolicy(putPolicyInput)
323	if err != nil {
324		t.Fatalf("AWS PutUserPolicy failed: %v", err)
325	}
326
327	attachUserPolicyInput := &iam.AttachUserPolicyInput{
328		PolicyArn: aws.String("arn:aws:iam::aws:policy/AdministratorAccess"),
329		UserName:  aws.String(userName),
330	}
331	_, err = svc.AttachUserPolicy(attachUserPolicyInput)
332	if err != nil {
333		t.Fatalf("AWS AttachUserPolicy failed, %v", err)
334	}
335
336	createAccessKeyInput := &iam.CreateAccessKeyInput{
337		UserName: aws.String(userName),
338	}
339	createAccessKeyOutput, err := svc.CreateAccessKey(createAccessKeyInput)
340	if err != nil {
341		t.Fatalf("AWS CreateAccessKey failed: %v", err)
342	}
343	if createAccessKeyOutput == nil {
344		t.Fatalf("AWS CreateAccessKey returned nil")
345	}
346	genAccessKey := createAccessKeyOutput.AccessKey
347
348	accessKey.AccessKeyID = *genAccessKey.AccessKeyId
349	accessKey.SecretAccessKey = *genAccessKey.SecretAccessKey
350}
351
352func deleteTestRole(roleName string) error {
353	awsConfig := &aws.Config{
354		Region:     aws.String("us-east-1"),
355		HTTPClient: cleanhttp.DefaultClient(),
356	}
357	svc := iam.New(session.New(awsConfig))
358
359	listAttachmentsInput := &iam.ListAttachedRolePoliciesInput{
360		RoleName: aws.String(roleName),
361	}
362	detacher := func(result *iam.ListAttachedRolePoliciesOutput, lastPage bool) bool {
363		for _, policy := range result.AttachedPolicies {
364			detachInput := &iam.DetachRolePolicyInput{
365				PolicyArn: policy.PolicyArn,
366				RoleName:  aws.String(roleName), // Required
367			}
368			_, err := svc.DetachRolePolicy(detachInput)
369			if err != nil {
370				log.Printf("[WARN] AWS DetachRolePolicy failed for policy %s: %v", *policy.PolicyArn, err)
371			}
372		}
373		return true
374	}
375	err := svc.ListAttachedRolePoliciesPages(listAttachmentsInput, detacher)
376	if err != nil {
377		log.Printf("[WARN] AWS DetachRolePolicy failed: %v", err)
378	}
379
380	params := &iam.DeleteRoleInput{
381		RoleName: aws.String(roleName),
382	}
383
384	log.Printf("[INFO] AWS DeleteRole: %s", roleName)
385	_, err = svc.DeleteRole(params)
386
387	if err != nil {
388		log.Printf("[WARN] AWS DeleteRole failed: %v", err)
389		return err
390	}
391	return nil
392}
393
394func deleteTestUser(accessKey *awsAccessKey, userName string) error {
395	awsConfig := &aws.Config{
396		Region:     aws.String("us-east-1"),
397		HTTPClient: cleanhttp.DefaultClient(),
398	}
399	svc := iam.New(session.New(awsConfig))
400
401	userDetachment := &iam.DetachUserPolicyInput{
402		PolicyArn: aws.String("arn:aws:iam::aws:policy/AdministratorAccess"),
403		UserName:  aws.String(userName),
404	}
405	_, err := svc.DetachUserPolicy(userDetachment)
406	if err != nil {
407		log.Printf("[WARN] AWS DetachUserPolicy failed: %v", err)
408		return err
409	}
410
411	deleteAccessKeyInput := &iam.DeleteAccessKeyInput{
412		AccessKeyId: aws.String(accessKey.AccessKeyID),
413		UserName:    aws.String(userName),
414	}
415	_, err = svc.DeleteAccessKey(deleteAccessKeyInput)
416	if err != nil {
417		log.Printf("[WARN] AWS DeleteAccessKey failed: %v", err)
418		return err
419	}
420
421	deleteTestUserPolicyInput := &iam.DeleteUserPolicyInput{
422		PolicyName: aws.String("SelfDestructionTimebomb"),
423		UserName:   aws.String(userName),
424	}
425	_, err = svc.DeleteUserPolicy(deleteTestUserPolicyInput)
426	if err != nil {
427		log.Printf("[WARN] AWS DeleteUserPolicy failed: %v", err)
428		return err
429	}
430	deleteTestUserInput := &iam.DeleteUserInput{
431		UserName: aws.String(userName),
432	}
433	log.Printf("[INFO] AWS DeleteUser: %s", userName)
434	_, err = svc.DeleteUser(deleteTestUserInput)
435	if err != nil {
436		log.Printf("[WARN] AWS DeleteUser failed: %v", err)
437		return err
438	}
439
440	return nil
441}
442
443func testAccStepConfig(t *testing.T) logicaltest.TestStep {
444	return logicaltest.TestStep{
445		Operation: logical.UpdateOperation,
446		Path:      "config/root",
447		Data: map[string]interface{}{
448			"region": os.Getenv("AWS_DEFAULT_REGION"),
449		},
450	}
451}
452
453func testAccStepConfigWithCreds(t *testing.T, accessKey *awsAccessKey) logicaltest.TestStep {
454	return logicaltest.TestStep{
455		Operation: logical.UpdateOperation,
456		Path:      "config/root",
457		Data: map[string]interface{}{
458			"region": os.Getenv("AWS_DEFAULT_REGION"),
459		},
460		PreFlight: func(req *logical.Request) error {
461			// Values in Data above get eagerly evaluated due to the testing framework.
462			// In particular, they get evaluated before accessKey gets set by CreateUser
463			// and thus would fail. By moving to a closure in a PreFlight, we ensure that
464			// the creds get evaluated lazily after they've been properly set
465			req.Data["access_key"] = accessKey.AccessKeyID
466			req.Data["secret_key"] = accessKey.SecretAccessKey
467			return nil
468		},
469	}
470}
471
472func testAccStepRotateRoot(oldAccessKey *awsAccessKey) logicaltest.TestStep {
473	return logicaltest.TestStep{
474		Operation: logical.UpdateOperation,
475		Path:      "config/rotate-root",
476		Check: func(resp *logical.Response) error {
477			if resp == nil {
478				return fmt.Errorf("received nil response from config/rotate-root")
479			}
480			newAccessKeyID := resp.Data["access_key"].(string)
481			if newAccessKeyID == oldAccessKey.AccessKeyID {
482				return fmt.Errorf("rotate-root didn't rotate access key")
483			}
484			awsConfig := &aws.Config{
485				Region:      aws.String("us-east-1"),
486				HTTPClient:  cleanhttp.DefaultClient(),
487				Credentials: credentials.NewStaticCredentials(oldAccessKey.AccessKeyID, oldAccessKey.SecretAccessKey, ""),
488			}
489			// sigh....
490			oldAccessKey.AccessKeyID = newAccessKeyID
491			log.Println("[WARN] Sleeping for 10 seconds waiting for AWS...")
492			time.Sleep(10 * time.Second)
493			svc := sts.New(session.New(awsConfig))
494			params := &sts.GetCallerIdentityInput{}
495			_, err := svc.GetCallerIdentity(params)
496			if err == nil {
497				return fmt.Errorf("bad: old credentials succeeded after rotate")
498			}
499			if aerr, ok := err.(awserr.Error); ok {
500				if aerr.Code() != "InvalidClientTokenId" {
501					return fmt.Errorf("Unknown error returned from AWS: %#v", aerr)
502				}
503				return nil
504			}
505			return err
506		},
507	}
508}
509
510func testAccStepRead(t *testing.T, path, name string, credentialTests []credentialTestFunc) logicaltest.TestStep {
511	return logicaltest.TestStep{
512		Operation: logical.ReadOperation,
513		Path:      path + "/" + name,
514		Check: func(resp *logical.Response) error {
515			var d struct {
516				AccessKey string `mapstructure:"access_key"`
517				SecretKey string `mapstructure:"secret_key"`
518				STSToken  string `mapstructure:"security_token"`
519			}
520			if err := mapstructure.Decode(resp.Data, &d); err != nil {
521				return err
522			}
523			log.Printf("[WARN] Generated credentials: %v", d)
524			for _, test := range credentialTests {
525				err := test(d.AccessKey, d.SecretKey, d.STSToken)
526				if err != nil {
527					return err
528				}
529			}
530			return nil
531		},
532	}
533}
534
535func testAccStepReadTTL(name string, maximumTTL time.Duration) logicaltest.TestStep {
536	return logicaltest.TestStep{
537		Operation: logical.ReadOperation,
538		Path:      "creds/" + name,
539		Check: func(resp *logical.Response) error {
540			if resp.Secret == nil {
541				return fmt.Errorf("bad: nil Secret returned")
542			}
543			ttl := resp.Secret.TTL
544			if ttl > maximumTTL {
545				return fmt.Errorf("bad: ttl of %d greater than maximum of %d", ttl/time.Second, maximumTTL/time.Second)
546			}
547			return nil
548		},
549	}
550}
551
552func describeInstancesTest(accessKey, secretKey, token string) error {
553	creds := credentials.NewStaticCredentials(accessKey, secretKey, token)
554	awsConfig := &aws.Config{
555		Credentials: creds,
556		Region:      aws.String("us-east-1"),
557		HTTPClient:  cleanhttp.DefaultClient(),
558	}
559	client := ec2.New(session.New(awsConfig))
560	log.Printf("[WARN] Verifying that the generated credentials work with ec2:DescribeInstances...")
561	return retryUntilSuccess(func() error {
562		_, err := client.DescribeInstances(&ec2.DescribeInstancesInput{})
563		return err
564	})
565}
566
567func describeAzsTestUnauthorized(accessKey, secretKey, token string) error {
568	creds := credentials.NewStaticCredentials(accessKey, secretKey, token)
569	awsConfig := &aws.Config{
570		Credentials: creds,
571		Region:      aws.String("us-east-1"),
572		HTTPClient:  cleanhttp.DefaultClient(),
573	}
574	client := ec2.New(session.New(awsConfig))
575	log.Printf("[WARN] Verifying that the generated credentials don't work with ec2:DescribeAvailabilityZones...")
576	return retryUntilSuccess(func() error {
577		_, err := client.DescribeAvailabilityZones(&ec2.DescribeAvailabilityZonesInput{})
578		// Need to make sure AWS authenticates the generated credentials but does not authorize the operation
579		if err == nil {
580			return fmt.Errorf("operation succeeded when expected failure")
581		}
582		if aerr, ok := err.(awserr.Error); ok {
583			if aerr.Code() == "UnauthorizedOperation" {
584				return nil
585			}
586		}
587		return err
588	})
589}
590
591func assertCreatedIAMUser(accessKey, secretKey, token string) error {
592	creds := credentials.NewStaticCredentials(accessKey, secretKey, token)
593	awsConfig := &aws.Config{
594		Credentials: creds,
595		Region:      aws.String("us-east-1"),
596		HTTPClient:  cleanhttp.DefaultClient(),
597	}
598	client := iam.New(session.New(awsConfig))
599	log.Printf("[WARN] Checking if IAM User is created properly...")
600	userOutput, err := client.GetUser(&iam.GetUserInput{})
601	if err != nil {
602		return err
603	}
604
605	if *userOutput.User.Path != "/path/" {
606		return fmt.Errorf("bad: got: %#v\nexpected: %#v", userOutput.User.Path, "/path/")
607	}
608
609	return nil
610}
611
612func listIamUsersTest(accessKey, secretKey, token string) error {
613	creds := credentials.NewStaticCredentials(accessKey, secretKey, token)
614	awsConfig := &aws.Config{
615		Credentials: creds,
616		Region:      aws.String("us-east-1"),
617		HTTPClient:  cleanhttp.DefaultClient(),
618	}
619	client := iam.New(session.New(awsConfig))
620	log.Printf("[WARN] Verifying that the generated credentials work with iam:ListUsers...")
621	return retryUntilSuccess(func() error {
622		_, err := client.ListUsers(&iam.ListUsersInput{})
623		return err
624	})
625}
626
627func listDynamoTablesTest(accessKey, secretKey, token string) error {
628	creds := credentials.NewStaticCredentials(accessKey, secretKey, token)
629	awsConfig := &aws.Config{
630		Credentials: creds,
631		Region:      aws.String("us-east-1"),
632		HTTPClient:  cleanhttp.DefaultClient(),
633	}
634	client := dynamodb.New(session.New(awsConfig))
635	log.Printf("[WARN] Verifying that the generated credentials work with dynamodb:ListTables...")
636	return retryUntilSuccess(func() error {
637		_, err := client.ListTables(&dynamodb.ListTablesInput{})
638		return err
639	})
640}
641
642func retryUntilSuccess(op func() error) error {
643	retryCount := 0
644	success := false
645	var err error
646	for !success && retryCount < 10 {
647		err = op()
648		if err == nil {
649			return nil
650		}
651		time.Sleep(time.Second)
652		retryCount++
653	}
654	return err
655}
656
657func testAccStepReadSTSWithArnPolicy(t *testing.T, name string) logicaltest.TestStep {
658	return logicaltest.TestStep{
659		Operation: logical.ReadOperation,
660		Path:      "sts/" + name,
661		ErrorOk:   true,
662		Check: func(resp *logical.Response) error {
663			if resp.Data["error"] !=
664				"attempted to retrieve iam_user credentials through the sts path; this is not allowed for legacy roles" {
665				t.Fatalf("bad: %v", resp)
666			}
667			return nil
668		},
669	}
670}
671
672func testAccStepWritePolicy(t *testing.T, name string, policy string) logicaltest.TestStep {
673	return logicaltest.TestStep{
674		Operation: logical.UpdateOperation,
675		Path:      "roles/" + name,
676		Data: map[string]interface{}{
677			"policy": policy,
678		},
679	}
680}
681
682func testAccStepDeletePolicy(t *testing.T, n string) logicaltest.TestStep {
683	return logicaltest.TestStep{
684		Operation: logical.DeleteOperation,
685		Path:      "roles/" + n,
686	}
687}
688
689func testAccStepReadPolicy(t *testing.T, name string, value string) logicaltest.TestStep {
690	return logicaltest.TestStep{
691		Operation: logical.ReadOperation,
692		Path:      "roles/" + name,
693		Check: func(resp *logical.Response) error {
694			if resp == nil {
695				if value == "" {
696					return nil
697				}
698
699				return fmt.Errorf("bad: %#v", resp)
700			}
701
702			expected := map[string]interface{}{
703				"policy_arns":              []string(nil),
704				"role_arns":                []string(nil),
705				"policy_document":          value,
706				"credential_type":          strings.Join([]string{iamUserCred, federationTokenCred}, ","),
707				"default_sts_ttl":          int64(0),
708				"max_sts_ttl":              int64(0),
709				"user_path":                "",
710				"permissions_boundary_arn": "",
711			}
712			if !reflect.DeepEqual(resp.Data, expected) {
713				return fmt.Errorf("bad: got: %#v\nexpected: %#v", resp.Data, expected)
714			}
715			return nil
716		},
717	}
718}
719
720const testDynamoPolicy = `{
721    "Version": "2012-10-17",
722    "Statement": [
723        {
724            "Sid": "Stmt1426528957000",
725            "Effect": "Allow",
726            "Action": [
727                "dynamodb:List*"
728            ],
729            "Resource": [
730                "*"
731            ]
732        }
733    ]
734}
735`
736
737const adminAccessPolicyArn = "arn:aws:iam::aws:policy/AdministratorAccess"
738const ec2PolicyArn = "arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess"
739const iamPolicyArn = "arn:aws:iam::aws:policy/IAMReadOnlyAccess"
740const dynamoPolicyArn = "arn:aws:iam::aws:policy/AmazonDynamoDBReadOnlyAccess"
741
742func testAccStepWriteRole(t *testing.T, name string, data map[string]interface{}) logicaltest.TestStep {
743	return logicaltest.TestStep{
744		Operation: logical.UpdateOperation,
745		Path:      "roles/" + name,
746		Data:      data,
747	}
748}
749
750func testAccStepReadRole(t *testing.T, name string, expected map[string]interface{}) logicaltest.TestStep {
751	return logicaltest.TestStep{
752		Operation: logical.ReadOperation,
753		Path:      "roles/" + name,
754		Check: func(resp *logical.Response) error {
755			if resp == nil {
756				if expected == nil {
757					return nil
758				}
759				return fmt.Errorf("bad: nil response")
760			}
761			if !reflect.DeepEqual(resp.Data, expected) {
762				return fmt.Errorf("bad: got %#v\nexpected: %#v", resp.Data, expected)
763			}
764			return nil
765		},
766	}
767}
768
769func testAccStepWriteArnPolicyRef(t *testing.T, name string, arn string) logicaltest.TestStep {
770	return logicaltest.TestStep{
771		Operation: logical.UpdateOperation,
772		Path:      "roles/" + name,
773		Data: map[string]interface{}{
774			"arn": ec2PolicyArn,
775		},
776	}
777}
778
779func TestBackend_basicPolicyArnRef(t *testing.T) {
780	t.Parallel()
781	logicaltest.Test(t, logicaltest.TestCase{
782		AcceptanceTest: true,
783		PreCheck:       func() { testAccPreCheck(t) },
784		LogicalBackend: getBackend(t),
785		Steps: []logicaltest.TestStep{
786			testAccStepConfig(t),
787			testAccStepWriteArnPolicyRef(t, "test", ec2PolicyArn),
788			testAccStepRead(t, "creds", "test", []credentialTestFunc{describeInstancesTest}),
789		},
790	})
791}
792
793func TestBackend_iamUserManagedInlinePolicies(t *testing.T) {
794	t.Parallel()
795	compacted, err := compactJSON(testDynamoPolicy)
796	if err != nil {
797		t.Fatalf("bad: %#v", err)
798	}
799	roleData := map[string]interface{}{
800		"policy_document": testDynamoPolicy,
801		"policy_arns":     []string{ec2PolicyArn, iamPolicyArn},
802		"credential_type": iamUserCred,
803		"user_path":       "/path/",
804	}
805	expectedRoleData := map[string]interface{}{
806		"policy_document":          compacted,
807		"policy_arns":              []string{ec2PolicyArn, iamPolicyArn},
808		"credential_type":          iamUserCred,
809		"role_arns":                []string(nil),
810		"default_sts_ttl":          int64(0),
811		"max_sts_ttl":              int64(0),
812		"user_path":                "/path/",
813		"permissions_boundary_arn": "",
814	}
815
816	logicaltest.Test(t, logicaltest.TestCase{
817		AcceptanceTest: true,
818		PreCheck:       func() { testAccPreCheck(t) },
819		LogicalBackend: getBackend(t),
820		Steps: []logicaltest.TestStep{
821			testAccStepConfig(t),
822			testAccStepWriteRole(t, "test", roleData),
823			testAccStepReadRole(t, "test", expectedRoleData),
824			testAccStepRead(t, "creds", "test", []credentialTestFunc{describeInstancesTest, listIamUsersTest, listDynamoTablesTest, assertCreatedIAMUser}),
825			testAccStepRead(t, "sts", "test", []credentialTestFunc{describeInstancesTest, listIamUsersTest, listDynamoTablesTest}),
826		},
827	})
828}
829
830func TestBackend_AssumedRoleWithPolicyDoc(t *testing.T) {
831	t.Parallel()
832	roleName := generateUniqueName(t.Name())
833	// This looks a bit curious. The policy document and the role document act
834	// as a logical intersection of policies. The role allows ec2:Describe*
835	// (among other permissions). This policy allows everything BUT
836	// ec2:DescribeAvailabilityZones. Thus, the logical intersection of the two
837	// is all ec2:Describe* EXCEPT ec2:DescribeAvailabilityZones, and so the
838	// describeAZs call should fail
839	allowAllButDescribeAzs := `
840{
841	"Version": "2012-10-17",
842	"Statement": [{
843			"Effect": "Allow",
844			"NotAction": "ec2:DescribeAvailabilityZones",
845			"Resource": "*"
846	}]
847}
848`
849	awsAccountID, err := getAccountID()
850	if err != nil {
851		t.Logf("Unable to retrive user via sts:GetCallerIdentity: %#v", err)
852		t.Skip("Could not determine AWS account ID from sts:GetCallerIdentity for acceptance tests, skipping")
853	}
854	roleData := map[string]interface{}{
855		"policy_document": allowAllButDescribeAzs,
856		"role_arns":       []string{fmt.Sprintf("arn:aws:iam::%s:role/%s", awsAccountID, roleName)},
857		"credential_type": assumedRoleCred,
858	}
859	logicaltest.Test(t, logicaltest.TestCase{
860		AcceptanceTest: true,
861		PreCheck: func() {
862			testAccPreCheck(t)
863			createRole(t, roleName, awsAccountID, []string{ec2PolicyArn})
864			// Sleep sometime because AWS is eventually consistent
865			log.Println("[WARN] Sleeping for 10 seconds waiting for AWS...")
866			time.Sleep(10 * time.Second)
867		},
868		LogicalBackend: getBackend(t),
869		Steps: []logicaltest.TestStep{
870			testAccStepConfig(t),
871			testAccStepWriteRole(t, "test", roleData),
872			testAccStepRead(t, "sts", "test", []credentialTestFunc{describeInstancesTest, describeAzsTestUnauthorized}),
873			testAccStepRead(t, "creds", "test", []credentialTestFunc{describeInstancesTest, describeAzsTestUnauthorized}),
874		},
875		Teardown: func() error {
876			return deleteTestRole(roleName)
877		},
878	})
879}
880
881func TestBackend_AssumedRoleWithPolicyARN(t *testing.T) {
882	t.Parallel()
883	roleName := generateUniqueName(t.Name())
884
885	awsAccountID, err := getAccountID()
886	if err != nil {
887		t.Logf("Unable to retrive user via sts:GetCallerIdentity: %#v", err)
888		t.Skip("Could not determine AWS account ID from sts:GetCallerIdentity for acceptance tests, skipping")
889	}
890	roleData := map[string]interface{}{
891		"policy_arns":     iamPolicyArn,
892		"role_arns":       []string{fmt.Sprintf("arn:aws:iam::%s:role/%s", awsAccountID, roleName)},
893		"credential_type": assumedRoleCred,
894	}
895	logicaltest.Test(t, logicaltest.TestCase{
896		AcceptanceTest: true,
897		PreCheck: func() {
898			testAccPreCheck(t)
899			createRole(t, roleName, awsAccountID, []string{ec2PolicyArn, iamPolicyArn})
900			log.Printf("[WARN] Sleeping for 10 seconds waiting for AWS...")
901			time.Sleep(10 * time.Second)
902		},
903		LogicalBackend: getBackend(t),
904		Steps: []logicaltest.TestStep{
905			testAccStepConfig(t),
906			testAccStepWriteRole(t, "test", roleData),
907			testAccStepRead(t, "sts", "test", []credentialTestFunc{listIamUsersTest, describeAzsTestUnauthorized}),
908			testAccStepRead(t, "creds", "test", []credentialTestFunc{listIamUsersTest, describeAzsTestUnauthorized}),
909		},
910		Teardown: func() error {
911			return deleteTestRole(roleName)
912		},
913	})
914}
915
916func TestBackend_FederationTokenWithPolicyARN(t *testing.T) {
917	t.Parallel()
918	userName := generateUniqueName(t.Name())
919	accessKey := &awsAccessKey{}
920
921	roleData := map[string]interface{}{
922		"policy_arns":     dynamoPolicyArn,
923		"credential_type": federationTokenCred,
924	}
925	logicaltest.Test(t, logicaltest.TestCase{
926		AcceptanceTest: true,
927		PreCheck: func() {
928			testAccPreCheck(t)
929			createUser(t, userName, accessKey)
930			// Sleep sometime because AWS is eventually consistent
931			log.Println("[WARN] Sleeping for 10 seconds waiting for AWS...")
932			time.Sleep(10 * time.Second)
933		},
934		LogicalBackend: getBackend(t),
935		Steps: []logicaltest.TestStep{
936			testAccStepConfigWithCreds(t, accessKey),
937			testAccStepWriteRole(t, "test", roleData),
938			testAccStepRead(t, "sts", "test", []credentialTestFunc{listDynamoTablesTest, describeAzsTestUnauthorized}),
939			testAccStepRead(t, "creds", "test", []credentialTestFunc{listDynamoTablesTest, describeAzsTestUnauthorized}),
940		},
941		Teardown: func() error {
942			return deleteTestUser(accessKey, userName)
943		},
944	})
945}
946
947func TestBackend_RoleDefaultSTSTTL(t *testing.T) {
948	t.Parallel()
949	roleName := generateUniqueName(t.Name())
950	minAwsAssumeRoleDuration := 900
951	awsAccountID, err := getAccountID()
952	if err != nil {
953		t.Logf("Unable to retrive user via sts:GetCallerIdentity: %#v", err)
954		t.Skip("Could not determine AWS account ID from sts:GetCallerIdentity for acceptance tests, skipping")
955	}
956	roleData := map[string]interface{}{
957		"role_arns":       []string{fmt.Sprintf("arn:aws:iam::%s:role/%s", awsAccountID, roleName)},
958		"credential_type": assumedRoleCred,
959		"default_sts_ttl": minAwsAssumeRoleDuration,
960		"max_sts_ttl":     minAwsAssumeRoleDuration,
961	}
962	logicaltest.Test(t, logicaltest.TestCase{
963		AcceptanceTest: true,
964		PreCheck: func() {
965			testAccPreCheck(t)
966			createRole(t, roleName, awsAccountID, []string{ec2PolicyArn})
967			log.Println("[WARN] Sleeping for 10 seconds waiting for AWS...")
968			time.Sleep(10 * time.Second)
969		},
970		LogicalBackend: getBackend(t),
971		Steps: []logicaltest.TestStep{
972			testAccStepConfig(t),
973			testAccStepWriteRole(t, "test", roleData),
974			testAccStepReadTTL("test", time.Duration(minAwsAssumeRoleDuration)*time.Second), // allow a little slack
975		},
976		Teardown: func() error {
977			return deleteTestRole(roleName)
978		},
979	})
980}
981
982func TestBackend_policyArnCrud(t *testing.T) {
983	t.Parallel()
984	logicaltest.Test(t, logicaltest.TestCase{
985		AcceptanceTest: true,
986		LogicalBackend: getBackend(t),
987		Steps: []logicaltest.TestStep{
988			testAccStepConfig(t),
989			testAccStepWriteArnPolicyRef(t, "test", ec2PolicyArn),
990			testAccStepReadArnPolicy(t, "test", ec2PolicyArn),
991			testAccStepDeletePolicy(t, "test"),
992			testAccStepReadArnPolicy(t, "test", ""),
993		},
994	})
995}
996
997func testAccStepReadArnPolicy(t *testing.T, name string, value string) logicaltest.TestStep {
998	return logicaltest.TestStep{
999		Operation: logical.ReadOperation,
1000		Path:      "roles/" + name,
1001		Check: func(resp *logical.Response) error {
1002			if resp == nil {
1003				if value == "" {
1004					return nil
1005				}
1006
1007				return fmt.Errorf("bad: %#v", resp)
1008			}
1009
1010			expected := map[string]interface{}{
1011				"policy_arns":              []string{value},
1012				"role_arns":                []string(nil),
1013				"policy_document":          "",
1014				"credential_type":          iamUserCred,
1015				"default_sts_ttl":          int64(0),
1016				"max_sts_ttl":              int64(0),
1017				"user_path":                "",
1018				"permissions_boundary_arn": "",
1019			}
1020			if !reflect.DeepEqual(resp.Data, expected) {
1021				return fmt.Errorf("bad: got: %#v\nexpected: %#v", resp.Data, expected)
1022			}
1023
1024			return nil
1025		},
1026	}
1027}
1028
1029func testAccStepWriteArnRoleRef(t *testing.T, vaultRoleName, awsRoleName, awsAccountID string) logicaltest.TestStep {
1030	return logicaltest.TestStep{
1031		Operation: logical.UpdateOperation,
1032		Path:      "roles/" + vaultRoleName,
1033		Data: map[string]interface{}{
1034			"arn": fmt.Sprintf("arn:aws:iam::%s:role/%s", awsAccountID, awsRoleName),
1035		},
1036	}
1037}
1038
1039func generateUniqueName(prefix string) string {
1040	return testhelpers.RandomWithPrefix(prefix)
1041}
1042
1043type awsAccessKey struct {
1044	AccessKeyID     string
1045	SecretAccessKey string
1046}
1047
1048type credentialTestFunc func(string, string, string) error
1049