Skip to content

Commit

Permalink
provider/aws: Various IAM policy normalizations for IAM data source
Browse files Browse the repository at this point in the history
 * Various string slices are sorted and truncated to strings if they
   only contain one element.
 * Sids are now included if they are empty.

This is to ensure what is sent to AWS matches what comes back, to
prevent recurring diffs even when the policy has changed.
  • Loading branch information
Chris Marchesi committed May 30, 2016
1 parent 6da9934 commit 7d4c122
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 53 deletions.
17 changes: 12 additions & 5 deletions builtin/providers/aws/data_source_aws_iam_policy_document.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,19 @@ func dataSourceAwsIamPolicyDocumentRead(d *schema.ResourceData, meta interface{}
return nil
}

func dataSourceAwsIamPolicyDocumentReplaceVarsInList(in []string) []string {
out := make([]string, len(in))
for i, item := range in {
out[i] = resourceAwsIamPolicyDocumentVarReplacer.Replace(item)
func dataSourceAwsIamPolicyDocumentReplaceVarsInList(in interface{}) interface{} {
switch v := in.(type) {
case string:
return resourceAwsIamPolicyDocumentVarReplacer.Replace(v)
case []string:
out := make([]string, len(v))
for i, item := range v {
out[i] = resourceAwsIamPolicyDocumentVarReplacer.Replace(item)
}
return out
default:
panic("dataSourceAwsIamPolicyDocumentReplaceVarsInList: input not string nor []string")
}
return out
}

func dataSourceAwsIamPolicyDocumentMakeConditions(in []interface{}) IAMPolicyStatementConditionSet {
Expand Down
48 changes: 17 additions & 31 deletions builtin/providers/aws/data_source_aws_iam_policy_document_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func testAccCheckStateValue(id, name, value string) resource.TestCheckFunc {
return fmt.Errorf("No ID is set")
}

v := rs.Primary.Attributes["output."+name]
v := rs.Primary.Attributes[name]
if v != value {
return fmt.Errorf(
"Value for %s is %s, not %s", name, v, value)
Expand Down Expand Up @@ -73,7 +73,6 @@ data "aws_iam_policy_document" "test" {
test = "StringLike"
variable = "s3:prefix"
values = [
"",
"home/",
"home/&{aws:username}/",
]
Expand Down Expand Up @@ -112,61 +111,48 @@ var testAccAWSIAMPolicyDocumentExpectedJSON = `{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Action": [
"s3:GetBucketLocation",
"s3:ListAllMyBuckets"
"s3:ListAllMyBuckets",
"s3:GetBucketLocation"
],
"Resource": [
"arn:aws:s3:::*"
]
"Resource": "arn:aws:s3:::*"
},
{
"Sid": "",
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::foo"
],
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::foo",
"NotPrincipal": {
"AWS": [
"arn:blahblah:example"
]
"AWS": "arn:blahblah:example"
},
"Condition": {
"StringLike": {
"s3:prefix": [
"",
"home/",
"home/${aws:username}/"
"home/${aws:username}/",
"home/"
]
}
}
},
{
"Sid": "",
"Effect": "Allow",
"Action": [
"s3:*"
],
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::foo/home/${aws:username}/*",
"arn:aws:s3:::foo/home/${aws:username}"
],
"Principal": {
"AWS": [
"arn:blahblah:example"
]
"AWS": "arn:blahblah:example"
}
},
{
"Sid": "",
"Effect": "Deny",
"NotAction": [
"s3:*"
],
"NotResource": [
"arn:aws:s3:::*"
]
"NotAction": "s3:*",
"NotResource": "arn:aws:s3:::*"
}
]
}`
55 changes: 38 additions & 17 deletions builtin/providers/aws/iam_policy_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package aws

import (
"encoding/json"
"sort"
)

type IAMPolicyDoc struct {
Expand All @@ -11,64 +12,84 @@ type IAMPolicyDoc struct {
}

type IAMPolicyStatement struct {
Sid string `json:",omitempty"`
Sid string
Effect string `json:",omitempty"`
Actions []string `json:"Action,omitempty"`
NotActions []string `json:"NotAction,omitempty"`
Resources []string `json:"Resource,omitempty"`
NotResources []string `json:"NotResource,omitempty"`
Actions interface{} `json:"Action,omitempty"`
NotActions interface{} `json:"NotAction,omitempty"`
Resources interface{} `json:"Resource,omitempty"`
NotResources interface{} `json:"NotResource,omitempty"`
Principals IAMPolicyStatementPrincipalSet `json:"Principal,omitempty"`
NotPrincipals IAMPolicyStatementPrincipalSet `json:"NotPrincipal,omitempty"`
Conditions IAMPolicyStatementConditionSet `json:"Condition,omitempty"`
}

type IAMPolicyStatementPrincipal struct {
Type string
Identifiers []string
Identifiers interface{}
}

type IAMPolicyStatementCondition struct {
Test string
Variable string
Values []string
Values interface{}
}

type IAMPolicyStatementPrincipalSet []IAMPolicyStatementPrincipal
type IAMPolicyStatementConditionSet []IAMPolicyStatementCondition

func (ps IAMPolicyStatementPrincipalSet) MarshalJSON() ([]byte, error) {
raw := map[string][]string{}
raw := map[string]interface{}{}

for _, p := range ps {
if _, ok := raw[p.Type]; !ok {
raw[p.Type] = make([]string, 0, len(p.Identifiers))
switch i := p.Identifiers.(type) {
case []string:
if _, ok := raw[p.Type]; !ok {
raw[p.Type] = make([]string, 0, len(i))
}
sort.Sort(sort.Reverse(sort.StringSlice(i)))
raw[p.Type] = append(raw[p.Type].([]string), i...)
case string:
raw[p.Type] = i
default:
panic("Unsupported data type for IAMPolicyStatementPrincipalSet")
}
raw[p.Type] = append(raw[p.Type], p.Identifiers...)
}

return json.Marshal(&raw)
}

func (cs IAMPolicyStatementConditionSet) MarshalJSON() ([]byte, error) {
raw := map[string]map[string][]string{}
raw := map[string]map[string]interface{}{}

for _, c := range cs {
if _, ok := raw[c.Test]; !ok {
raw[c.Test] = map[string][]string{}
raw[c.Test] = map[string]interface{}{}
}
if _, ok := raw[c.Test][c.Variable]; !ok {
raw[c.Test][c.Variable] = make([]string, 0, len(c.Values))
switch i := c.Values.(type) {
case []string:
if _, ok := raw[c.Test][c.Variable]; !ok {
raw[c.Test][c.Variable] = make([]string, 0, len(i))
}
sort.Sort(sort.Reverse(sort.StringSlice(i)))
raw[c.Test][c.Variable] = append(raw[c.Test][c.Variable].([]string), i...)
case string:
raw[c.Test][c.Variable] = i
default:
panic("Unsupported data type for IAMPolicyStatementConditionSet")
}
raw[c.Test][c.Variable] = append(raw[c.Test][c.Variable], c.Values...)
}

return json.Marshal(&raw)
}

func iamPolicyDecodeConfigStringList(lI []interface{}) []string {
func iamPolicyDecodeConfigStringList(lI []interface{}) interface{} {
if len(lI) == 1 {
return lI[0].(string)
}
ret := make([]string, len(lI))
for i, vI := range lI {
ret[i] = vI.(string)
}
sort.Sort(sort.Reverse(sort.StringSlice(ret)))
return ret
}

0 comments on commit 7d4c122

Please sign in to comment.