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
This commit is contained in:
Paul Stack 2017-01-03 20:04:16 +00:00 committed by GitHub
parent 5a30bce1bf
commit dec4cc0e1a
2 changed files with 65 additions and 21 deletions

View File

@ -2,10 +2,12 @@ package awspolicy
import ( import (
"encoding/json" "encoding/json"
"errors"
"reflect" "reflect"
"strings" "strings"
"github.com/hashicorp/errwrap" "github.com/hashicorp/errwrap"
"github.com/mitchellh/mapstructure"
) )
// PoliciesAreEquivalent tests for the structural equivalence of two // PoliciesAreEquivalent tests for the structural equivalence of two
@ -19,23 +21,65 @@ import (
// otherwise. If either of the input strings are not valid JSON, // otherwise. If either of the input strings are not valid JSON,
// false is returned along with an error. // false is returned along with an error.
func PoliciesAreEquivalent(policy1, policy2 string) (bool, error) { func PoliciesAreEquivalent(policy1, policy2 string) (bool, error) {
policy1doc := &awsPolicyDocument{} policy1intermediate := &intermediateAwsPolicyDocument{}
if err := json.Unmarshal([]byte(policy1), policy1doc); err != nil { if err := json.Unmarshal([]byte(policy1), policy1intermediate); err != nil {
return false, errwrap.Wrapf("Error unmarshaling policy: {{err}}", err) return false, errwrap.Wrapf("Error unmarshaling policy: {{err}}", err)
} }
policy2doc := &awsPolicyDocument{} policy2intermediate := &intermediateAwsPolicyDocument{}
if err := json.Unmarshal([]byte(policy2), policy2doc); err != nil { if err := json.Unmarshal([]byte(policy2), policy2intermediate); err != nil {
return false, errwrap.Wrapf("Error unmarshaling policy: {{err}}", err) 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 { type awsPolicyDocument struct {
Version string `json:",omitempty"` Version string
Id string `json:",omitempty"` Id string
Statements []*awsPolicyStatement `json:"Statement"` Statements []*awsPolicyStatement
} }
func (doc *awsPolicyDocument) equals(other *awsPolicyDocument) bool { 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 // 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, // other which is equal? If no, policies are not equal, if yes,
// then they may be. // then they may be.
for _, ours := range doc.Statements { for _, ours := range doc.Statements {
@ -89,15 +133,15 @@ func (doc *awsPolicyDocument) equals(other *awsPolicyDocument) bool {
} }
type awsPolicyStatement struct { type awsPolicyStatement struct {
Sid string `json:",omitempty"` Sid string `json:",omitempty" mapstructure:"Sid"`
Effect string `json:",omitempty"` Effect string `json:",omitempty" mapstructure:"Effect"`
Actions interface{} `json:"Action,omitempty"` Actions interface{} `json:"Action,omitempty" mapstructure:"Action"`
NotActions interface{} `json:"NotAction,omitempty"` NotActions interface{} `json:"NotAction,omitempty" mapstructure:"NotAction"`
Resources interface{} `json:"Resource,omitempty"` Resources interface{} `json:"Resource,omitempty" mapstructure:"Resource"`
NotResources interface{} `json:"NotResource,omitempty"` NotResources interface{} `json:"NotResource,omitempty" mapstructure:"NotResource"`
Principals interface{} `json:"Principal,omitempty"` Principals interface{} `json:"Principal,omitempty" mapstructure:"Principal"`
NotPrincipals interface{} `json:"NotPrincipal,omitempty"` NotPrincipals interface{} `json:"NotPrincipal,omitempty" mapstructure:"NotPrincipal"`
Conditions map[string]map[string]interface{} `json:"Condition,omitempty"` Conditions map[string]map[string]interface{} `json:"Condition,omitempty" mapstructure:"Condition"`
} }
func (statement *awsPolicyStatement) equals(other *awsPolicyStatement) bool { func (statement *awsPolicyStatement) equals(other *awsPolicyStatement) bool {

6
vendor/vendor.json vendored
View File

@ -1857,10 +1857,10 @@
"revision": "f233a8bac88d1f2dc282a98186f5a3363b806181" "revision": "f233a8bac88d1f2dc282a98186f5a3363b806181"
}, },
{ {
"checksumSHA1": "zyyyjWKu9gGLFy00k8utV7pncvg=", "checksumSHA1": "rixrz9zVpq9gss5jHqc/nGNpSw0=",
"path": "github.com/jen20/awspolicyequivalence", "path": "github.com/jen20/awspolicyequivalence",
"revision": "ebe5485f2c1822e7bee8b5008e14d9481a14a3a3", "revision": "ed4c14ea400d756059e9312e409477a9ebf448f4",
"revisionTime": "2016-09-29T21:48:42Z" "revisionTime": "2017-01-03T19:23:32Z"
}, },
{ {
"checksumSHA1": "tRK0n/cIjBCYjnumPiP9cCS2CnA=", "checksumSHA1": "tRK0n/cIjBCYjnumPiP9cCS2CnA=",