From dec4cc0e1adb5ebd54b3444ccc8ed84e50f51e9e Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Tue, 3 Jan 2017 20:04:16 +0000 Subject: [PATCH] provider/aws: Fix the normalization of AWS policy statements (#11009) Fixes: #10784 When AWS was passed an AWS policy with a single statement, it normalized it to a slice of statement. This was causing issues for Terraform when it was trying to reapply changes --- .../aws_policy_equivalence.go | 80 ++++++++++++++----- vendor/vendor.json | 6 +- 2 files changed, 65 insertions(+), 21 deletions(-) diff --git a/vendor/github.com/jen20/awspolicyequivalence/aws_policy_equivalence.go b/vendor/github.com/jen20/awspolicyequivalence/aws_policy_equivalence.go index 9aef4ca81..94a2ef00b 100644 --- a/vendor/github.com/jen20/awspolicyequivalence/aws_policy_equivalence.go +++ b/vendor/github.com/jen20/awspolicyequivalence/aws_policy_equivalence.go @@ -2,10 +2,12 @@ package awspolicy import ( "encoding/json" + "errors" "reflect" "strings" "github.com/hashicorp/errwrap" + "github.com/mitchellh/mapstructure" ) // PoliciesAreEquivalent tests for the structural equivalence of two @@ -19,23 +21,65 @@ import ( // otherwise. If either of the input strings are not valid JSON, // false is returned along with an error. func PoliciesAreEquivalent(policy1, policy2 string) (bool, error) { - policy1doc := &awsPolicyDocument{} - if err := json.Unmarshal([]byte(policy1), policy1doc); err != nil { + policy1intermediate := &intermediateAwsPolicyDocument{} + if err := json.Unmarshal([]byte(policy1), policy1intermediate); err != nil { return false, errwrap.Wrapf("Error unmarshaling policy: {{err}}", err) } - policy2doc := &awsPolicyDocument{} - if err := json.Unmarshal([]byte(policy2), policy2doc); err != nil { + policy2intermediate := &intermediateAwsPolicyDocument{} + if err := json.Unmarshal([]byte(policy2), policy2intermediate); err != nil { return false, errwrap.Wrapf("Error unmarshaling policy: {{err}}", err) } - return policy1doc.equals(policy2doc), nil + policy1Doc, err := policy1intermediate.document() + if err != nil { + return false, errwrap.Wrapf("Error parsing policy: {{err}}", err) + } + policy2Doc, err := policy2intermediate.document() + if err != nil { + return false, errwrap.Wrapf("Error parsing policy: {{err}}", err) + } + + return policy1Doc.equals(policy2Doc), nil +} + +type intermediateAwsPolicyDocument struct { + Version string `json:",omitempty"` + Id string `json:",omitempty"` + Statements interface{} `json:"Statement"` +} + +func (intermediate *intermediateAwsPolicyDocument) document() (*awsPolicyDocument, error) { + var statements []*awsPolicyStatement + + switch s := intermediate.Statements.(type) { + case []interface{}: + if err := mapstructure.Decode(s, &statements); err != nil { + return nil, errwrap.Wrapf("Error parsing statement: {{err}}", err) + } + case map[string]interface{}: + var singleStatement *awsPolicyStatement + if err := mapstructure.Decode(s, &singleStatement); err != nil { + return nil, errwrap.Wrapf("Error parsing statement: {{err}}", err) + } + statements = append(statements, singleStatement) + default: + return nil, errors.New("Unknown error parsing statement") + } + + document := &awsPolicyDocument{ + Version: intermediate.Version, + Id: intermediate.Id, + Statements: statements, + } + + return document, nil } type awsPolicyDocument struct { - Version string `json:",omitempty"` - Id string `json:",omitempty"` - Statements []*awsPolicyStatement `json:"Statement"` + Version string + Id string + Statements []*awsPolicyStatement } func (doc *awsPolicyDocument) equals(other *awsPolicyDocument) bool { @@ -54,7 +98,7 @@ func (doc *awsPolicyDocument) equals(other *awsPolicyDocument) bool { } // If we have the same number of statements in the policy, does - // each statement in the doc have a corresponding statement in + // each statement in the intermediate have a corresponding statement in // other which is equal? If no, policies are not equal, if yes, // then they may be. for _, ours := range doc.Statements { @@ -89,15 +133,15 @@ func (doc *awsPolicyDocument) equals(other *awsPolicyDocument) bool { } type awsPolicyStatement struct { - Sid string `json:",omitempty"` - Effect string `json:",omitempty"` - Actions interface{} `json:"Action,omitempty"` - NotActions interface{} `json:"NotAction,omitempty"` - Resources interface{} `json:"Resource,omitempty"` - NotResources interface{} `json:"NotResource,omitempty"` - Principals interface{} `json:"Principal,omitempty"` - NotPrincipals interface{} `json:"NotPrincipal,omitempty"` - Conditions map[string]map[string]interface{} `json:"Condition,omitempty"` + Sid string `json:",omitempty" mapstructure:"Sid"` + Effect string `json:",omitempty" mapstructure:"Effect"` + Actions interface{} `json:"Action,omitempty" mapstructure:"Action"` + NotActions interface{} `json:"NotAction,omitempty" mapstructure:"NotAction"` + Resources interface{} `json:"Resource,omitempty" mapstructure:"Resource"` + NotResources interface{} `json:"NotResource,omitempty" mapstructure:"NotResource"` + Principals interface{} `json:"Principal,omitempty" mapstructure:"Principal"` + NotPrincipals interface{} `json:"NotPrincipal,omitempty" mapstructure:"NotPrincipal"` + Conditions map[string]map[string]interface{} `json:"Condition,omitempty" mapstructure:"Condition"` } func (statement *awsPolicyStatement) equals(other *awsPolicyStatement) bool { diff --git a/vendor/vendor.json b/vendor/vendor.json index 83d4d8a0e..eb27d57e5 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1857,10 +1857,10 @@ "revision": "f233a8bac88d1f2dc282a98186f5a3363b806181" }, { - "checksumSHA1": "zyyyjWKu9gGLFy00k8utV7pncvg=", + "checksumSHA1": "rixrz9zVpq9gss5jHqc/nGNpSw0=", "path": "github.com/jen20/awspolicyequivalence", - "revision": "ebe5485f2c1822e7bee8b5008e14d9481a14a3a3", - "revisionTime": "2016-09-29T21:48:42Z" + "revision": "ed4c14ea400d756059e9312e409477a9ebf448f4", + "revisionTime": "2017-01-03T19:23:32Z" }, { "checksumSHA1": "tRK0n/cIjBCYjnumPiP9cCS2CnA=",