generated from TrueCloudLab/basic
[#XX] Add IAM policy unmarshaler
This commit is contained in:
parent
3970569602
commit
0933aa7ce6
2 changed files with 286 additions and 26 deletions
161
policy.go
161
policy.go
|
@ -1,30 +1,139 @@
|
||||||
package policyengine
|
package policyengine
|
||||||
|
|
||||||
//{
|
import (
|
||||||
// "Version": "xyz",
|
"encoding/json"
|
||||||
// "Policy": [
|
"errors"
|
||||||
// {
|
)
|
||||||
// "Effect": "Allow",
|
|
||||||
// "Action": [
|
|
||||||
// "native:*",
|
|
||||||
// "s3:PutObject",
|
|
||||||
// "s3:GetObject"
|
|
||||||
// ],
|
|
||||||
// "Resource": ["*"],
|
|
||||||
// "Principal": ["did:frostfs:039e3ee771a223361fe7862f532e9511b57baaae3c3e2622682e99d0e660f7671"],
|
|
||||||
// "Condition": [ {"StringEquals": {"native::object::attribute", "iamuser-admin"}]
|
|
||||||
// }
|
|
||||||
// ]
|
|
||||||
//}
|
|
||||||
|
|
||||||
// type Policy struct {
|
type (
|
||||||
// Rules []Rule `json:"Policy"`
|
// IAMPolicy grammar https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_grammar.html
|
||||||
// }
|
IAMPolicy struct {
|
||||||
|
Version string `json:"Version,omitempty"`
|
||||||
|
ID string `json:"Id,omitempty"`
|
||||||
|
Statement IAMStatements `json:"Statement"`
|
||||||
|
}
|
||||||
|
|
||||||
// type AWSRule struct {
|
IAMStatements []IAMStatement
|
||||||
// Effect string `json:"Effect"`
|
|
||||||
// Action []string `json:"Action"`
|
IAMStatement struct {
|
||||||
// Resource []string `json:"Resource"`
|
SID string `json:"Sid,omitempty"`
|
||||||
// Principal []string `json:"Principal"`
|
Principal IAMPrincipal `json:"Principal,omitempty"`
|
||||||
// Condition []Condition `json:"Condition"`
|
Effect IAMEffect `json:"Effect"`
|
||||||
// }
|
Action IAMAction `json:"Action"`
|
||||||
|
Resource IAMResource `json:"Resource"`
|
||||||
|
Condition IAMCondition `json:"Condition,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
IAMPrincipal map[string][]string
|
||||||
|
|
||||||
|
IAMEffect string
|
||||||
|
|
||||||
|
IAMAction []string
|
||||||
|
|
||||||
|
IAMResource []string
|
||||||
|
|
||||||
|
IAMCondition map[string]map[string]string
|
||||||
|
)
|
||||||
|
|
||||||
|
const IAMWildcard = "*"
|
||||||
|
|
||||||
|
const (
|
||||||
|
IAMAllowEffect IAMEffect = "Allow"
|
||||||
|
IAMDenyEffect IAMEffect = "Deny"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *IAMStatements) UnmarshalJSON(data []byte) error {
|
||||||
|
var list []IAMStatement
|
||||||
|
if err := json.Unmarshal(data, &list); err == nil {
|
||||||
|
*s = list
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var elem IAMStatement
|
||||||
|
if err := json.Unmarshal(data, &elem); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*s = []IAMStatement{elem}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *IAMPrincipal) UnmarshalJSON(data []byte) error {
|
||||||
|
*p = make(IAMPrincipal)
|
||||||
|
|
||||||
|
var str string
|
||||||
|
|
||||||
|
if err := json.Unmarshal(data, &str); err == nil {
|
||||||
|
if str != IAMWildcard {
|
||||||
|
return errors.New("invalid IAM string principal")
|
||||||
|
}
|
||||||
|
(*p)[IAMWildcard] = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
m := make(map[string]interface{})
|
||||||
|
if err := json.Unmarshal(data, &m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, val := range m {
|
||||||
|
element, ok := val.(string)
|
||||||
|
if ok {
|
||||||
|
(*p)[key] = []string{element}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
list, ok := val.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return errors.New("invalid principal format")
|
||||||
|
}
|
||||||
|
|
||||||
|
resList := make([]string, len(list))
|
||||||
|
for i := range list {
|
||||||
|
val, ok := list[i].(string)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("invalid principal format")
|
||||||
|
}
|
||||||
|
resList[i] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
(*p)[key] = resList
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *IAMAction) UnmarshalJSON(data []byte) error {
|
||||||
|
var list []string
|
||||||
|
if err := json.Unmarshal(data, &list); err == nil {
|
||||||
|
*a = list
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var elem string
|
||||||
|
if err := json.Unmarshal(data, &elem); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*a = []string{elem}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *IAMResource) UnmarshalJSON(data []byte) error {
|
||||||
|
var list []string
|
||||||
|
if err := json.Unmarshal(data, &list); err == nil {
|
||||||
|
*r = list
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var elem string
|
||||||
|
if err := json.Unmarshal(data, &elem); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*r = []string{elem}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
151
policy_test.go
Normal file
151
policy_test.go
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
package policyengine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUnmarshalIAMPolicy(t *testing.T) {
|
||||||
|
t.Run("simple fields", func(t *testing.T) {
|
||||||
|
policy := `{
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Id": "PutObjPolicy",
|
||||||
|
"Statement": {
|
||||||
|
"Sid": "DenyObjectsThatAreNotSSEKMS",
|
||||||
|
"Principal": "*",
|
||||||
|
"Effect": "Deny",
|
||||||
|
"Action": "s3:PutObject",
|
||||||
|
"Resource": "arn:aws:s3:::DOC-EXAMPLE-BUCKET/*",
|
||||||
|
"Condition": {
|
||||||
|
"Null": {
|
||||||
|
"s3:x-amz-server-side-encryption-aws-kms-key-id": "true"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
expected := IAMPolicy{
|
||||||
|
Version: "2012-10-17",
|
||||||
|
ID: "PutObjPolicy",
|
||||||
|
Statement: []IAMStatement{{
|
||||||
|
SID: "DenyObjectsThatAreNotSSEKMS",
|
||||||
|
Principal: map[string][]string{
|
||||||
|
"*": nil,
|
||||||
|
},
|
||||||
|
Effect: IAMDenyEffect,
|
||||||
|
Action: []string{"s3:PutObject"},
|
||||||
|
Resource: []string{"arn:aws:s3:::DOC-EXAMPLE-BUCKET/*"},
|
||||||
|
Condition: map[string]map[string]string{
|
||||||
|
"Null": {
|
||||||
|
"s3:x-amz-server-side-encryption-aws-kms-key-id": "true",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
var p IAMPolicy
|
||||||
|
err := json.Unmarshal([]byte(policy), &p)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, expected, p)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("complex fields", func(t *testing.T) {
|
||||||
|
policy := `{
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [{
|
||||||
|
"Principal":{
|
||||||
|
"AWS":[
|
||||||
|
"arn:aws:iam::111122223333:user/JohnDoe"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": [
|
||||||
|
"s3:PutObject"
|
||||||
|
],
|
||||||
|
"Resource": [
|
||||||
|
"arn:aws:s3:::DOC-EXAMPLE-BUCKET/*"
|
||||||
|
],
|
||||||
|
"Condition": {
|
||||||
|
"StringEquals": {
|
||||||
|
"s3:RequestObjectTag/Department": "Finance"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}`
|
||||||
|
|
||||||
|
expected := IAMPolicy{
|
||||||
|
Version: "2012-10-17",
|
||||||
|
Statement: []IAMStatement{{
|
||||||
|
Principal: map[string][]string{
|
||||||
|
"AWS": {"arn:aws:iam::111122223333:user/JohnDoe"},
|
||||||
|
},
|
||||||
|
Effect: IAMAllowEffect,
|
||||||
|
Action: []string{"s3:PutObject"},
|
||||||
|
Resource: []string{"arn:aws:s3:::DOC-EXAMPLE-BUCKET/*"},
|
||||||
|
Condition: map[string]map[string]string{
|
||||||
|
"StringEquals": {
|
||||||
|
"s3:RequestObjectTag/Department": "Finance",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
var p IAMPolicy
|
||||||
|
err := json.Unmarshal([]byte(policy), &p)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, expected, p)
|
||||||
|
|
||||||
|
raw, err := json.Marshal(expected)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.JSONEq(t, policy, string(raw))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("check principal AWS", func(t *testing.T) {
|
||||||
|
policy := `{
|
||||||
|
"Statement": [{
|
||||||
|
"Principal":{
|
||||||
|
"AWS":"arn:aws:iam::111122223333:user/JohnDoe"
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}`
|
||||||
|
|
||||||
|
expected := IAMPolicy{
|
||||||
|
Statement: []IAMStatement{{
|
||||||
|
Principal: map[string][]string{
|
||||||
|
"AWS": {"arn:aws:iam::111122223333:user/JohnDoe"},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
var p IAMPolicy
|
||||||
|
err := json.Unmarshal([]byte(policy), &p)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, expected, p)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("native example", func(t *testing.T) {
|
||||||
|
policy := `
|
||||||
|
{
|
||||||
|
"Version": "xyz",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": [
|
||||||
|
"native:*",
|
||||||
|
"s3:PutObject",
|
||||||
|
"s3:GetObject"
|
||||||
|
],
|
||||||
|
"Resource": ["*"],
|
||||||
|
"Principal": {"FrostFS": ["did:frostfs:039e3ee771a223361fe7862f532e9511b57baaae3c3e2622682e99d0e660f7671"]},
|
||||||
|
"Condition": {"StringEquals": {"native::object::attribute": "iamuser-admin"}}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`
|
||||||
|
|
||||||
|
var p IAMPolicy
|
||||||
|
err := json.Unmarshal([]byte(policy), &p)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in a new issue