mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 18:48:08 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			449 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			449 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package aws
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"reflect"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"testing"
 | |
| 
 | |
| 	"github.com/hashicorp/vault/sdk/logical"
 | |
| )
 | |
| 
 | |
| const adminAccessPolicyARN = "arn:aws:iam::aws:policy/AdministratorAccess"
 | |
| 
 | |
| func TestBackend_PathListRoles(t *testing.T) {
 | |
| 	var resp *logical.Response
 | |
| 	var err error
 | |
| 	config := logical.TestBackendConfig()
 | |
| 	config.StorageView = &logical.InmemStorage{}
 | |
| 
 | |
| 	b := Backend()
 | |
| 	if err := b.Setup(context.Background(), config); err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 
 | |
| 	roleData := map[string]interface{}{
 | |
| 		"role_arns":       []string{"arn:aws:iam::123456789012:role/path/RoleName"},
 | |
| 		"credential_type": assumedRoleCred,
 | |
| 		"default_sts_ttl": 3600,
 | |
| 		"max_sts_ttl":     3600,
 | |
| 	}
 | |
| 
 | |
| 	roleReq := &logical.Request{
 | |
| 		Operation: logical.UpdateOperation,
 | |
| 		Storage:   config.StorageView,
 | |
| 		Data:      roleData,
 | |
| 	}
 | |
| 
 | |
| 	for i := 1; i <= 10; i++ {
 | |
| 		roleReq.Path = "roles/testrole" + strconv.Itoa(i)
 | |
| 		resp, err = b.HandleRequest(context.Background(), roleReq)
 | |
| 		if err != nil || (resp != nil && resp.IsError()) {
 | |
| 			t.Fatalf("bad: role creation failed. resp:%#v\n err:%v", resp, err)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	resp, err = b.HandleRequest(context.Background(), &logical.Request{
 | |
| 		Operation: logical.ListOperation,
 | |
| 		Path:      "roles",
 | |
| 		Storage:   config.StorageView,
 | |
| 	})
 | |
| 	if err != nil || (resp != nil && resp.IsError()) {
 | |
| 		t.Fatalf("bad: listing roles failed. resp:%#v\n err:%v", resp, err)
 | |
| 	}
 | |
| 
 | |
| 	if len(resp.Data["keys"].([]string)) != 10 {
 | |
| 		t.Fatalf("failed to list all 10 roles")
 | |
| 	}
 | |
| 
 | |
| 	resp, err = b.HandleRequest(context.Background(), &logical.Request{
 | |
| 		Operation: logical.ListOperation,
 | |
| 		Path:      "roles/",
 | |
| 		Storage:   config.StorageView,
 | |
| 	})
 | |
| 	if err != nil || (resp != nil && resp.IsError()) {
 | |
| 		t.Fatalf("bad: listing roles failed. resp:%#v\n err:%v", resp, err)
 | |
| 	}
 | |
| 
 | |
| 	if len(resp.Data["keys"].([]string)) != 10 {
 | |
| 		t.Fatalf("failed to list all 10 roles")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestUpgradeLegacyPolicyEntry(t *testing.T) {
 | |
| 	var input string
 | |
| 	var expected awsRoleEntry
 | |
| 	var output *awsRoleEntry
 | |
| 
 | |
| 	input = "arn:aws:iam::123456789012:role/path/RoleName"
 | |
| 	expected = awsRoleEntry{
 | |
| 		CredentialTypes:          []string{assumedRoleCred},
 | |
| 		RoleArns:                 []string{input},
 | |
| 		ProhibitFlexibleCredPath: true,
 | |
| 		Version:                  1,
 | |
| 	}
 | |
| 	output = upgradeLegacyPolicyEntry(input)
 | |
| 	if output.InvalidData != "" {
 | |
| 		t.Fatalf("bad: error processing upgrade of %q: got invalid data of %v", input, output.InvalidData)
 | |
| 	}
 | |
| 	if !reflect.DeepEqual(*output, expected) {
 | |
| 		t.Fatalf("bad: expected %#v; received %#v", expected, *output)
 | |
| 	}
 | |
| 
 | |
| 	input = "arn:aws:iam::123456789012:policy/MyPolicy"
 | |
| 	expected = awsRoleEntry{
 | |
| 		CredentialTypes:          []string{iamUserCred},
 | |
| 		PolicyArns:               []string{input},
 | |
| 		ProhibitFlexibleCredPath: true,
 | |
| 		Version:                  1,
 | |
| 	}
 | |
| 	output = upgradeLegacyPolicyEntry(input)
 | |
| 	if output.InvalidData != "" {
 | |
| 		t.Fatalf("bad: error processing upgrade of %q: got invalid data of %v", input, output.InvalidData)
 | |
| 	}
 | |
| 	if !reflect.DeepEqual(*output, expected) {
 | |
| 		t.Fatalf("bad: expected %#v; received %#v", expected, *output)
 | |
| 	}
 | |
| 
 | |
| 	input = "arn:aws:iam::aws:policy/AWSManagedPolicy"
 | |
| 	expected.PolicyArns = []string{input}
 | |
| 	output = upgradeLegacyPolicyEntry(input)
 | |
| 	if output.InvalidData != "" {
 | |
| 		t.Fatalf("bad: error processing upgrade of %q: got invalid data of %v", input, output.InvalidData)
 | |
| 	}
 | |
| 	if !reflect.DeepEqual(*output, expected) {
 | |
| 		t.Fatalf("bad: expected %#v; received %#v", expected, *output)
 | |
| 	}
 | |
| 
 | |
| 	input = `
 | |
| {
 | |
| 	"Version": "2012-10-07",
 | |
| 	"Statement": [
 | |
| 		{
 | |
| 			"Effect": "Allow",
 | |
| 			"Action": "ec2:Describe*",
 | |
| 			"Resource": "*"
 | |
| 		}
 | |
| 	]
 | |
| }`
 | |
| 	compacted, err := compactJSON(input)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("error parsing JSON: %v", err)
 | |
| 	}
 | |
| 	expected = awsRoleEntry{
 | |
| 		CredentialTypes:          []string{iamUserCred, federationTokenCred},
 | |
| 		PolicyDocument:           compacted,
 | |
| 		ProhibitFlexibleCredPath: true,
 | |
| 		Version:                  1,
 | |
| 	}
 | |
| 	output = upgradeLegacyPolicyEntry(input)
 | |
| 	if output.InvalidData != "" {
 | |
| 		t.Fatalf("bad: error processing upgrade of %q: got invalid data of %v", input, output.InvalidData)
 | |
| 	}
 | |
| 	if !reflect.DeepEqual(*output, expected) {
 | |
| 		t.Fatalf("bad: expected %#v; received %#v", expected, *output)
 | |
| 	}
 | |
| 
 | |
| 	// Due to lack of prior input validation, this could exist in the storage, and we need
 | |
| 	// to be able to read it out in some fashion, so have to handle this in a poor fashion
 | |
| 	input = "arn:gobbledygook"
 | |
| 	expected = awsRoleEntry{
 | |
| 		InvalidData: input,
 | |
| 		Version:     1,
 | |
| 	}
 | |
| 	output = upgradeLegacyPolicyEntry(input)
 | |
| 	if !reflect.DeepEqual(*output, expected) {
 | |
| 		t.Fatalf("bad: expected %#v; received %#v", expected, *output)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestUserPathValidity(t *testing.T) {
 | |
| 	testCases := []struct {
 | |
| 		description string
 | |
| 		userPath    string
 | |
| 		isValid     bool
 | |
| 	}{
 | |
| 		{
 | |
| 			description: "Default",
 | |
| 			userPath:    "/",
 | |
| 			isValid:     true,
 | |
| 		},
 | |
| 		{
 | |
| 			description: "Empty",
 | |
| 			userPath:    "",
 | |
| 			isValid:     false,
 | |
| 		},
 | |
| 		{
 | |
| 			description: "Valid",
 | |
| 			userPath:    "/path/",
 | |
| 			isValid:     true,
 | |
| 		},
 | |
| 		{
 | |
| 			description: "Missing leading slash",
 | |
| 			userPath:    "path/",
 | |
| 			isValid:     false,
 | |
| 		},
 | |
| 		{
 | |
| 			description: "Missing trailing slash",
 | |
| 			userPath:    "/path",
 | |
| 			isValid:     false,
 | |
| 		},
 | |
| 		{
 | |
| 			description: "Invalid character",
 | |
| 			userPath:    "/šiauliai/",
 | |
| 			isValid:     false,
 | |
| 		},
 | |
| 		{
 | |
| 			description: "Max length",
 | |
| 			userPath:    "/" + strings.Repeat("a", 510) + "/",
 | |
| 			isValid:     true,
 | |
| 		},
 | |
| 		{
 | |
| 			description: "Too long",
 | |
| 			userPath:    "/" + strings.Repeat("a", 511) + "/",
 | |
| 			isValid:     false,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, tc := range testCases {
 | |
| 		t.Run(tc.description, func(t *testing.T) {
 | |
| 			if tc.isValid != userPathRegex.MatchString(tc.userPath) {
 | |
| 				t.Fatalf("bad: expected %s", strconv.FormatBool(tc.isValid))
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestRoleCRUDWithPermissionsBoundary(t *testing.T) {
 | |
| 	roleName := "test_perm_boundary"
 | |
| 
 | |
| 	config := logical.TestBackendConfig()
 | |
| 	config.StorageView = &logical.InmemStorage{}
 | |
| 
 | |
| 	b := Backend()
 | |
| 	if err := b.Setup(context.Background(), config); err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 
 | |
| 	permissionsBoundaryARN := "arn:aws:iam::aws:policy/EC2FullAccess"
 | |
| 
 | |
| 	roleData := map[string]interface{}{
 | |
| 		"credential_type":          iamUserCred,
 | |
| 		"policy_arns":              []string{adminAccessPolicyARN},
 | |
| 		"permissions_boundary_arn": permissionsBoundaryARN,
 | |
| 	}
 | |
| 	request := &logical.Request{
 | |
| 		Operation: logical.UpdateOperation,
 | |
| 		Path:      "roles/" + roleName,
 | |
| 		Storage:   config.StorageView,
 | |
| 		Data:      roleData,
 | |
| 	}
 | |
| 	resp, err := b.HandleRequest(context.Background(), request)
 | |
| 	if err != nil || (resp != nil && resp.IsError()) {
 | |
| 		t.Fatalf("bad: role creation failed. resp:%#v\nerr:%v", resp, err)
 | |
| 	}
 | |
| 
 | |
| 	request = &logical.Request{
 | |
| 		Operation: logical.ReadOperation,
 | |
| 		Path:      "roles/" + roleName,
 | |
| 		Storage:   config.StorageView,
 | |
| 	}
 | |
| 	resp, err = b.HandleRequest(context.Background(), request)
 | |
| 	if err != nil || (resp != nil && resp.IsError()) {
 | |
| 		t.Fatalf("bad: reading role failed. resp:%#v\nerr:%v", resp, err)
 | |
| 	}
 | |
| 	if resp.Data["credential_type"] != iamUserCred {
 | |
| 		t.Errorf("bad: expected credential_type of %s, got %s instead", iamUserCred, resp.Data["credential_type"])
 | |
| 	}
 | |
| 	if resp.Data["permissions_boundary_arn"] != permissionsBoundaryARN {
 | |
| 		t.Errorf("bad: expected permissions_boundary_arn of %s, got %s instead", permissionsBoundaryARN, resp.Data["permissions_boundary_arn"])
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestRoleWithPermissionsBoundaryValidation(t *testing.T) {
 | |
| 	config := logical.TestBackendConfig()
 | |
| 	config.StorageView = &logical.InmemStorage{}
 | |
| 
 | |
| 	b := Backend()
 | |
| 	if err := b.Setup(context.Background(), config); err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 
 | |
| 	roleData := map[string]interface{}{
 | |
| 		"credential_type":          assumedRoleCred, // only iamUserCred supported with permissions_boundary_arn
 | |
| 		"role_arns":                []string{"arn:aws:iam::123456789012:role/VaultRole"},
 | |
| 		"permissions_boundary_arn": "arn:aws:iam::aws:policy/FooBar",
 | |
| 	}
 | |
| 	request := &logical.Request{
 | |
| 		Operation: logical.UpdateOperation,
 | |
| 		Path:      "roles/test_perm_boundary",
 | |
| 		Storage:   config.StorageView,
 | |
| 		Data:      roleData,
 | |
| 	}
 | |
| 	resp, err := b.HandleRequest(context.Background(), request)
 | |
| 	if err == nil && (resp == nil || !resp.IsError()) {
 | |
| 		t.Fatalf("bad: expected role creation to fail due to bad credential_type, but it didn't. resp:%#v\nerr:%v", resp, err)
 | |
| 	}
 | |
| 
 | |
| 	roleData = map[string]interface{}{
 | |
| 		"credential_type":          iamUserCred,
 | |
| 		"policy_arns":              []string{adminAccessPolicyARN},
 | |
| 		"permissions_boundary_arn": "arn:aws:notiam::aws:policy/FooBar",
 | |
| 	}
 | |
| 	request.Data = roleData
 | |
| 	resp, err = b.HandleRequest(context.Background(), request)
 | |
| 	if err == nil && (resp == nil || !resp.IsError()) {
 | |
| 		t.Fatalf("bad: expected role creation to fail due to malformed permissions_boundary_arn, but it didn't. resp:%#v\nerr:%v", resp, err)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestValidateAWSManagedPolicy(t *testing.T) {
 | |
| 	expectErr := func(arn string) {
 | |
| 		err := validateAWSManagedPolicy(arn)
 | |
| 		if err == nil {
 | |
| 			t.Errorf("bad: expected arn of %s to return an error but it didn't", arn)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	expectErr("not_an_arn")
 | |
| 	expectErr("notarn:aws:iam::aws:policy/FooBar")
 | |
| 	expectErr("arn:aws:notiam::aws:policy/FooBar")
 | |
| 	expectErr("arn:aws:iam::aws:notpolicy/FooBar")
 | |
| 	expectErr("arn:aws:iam::aws:policynot/FooBar")
 | |
| 
 | |
| 	arn := "arn:aws:iam::aws:policy/FooBar"
 | |
| 	err := validateAWSManagedPolicy(arn)
 | |
| 	if err != nil {
 | |
| 		t.Errorf("bad: expected arn of %s to not return an error but it did: %#v", arn, err)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestRoleEntryValidationCredTypes(t *testing.T) {
 | |
| 	roleEntry := awsRoleEntry{
 | |
| 		CredentialTypes: []string{},
 | |
| 		PolicyArns:      []string{adminAccessPolicyARN},
 | |
| 	}
 | |
| 	if roleEntry.validate() == nil {
 | |
| 		t.Errorf("bad: invalid roleEntry with no CredentialTypes %#v passed validation", roleEntry)
 | |
| 	}
 | |
| 	roleEntry.CredentialTypes = []string{"invalid_type"}
 | |
| 	if roleEntry.validate() == nil {
 | |
| 		t.Errorf("bad: invalid roleEntry with invalid CredentialTypes %#v passed validation", roleEntry)
 | |
| 	}
 | |
| 	roleEntry.CredentialTypes = []string{iamUserCred, "invalid_type"}
 | |
| 	if roleEntry.validate() == nil {
 | |
| 		t.Errorf("bad: invalid roleEntry with invalid CredentialTypes %#v passed validation", roleEntry)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestRoleEntryValidationIamUserCred(t *testing.T) {
 | |
| 	allowAllPolicyDocument := `{"Version": "2012-10-17", "Statement": [{"Sid": "AllowAll", "Effect": "Allow", "Action": "*", "Resource": "*"}]}`
 | |
| 	roleEntry := awsRoleEntry{
 | |
| 		CredentialTypes:        []string{iamUserCred},
 | |
| 		PolicyArns:             []string{adminAccessPolicyARN},
 | |
| 		PermissionsBoundaryARN: adminAccessPolicyARN,
 | |
| 	}
 | |
| 	err := roleEntry.validate()
 | |
| 	if err != nil {
 | |
| 		t.Errorf("bad: valid roleEntry %#v failed validation: %v", roleEntry, err)
 | |
| 	}
 | |
| 	roleEntry.PolicyDocument = allowAllPolicyDocument
 | |
| 	err = roleEntry.validate()
 | |
| 	if err != nil {
 | |
| 		t.Errorf("bad: valid roleEntry %#v failed validation: %v", roleEntry, err)
 | |
| 	}
 | |
| 	roleEntry.PolicyArns = []string{}
 | |
| 	err = roleEntry.validate()
 | |
| 	if err != nil {
 | |
| 		t.Errorf("bad: valid roleEntry %#v failed validation: %v", roleEntry, err)
 | |
| 	}
 | |
| 
 | |
| 	roleEntry = awsRoleEntry{
 | |
| 		CredentialTypes: []string{iamUserCred},
 | |
| 		RoleArns:        []string{"arn:aws:iam::123456789012:role/SomeRole"},
 | |
| 	}
 | |
| 	if roleEntry.validate() == nil {
 | |
| 		t.Errorf("bad: invalid roleEntry with invalid RoleArns parameter %#v passed validation", roleEntry)
 | |
| 	}
 | |
| 
 | |
| 	roleEntry = awsRoleEntry{
 | |
| 		CredentialTypes: []string{iamUserCred},
 | |
| 		PolicyArns:      []string{adminAccessPolicyARN},
 | |
| 		DefaultSTSTTL:   1,
 | |
| 	}
 | |
| 	if roleEntry.validate() == nil {
 | |
| 		t.Errorf("bad: invalid roleEntry with unrecognized DefaultSTSTTL %#v passed validation", roleEntry)
 | |
| 	}
 | |
| 	roleEntry.DefaultSTSTTL = 0
 | |
| 	roleEntry.MaxSTSTTL = 1
 | |
| 	if roleEntry.validate() == nil {
 | |
| 		t.Errorf("bad: invalid roleEntry with unrecognized MaxSTSTTL %#v passed validation", roleEntry)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestRoleEntryValidationAssumedRoleCred(t *testing.T) {
 | |
| 	allowAllPolicyDocument := `{"Version": "2012-10-17", "Statement": [{"Sid": "AllowAll", "Effect": "Allow", "Action": "*", "Resource": "*"}]}`
 | |
| 	roleEntry := awsRoleEntry{
 | |
| 		CredentialTypes: []string{assumedRoleCred},
 | |
| 		RoleArns:        []string{"arn:aws:iam::123456789012:role/SomeRole"},
 | |
| 		PolicyArns:      []string{adminAccessPolicyARN},
 | |
| 		PolicyDocument:  allowAllPolicyDocument,
 | |
| 		DefaultSTSTTL:   2,
 | |
| 		MaxSTSTTL:       3,
 | |
| 	}
 | |
| 	if err := roleEntry.validate(); err != nil {
 | |
| 		t.Errorf("bad: valid roleEntry %#v failed validation: %v", roleEntry, err)
 | |
| 	}
 | |
| 
 | |
| 	roleEntry.MaxSTSTTL = 1
 | |
| 	if roleEntry.validate() == nil {
 | |
| 		t.Errorf("bad: invalid roleEntry with MaxSTSTTL < DefaultSTSTTL %#v passed validation", roleEntry)
 | |
| 	}
 | |
| 	roleEntry.MaxSTSTTL = 0
 | |
| 	roleEntry.UserPath = "/foobar/"
 | |
| 	if roleEntry.validate() == nil {
 | |
| 		t.Errorf("bad: invalid roleEntry with unrecognized UserPath %#v passed validation", roleEntry)
 | |
| 	}
 | |
| 	roleEntry.UserPath = ""
 | |
| 	roleEntry.PermissionsBoundaryARN = adminAccessPolicyARN
 | |
| 	if roleEntry.validate() == nil {
 | |
| 		t.Errorf("bad: invalid roleEntry with unrecognized PermissionsBoundary %#v passed validation", roleEntry)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestRoleEntryValidationFederationTokenCred(t *testing.T) {
 | |
| 	allowAllPolicyDocument := `{"Version": "2012-10-17", "Statement": [{"Sid": "AllowAll", "Effect": "Allow", "Action": "*", "Resource": "*"}]}`
 | |
| 	roleEntry := awsRoleEntry{
 | |
| 		CredentialTypes: []string{federationTokenCred},
 | |
| 		PolicyDocument:  allowAllPolicyDocument,
 | |
| 		PolicyArns:      []string{adminAccessPolicyARN},
 | |
| 		DefaultSTSTTL:   2,
 | |
| 		MaxSTSTTL:       3,
 | |
| 	}
 | |
| 	if err := roleEntry.validate(); err != nil {
 | |
| 		t.Errorf("bad: valid roleEntry %#v failed validation: %v", roleEntry, err)
 | |
| 	}
 | |
| 
 | |
| 	roleEntry.RoleArns = []string{"arn:aws:iam::123456789012:role/SomeRole"}
 | |
| 	if roleEntry.validate() == nil {
 | |
| 		t.Errorf("bad: invalid roleEntry with unrecognized RoleArns %#v passed validation", roleEntry)
 | |
| 	}
 | |
| 	roleEntry.RoleArns = []string{}
 | |
| 	roleEntry.UserPath = "/foobar/"
 | |
| 	if roleEntry.validate() == nil {
 | |
| 		t.Errorf("bad: invalid roleEntry with unrecognized UserPath %#v passed validation", roleEntry)
 | |
| 	}
 | |
| 
 | |
| 	roleEntry.UserPath = ""
 | |
| 	roleEntry.MaxSTSTTL = 1
 | |
| 	if roleEntry.validate() == nil {
 | |
| 		t.Errorf("bad: invalid roleEntry with MaxSTSTTL < DefaultSTSTTL %#v passed validation", roleEntry)
 | |
| 	}
 | |
| 	roleEntry.MaxSTSTTL = 0
 | |
| 	roleEntry.PermissionsBoundaryARN = adminAccessPolicyARN
 | |
| 	if roleEntry.validate() == nil {
 | |
| 		t.Errorf("bad: invalid roleEntry with unrecognized PermissionsBoundary %#v passed validation", roleEntry)
 | |
| 	}
 | |
| }
 | 
