mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-08 15:13:56 -06:00
145bf42806
There are three equivalent forms for expressing "everyone" (including anonymous) in IAM policies: - "Principals": "*" - "Principals": {"AWS": "*"} - "Principals": {"*": "*"} The more-constrained syntax used by our aws_iam_policy_document data source means that the user can only express the latter two of these directly. However, when returning IAM policies from the API AWS likes to normalize to the first form, causing unresolvable diffs. This fixes #9335 by handling the "everyone" case as a special case, serializing it in JSON as the "*" shorthand form. This change does *not* address the normalization of hand-written policies containing such elements. A similar change would need to be made in the external package github.com/jen20/awspolicyequivalence in order to avoid the issue for hand-written policies.
113 lines
3.1 KiB
Go
113 lines
3.1 KiB
Go
package aws
|
|
|
|
import (
|
|
"encoding/json"
|
|
"sort"
|
|
)
|
|
|
|
type IAMPolicyDoc struct {
|
|
Version string `json:",omitempty"`
|
|
Id string `json:",omitempty"`
|
|
Statements []*IAMPolicyStatement `json:"Statement"`
|
|
}
|
|
|
|
type IAMPolicyStatement struct {
|
|
Sid string
|
|
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 IAMPolicyStatementPrincipalSet `json:"Principal,omitempty"`
|
|
NotPrincipals IAMPolicyStatementPrincipalSet `json:"NotPrincipal,omitempty"`
|
|
Conditions IAMPolicyStatementConditionSet `json:"Condition,omitempty"`
|
|
}
|
|
|
|
type IAMPolicyStatementPrincipal struct {
|
|
Type string
|
|
Identifiers interface{}
|
|
}
|
|
|
|
type IAMPolicyStatementCondition struct {
|
|
Test string
|
|
Variable string
|
|
Values interface{}
|
|
}
|
|
|
|
type IAMPolicyStatementPrincipalSet []IAMPolicyStatementPrincipal
|
|
type IAMPolicyStatementConditionSet []IAMPolicyStatementCondition
|
|
|
|
func (ps IAMPolicyStatementPrincipalSet) MarshalJSON() ([]byte, error) {
|
|
raw := map[string]interface{}{}
|
|
|
|
// As a special case, IAM considers the string value "*" to be
|
|
// equivalent to "AWS": "*", and normalizes policies as such.
|
|
// We'll follow their lead and do the same normalization here.
|
|
// IAM also considers {"*": "*"} to be equivalent to this.
|
|
if len(ps) == 1 {
|
|
p := ps[0]
|
|
if p.Type == "AWS" || p.Type == "*" {
|
|
if sv, ok := p.Identifiers.(string); ok && sv == "*" {
|
|
return []byte(`"*"`), nil
|
|
}
|
|
|
|
if av, ok := p.Identifiers.([]string); ok && len(av) == 1 && av[0] == "*" {
|
|
return []byte(`"*"`), nil
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, p := range ps {
|
|
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")
|
|
}
|
|
}
|
|
|
|
return json.Marshal(&raw)
|
|
}
|
|
|
|
func (cs IAMPolicyStatementConditionSet) MarshalJSON() ([]byte, error) {
|
|
raw := map[string]map[string]interface{}{}
|
|
|
|
for _, c := range cs {
|
|
if _, ok := raw[c.Test]; !ok {
|
|
raw[c.Test] = map[string]interface{}{}
|
|
}
|
|
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")
|
|
}
|
|
}
|
|
|
|
return json.Marshal(&raw)
|
|
}
|
|
|
|
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
|
|
}
|