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:
parent
5a30bce1bf
commit
dec4cc0e1a
|
@ -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 {
|
||||||
|
|
|
@ -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=",
|
||||||
|
|
Loading…
Reference in New Issue