forked from TrueCloudLab/frostfs-s3-gw
[#680] Move policy engine converter to s3-gw
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
parent
e788bb6ec9
commit
0ba6989197
21 changed files with 4325 additions and 50 deletions
479
pkg/policy-engine/common/policy_test.go
Normal file
479
pkg/policy-engine/common/policy_test.go
Normal file
|
@ -0,0 +1,479 @@
|
|||
package common
|
||||
|
||||
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 := Policy{
|
||||
Version: "2012-10-17",
|
||||
ID: "PutObjPolicy",
|
||||
Statement: []Statement{{
|
||||
SID: "DenyObjectsThatAreNotSSEKMS",
|
||||
Principal: map[PrincipalType][]string{
|
||||
"*": nil,
|
||||
},
|
||||
Effect: DenyEffect,
|
||||
Action: []string{"s3:PutObject"},
|
||||
Resource: []string{"arn:aws:s3:::DOC-EXAMPLE-BUCKET/*"},
|
||||
Conditions: map[string]Condition{
|
||||
"Null": {
|
||||
"s3:x-amz-server-side-encryption-aws-kms-key-id": {"true"},
|
||||
},
|
||||
},
|
||||
}},
|
||||
}
|
||||
|
||||
var p Policy
|
||||
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 := Policy{
|
||||
Version: "2012-10-17",
|
||||
Statement: []Statement{{
|
||||
Principal: map[PrincipalType][]string{
|
||||
AWSPrincipalType: {"arn:aws:iam::111122223333:user/JohnDoe"},
|
||||
},
|
||||
Effect: AllowEffect,
|
||||
Action: []string{"s3:PutObject"},
|
||||
Resource: []string{"arn:aws:s3:::DOC-EXAMPLE-BUCKET/*"},
|
||||
Conditions: map[string]Condition{
|
||||
"StringEquals": {
|
||||
"s3:RequestObjectTag/Department": {"Finance"},
|
||||
},
|
||||
},
|
||||
}},
|
||||
}
|
||||
|
||||
var p Policy
|
||||
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 := Policy{
|
||||
Statement: []Statement{{
|
||||
Principal: map[PrincipalType][]string{
|
||||
AWSPrincipalType: {"arn:aws:iam::111122223333:user/JohnDoe"},
|
||||
},
|
||||
}},
|
||||
}
|
||||
|
||||
var p Policy
|
||||
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 Policy
|
||||
err := json.Unmarshal([]byte(policy), &p)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("condition array", func(t *testing.T) {
|
||||
policy := `
|
||||
{
|
||||
"Statement": [{
|
||||
"Condition": {"StringLike": {"ec2:InstanceType": ["t1.*", "t2.*", "m3.*"]}}
|
||||
}]
|
||||
}`
|
||||
|
||||
expected := Policy{
|
||||
Statement: []Statement{{
|
||||
Conditions: map[string]Condition{
|
||||
"StringLike": {"ec2:InstanceType": {"t1.*", "t2.*", "m3.*"}},
|
||||
},
|
||||
}},
|
||||
}
|
||||
|
||||
var p Policy
|
||||
err := json.Unmarshal([]byte(policy), &p)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected, p)
|
||||
})
|
||||
|
||||
t.Run("'Not*' fields", func(t *testing.T) {
|
||||
policy := `
|
||||
{
|
||||
"Id": "PutObjPolicy",
|
||||
"Statement": [{
|
||||
"NotPrincipal": {"AWS":["arn:aws:iam::111122223333:user/Alice"]},
|
||||
"Effect": "Deny",
|
||||
"NotAction": "s3:PutObject",
|
||||
"NotResource": "arn:aws:s3:::DOC-EXAMPLE-BUCKET/*"
|
||||
}]
|
||||
}`
|
||||
|
||||
expected := Policy{
|
||||
ID: "PutObjPolicy",
|
||||
Statement: []Statement{{
|
||||
NotPrincipal: map[PrincipalType][]string{
|
||||
AWSPrincipalType: {"arn:aws:iam::111122223333:user/Alice"},
|
||||
},
|
||||
Effect: DenyEffect,
|
||||
NotAction: []string{"s3:PutObject"},
|
||||
NotResource: []string{"arn:aws:s3:::DOC-EXAMPLE-BUCKET/*"},
|
||||
}},
|
||||
}
|
||||
|
||||
var p Policy
|
||||
err := json.Unmarshal([]byte(policy), &p)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected, p)
|
||||
})
|
||||
}
|
||||
|
||||
func TestValidatePolicies(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
policy Policy
|
||||
typ PolicyType
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
name: "valid permission boundaries",
|
||||
policy: Policy{
|
||||
Version: policyVersion,
|
||||
Statement: []Statement{{
|
||||
Effect: AllowEffect,
|
||||
Action: []string{"s3:*", "cloudwatch:*", "ec2:*"},
|
||||
Resource: []string{Wildcard},
|
||||
}},
|
||||
},
|
||||
typ: GeneralPolicyType,
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
name: "general invalid effect",
|
||||
policy: Policy{
|
||||
Version: policyVersion,
|
||||
Statement: []Statement{{
|
||||
Effect: "dummy",
|
||||
Action: []string{"s3:*", "cloudwatch:*", "ec2:*"},
|
||||
Resource: []string{Wildcard},
|
||||
}},
|
||||
},
|
||||
typ: GeneralPolicyType,
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "general invalid principal block",
|
||||
policy: Policy{
|
||||
Version: policyVersion,
|
||||
Statement: []Statement{{
|
||||
Effect: AllowEffect,
|
||||
Action: []string{"s3:*", "cloudwatch:*", "ec2:*"},
|
||||
Resource: []string{Wildcard},
|
||||
Principal: map[PrincipalType][]string{Wildcard: nil},
|
||||
NotPrincipal: map[PrincipalType][]string{Wildcard: nil},
|
||||
}},
|
||||
},
|
||||
typ: GeneralPolicyType,
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "general invalid not principal",
|
||||
policy: Policy{
|
||||
Version: policyVersion,
|
||||
Statement: []Statement{{
|
||||
Effect: AllowEffect,
|
||||
Action: []string{"s3:*", "cloudwatch:*", "ec2:*"},
|
||||
Resource: []string{Wildcard},
|
||||
NotPrincipal: map[PrincipalType][]string{AWSPrincipalType: {"arn:aws:iam::111122223333:user/Alice"}},
|
||||
}},
|
||||
},
|
||||
typ: GeneralPolicyType,
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "general invalid principal type",
|
||||
policy: Policy{
|
||||
Version: policyVersion,
|
||||
Statement: []Statement{{
|
||||
Effect: AllowEffect,
|
||||
Action: []string{"s3:*", "cloudwatch:*", "ec2:*"},
|
||||
Resource: []string{Wildcard},
|
||||
NotPrincipal: map[PrincipalType][]string{"dummy": {"arn:aws:iam::111122223333:user/Alice"}},
|
||||
}},
|
||||
},
|
||||
typ: GeneralPolicyType,
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "general invalid action block",
|
||||
policy: Policy{
|
||||
Version: policyVersion,
|
||||
Statement: []Statement{{
|
||||
Effect: AllowEffect,
|
||||
Action: []string{"s3:*", "cloudwatch:*", "ec2:*"},
|
||||
NotAction: []string{"iam:*"},
|
||||
Resource: []string{Wildcard},
|
||||
}},
|
||||
},
|
||||
typ: GeneralPolicyType,
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "general invalid resource block",
|
||||
policy: Policy{
|
||||
Version: policyVersion,
|
||||
Statement: []Statement{{
|
||||
Effect: AllowEffect,
|
||||
Resource: []string{Wildcard},
|
||||
NotResource: []string{"arn:aws:s3:::DOC-EXAMPLE-BUCKET/*"},
|
||||
}},
|
||||
},
|
||||
typ: GeneralPolicyType,
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "invalid resource block",
|
||||
policy: Policy{
|
||||
Version: policyVersion,
|
||||
Statement: []Statement{{
|
||||
Effect: AllowEffect,
|
||||
Resource: []string{},
|
||||
NotResource: []string{"arn:aws:s3:::DOC-EXAMPLE-BUCKET/*"},
|
||||
}},
|
||||
},
|
||||
typ: GeneralPolicyType,
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "missing resource block",
|
||||
policy: Policy{
|
||||
Version: policyVersion,
|
||||
Statement: []Statement{{
|
||||
Effect: AllowEffect,
|
||||
}},
|
||||
},
|
||||
typ: GeneralPolicyType,
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "missing statement block",
|
||||
policy: Policy{},
|
||||
typ: GeneralPolicyType,
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "duplicate sid",
|
||||
policy: Policy{
|
||||
Version: policyVersion,
|
||||
Statement: []Statement{
|
||||
{
|
||||
SID: "sid",
|
||||
Effect: AllowEffect,
|
||||
Action: []string{"s3:*"},
|
||||
Resource: []string{Wildcard},
|
||||
},
|
||||
{
|
||||
SID: "sid",
|
||||
Effect: AllowEffect,
|
||||
Action: []string{"cloudwatch:*"},
|
||||
Resource: []string{Wildcard},
|
||||
}},
|
||||
},
|
||||
typ: GeneralPolicyType,
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "missing version",
|
||||
policy: Policy{
|
||||
Statement: []Statement{{
|
||||
Effect: AllowEffect,
|
||||
Action: []string{"s3:*"},
|
||||
Resource: []string{Wildcard},
|
||||
}},
|
||||
},
|
||||
typ: GeneralPolicyType,
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "identity based valid",
|
||||
policy: Policy{
|
||||
Version: policyVersion,
|
||||
Statement: []Statement{{
|
||||
Effect: AllowEffect,
|
||||
Action: []string{"s3:PutObject"},
|
||||
Resource: []string{Wildcard},
|
||||
}},
|
||||
},
|
||||
typ: IdentityBasedPolicyType,
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
name: "identity based invalid because of id presence",
|
||||
policy: Policy{
|
||||
ID: "some-id",
|
||||
Version: policyVersion,
|
||||
Statement: []Statement{{
|
||||
Effect: AllowEffect,
|
||||
Action: []string{"s3:PutObject"},
|
||||
Resource: []string{Wildcard},
|
||||
}},
|
||||
},
|
||||
typ: IdentityBasedPolicyType,
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "identity based invalid because of principal presence",
|
||||
policy: Policy{
|
||||
Version: policyVersion,
|
||||
Statement: []Statement{{
|
||||
Effect: AllowEffect,
|
||||
Action: []string{"s3:PutObject"},
|
||||
Resource: []string{Wildcard},
|
||||
Principal: map[PrincipalType][]string{AWSPrincipalType: {"arn:aws:iam::111122223333:user/Alice"}},
|
||||
}},
|
||||
},
|
||||
typ: IdentityBasedPolicyType,
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "identity based invalid because of not principal presence",
|
||||
policy: Policy{
|
||||
Version: policyVersion,
|
||||
Statement: []Statement{{
|
||||
Effect: AllowEffect,
|
||||
Action: []string{"s3:PutObject"},
|
||||
Resource: []string{Wildcard},
|
||||
NotPrincipal: map[PrincipalType][]string{AWSPrincipalType: {"arn:aws:iam::111122223333:user/Alice"}},
|
||||
}},
|
||||
},
|
||||
typ: IdentityBasedPolicyType,
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: "resource based valid principal",
|
||||
policy: Policy{
|
||||
Version: policyVersion,
|
||||
Statement: []Statement{{
|
||||
Effect: DenyEffect,
|
||||
Action: []string{"s3:PutObject"},
|
||||
Resource: []string{Wildcard},
|
||||
Principal: map[PrincipalType][]string{AWSPrincipalType: {"arn:aws:iam::111122223333:user/Alice"}},
|
||||
}},
|
||||
},
|
||||
typ: ResourceBasedPolicyType,
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
name: "resource based valid not principal",
|
||||
policy: Policy{
|
||||
ID: "some-id",
|
||||
Version: policyVersion,
|
||||
Statement: []Statement{{
|
||||
Effect: DenyEffect,
|
||||
Action: []string{"s3:PutObject"},
|
||||
Resource: []string{Wildcard},
|
||||
NotPrincipal: map[PrincipalType][]string{AWSPrincipalType: {"arn:aws:iam::111122223333:user/Alice"}},
|
||||
}},
|
||||
},
|
||||
typ: ResourceBasedPolicyType,
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
name: "resource based invalid missing principal",
|
||||
policy: Policy{
|
||||
ID: "some-id",
|
||||
Version: policyVersion,
|
||||
Statement: []Statement{{
|
||||
Effect: AllowEffect,
|
||||
Action: []string{"s3:PutObject"},
|
||||
Resource: []string{Wildcard},
|
||||
}},
|
||||
},
|
||||
typ: ResourceBasedPolicyType,
|
||||
isValid: false,
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := tc.policy.Validate(tc.typ)
|
||||
if tc.isValid {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.Error(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue