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
|
||||
|
||||
//{
|
||||
// "Version": "xyz",
|
||||
// "Policy": [
|
||||
// {
|
||||
// "Effect": "Allow",
|
||||
// "Action": [
|
||||
// "native:*",
|
||||
// "s3:PutObject",
|
||||
// "s3:GetObject"
|
||||
// ],
|
||||
// "Resource": ["*"],
|
||||
// "Principal": ["did:frostfs:039e3ee771a223361fe7862f532e9511b57baaae3c3e2622682e99d0e660f7671"],
|
||||
// "Condition": [ {"StringEquals": {"native::object::attribute", "iamuser-admin"}]
|
||||
// }
|
||||
// ]
|
||||
//}
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// type Policy struct {
|
||||
// Rules []Rule `json:"Policy"`
|
||||
// }
|
||||
type (
|
||||
// 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 {
|
||||
// Effect string `json:"Effect"`
|
||||
// Action []string `json:"Action"`
|
||||
// Resource []string `json:"Resource"`
|
||||
// Principal []string `json:"Principal"`
|
||||
// Condition []Condition `json:"Condition"`
|
||||
// }
|
||||
IAMStatements []IAMStatement
|
||||
|
||||
IAMStatement struct {
|
||||
SID string `json:"Sid,omitempty"`
|
||||
Principal IAMPrincipal `json:"Principal,omitempty"`
|
||||
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