2016-06-21 00:09:17 +00:00
|
|
|
package aws
|
|
|
|
|
|
|
|
import (
|
2018-01-08 18:31:38 +00:00
|
|
|
"context"
|
2018-08-16 10:38:13 +00:00
|
|
|
"reflect"
|
2016-06-21 00:09:17 +00:00
|
|
|
"strconv"
|
2019-03-31 13:10:17 +00:00
|
|
|
"strings"
|
2016-06-21 00:09:17 +00:00
|
|
|
"testing"
|
|
|
|
|
2019-04-12 21:54:35 +00:00
|
|
|
"github.com/hashicorp/vault/sdk/logical"
|
2016-06-21 00:09:17 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestBackend_PathListRoles(t *testing.T) {
|
|
|
|
var resp *logical.Response
|
|
|
|
var err error
|
|
|
|
config := logical.TestBackendConfig()
|
|
|
|
config.StorageView = &logical.InmemStorage{}
|
|
|
|
|
|
|
|
b := Backend()
|
2018-01-19 06:44:44 +00:00
|
|
|
if err := b.Setup(context.Background(), config); err != nil {
|
2016-06-21 00:09:17 +00:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
roleData := map[string]interface{}{
|
2018-08-16 10:38:13 +00:00
|
|
|
"role_arns": []string{"arn:aws:iam::123456789012:role/path/RoleName"},
|
|
|
|
"credential_type": assumedRoleCred,
|
2018-10-02 14:14:16 +00:00
|
|
|
"default_sts_ttl": 3600,
|
2018-10-20 14:36:47 +00:00
|
|
|
"max_sts_ttl": 3600,
|
2016-06-21 00:09:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
roleReq := &logical.Request{
|
|
|
|
Operation: logical.UpdateOperation,
|
|
|
|
Storage: config.StorageView,
|
|
|
|
Data: roleData,
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := 1; i <= 10; i++ {
|
|
|
|
roleReq.Path = "roles/testrole" + strconv.Itoa(i)
|
2018-01-08 18:31:38 +00:00
|
|
|
resp, err = b.HandleRequest(context.Background(), roleReq)
|
2016-06-21 00:09:17 +00:00
|
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
|
|
t.Fatalf("bad: role creation failed. resp:%#v\n err:%v", resp, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-08 18:31:38 +00:00
|
|
|
resp, err = b.HandleRequest(context.Background(), &logical.Request{
|
2016-06-21 00:09:17 +00:00
|
|
|
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")
|
|
|
|
}
|
|
|
|
|
2018-01-08 18:31:38 +00:00
|
|
|
resp, err = b.HandleRequest(context.Background(), &logical.Request{
|
2016-06-21 00:09:17 +00:00
|
|
|
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")
|
|
|
|
}
|
|
|
|
}
|
2018-08-16 10:38:13 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
2019-03-31 13:10:17 +00:00
|
|
|
|
|
|
|
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))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2019-08-08 18:53:06 +00:00
|
|
|
|
|
|
|
func TestRoleEntryValidationCredTypes(t *testing.T) {
|
|
|
|
roleEntry := awsRoleEntry{
|
|
|
|
CredentialTypes: []string{},
|
|
|
|
PolicyArns: []string{"arn:aws:iam::aws:policy/AdministratorAccess"},
|
|
|
|
}
|
|
|
|
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) {
|
|
|
|
var allowAllPolicyDocument = `{"Version": "2012-10-17", "Statement": [{"Sid": "AllowAll", "Effect": "Allow", "Action": "*", "Resource": "*"}]}`
|
|
|
|
|
|
|
|
roleEntry := awsRoleEntry{
|
|
|
|
CredentialTypes: []string{iamUserCred},
|
|
|
|
PolicyArns: []string{"arn:aws:iam::aws:policy/AdministratorAccess"},
|
|
|
|
}
|
|
|
|
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{"arn:aws:iam::aws:policy/AdministratorAccess"},
|
|
|
|
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) {
|
|
|
|
var 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"},
|
|
|
|
PolicyDocument: allowAllPolicyDocument,
|
|
|
|
DefaultSTSTTL: 2,
|
|
|
|
MaxSTSTTL: 3,
|
|
|
|
}
|
|
|
|
if err := roleEntry.validate(); err != nil {
|
|
|
|
t.Errorf("bad: valid roleEntry %#v failed validation: %v", roleEntry, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
roleEntry.PolicyArns = []string{"arn:aws:iam::aws:policy/AdministratorAccess"}
|
|
|
|
if roleEntry.validate() == nil {
|
|
|
|
t.Errorf("bad: invalid roleEntry with unrecognized PolicyArns %#v passed validation", roleEntry)
|
|
|
|
}
|
|
|
|
roleEntry.PolicyArns = []string{}
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRoleEntryValidationFederationTokenCred(t *testing.T) {
|
|
|
|
var allowAllPolicyDocument = `{"Version": "2012-10-17", "Statement": [{"Sid": "AllowAll", "Effect": "Allow", "Action": "*", "Resource": "*"}]}`
|
|
|
|
roleEntry := awsRoleEntry{
|
|
|
|
CredentialTypes: []string{federationTokenCred},
|
|
|
|
PolicyDocument: allowAllPolicyDocument,
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|