aboutsummaryrefslogtreecommitdiff
path: root/weed/iam/policy/policy_variable_matching_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'weed/iam/policy/policy_variable_matching_test.go')
-rw-r--r--weed/iam/policy/policy_variable_matching_test.go191
1 files changed, 191 insertions, 0 deletions
diff --git a/weed/iam/policy/policy_variable_matching_test.go b/weed/iam/policy/policy_variable_matching_test.go
new file mode 100644
index 000000000..6b9827dff
--- /dev/null
+++ b/weed/iam/policy/policy_variable_matching_test.go
@@ -0,0 +1,191 @@
+package policy
+
+import (
+ "context"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+// TestPolicyVariableMatchingInActionsAndResources tests that Actions and Resources
+// now support policy variables like ${aws:username} just like string conditions do
+func TestPolicyVariableMatchingInActionsAndResources(t *testing.T) {
+ engine := NewPolicyEngine()
+ config := &PolicyEngineConfig{
+ DefaultEffect: "Deny",
+ StoreType: "memory",
+ }
+
+ err := engine.Initialize(config)
+ require.NoError(t, err)
+
+ ctx := context.Background()
+ filerAddress := ""
+
+ // Create a policy that uses policy variables in Action and Resource fields
+ policyDoc := &PolicyDocument{
+ Version: "2012-10-17",
+ Statement: []Statement{
+ {
+ Sid: "AllowUserSpecificActions",
+ Effect: "Allow",
+ Action: []string{
+ "s3:Get*", // Regular wildcard
+ "s3:${aws:principaltype}*", // Policy variable in action
+ },
+ Resource: []string{
+ "arn:aws:s3:::user-${aws:username}/*", // Policy variable in resource
+ "arn:aws:s3:::shared/${saml:username}/*", // Different policy variable
+ },
+ },
+ },
+ }
+
+ err = engine.AddPolicy(filerAddress, "user-specific-policy", policyDoc)
+ require.NoError(t, err)
+
+ tests := []struct {
+ name string
+ principal string
+ action string
+ resource string
+ requestContext map[string]interface{}
+ expectedEffect Effect
+ description string
+ }{
+ {
+ name: "policy_variable_in_action_matches",
+ principal: "test-user",
+ action: "s3:AssumedRole", // Should match s3:${aws:principaltype}* when principaltype=AssumedRole
+ resource: "arn:aws:s3:::user-testuser/file.txt",
+ requestContext: map[string]interface{}{
+ "aws:username": "testuser",
+ "aws:principaltype": "AssumedRole",
+ },
+ expectedEffect: EffectAllow,
+ description: "Action with policy variable should match when variable is expanded",
+ },
+ {
+ name: "policy_variable_in_resource_matches",
+ principal: "alice",
+ action: "s3:GetObject",
+ resource: "arn:aws:s3:::user-alice/document.pdf", // Should match user-${aws:username}/*
+ requestContext: map[string]interface{}{
+ "aws:username": "alice",
+ },
+ expectedEffect: EffectAllow,
+ description: "Resource with policy variable should match when variable is expanded",
+ },
+ {
+ name: "saml_username_variable_in_resource",
+ principal: "bob",
+ action: "s3:GetObject",
+ resource: "arn:aws:s3:::shared/bob/data.json", // Should match shared/${saml:username}/*
+ requestContext: map[string]interface{}{
+ "saml:username": "bob",
+ },
+ expectedEffect: EffectAllow,
+ description: "SAML username variable should be expanded in resource patterns",
+ },
+ {
+ name: "policy_variable_no_match_wrong_user",
+ principal: "charlie",
+ action: "s3:GetObject",
+ resource: "arn:aws:s3:::user-alice/file.txt", // charlie trying to access alice's files
+ requestContext: map[string]interface{}{
+ "aws:username": "charlie",
+ },
+ expectedEffect: EffectDeny,
+ description: "Policy variable should prevent access when username doesn't match",
+ },
+ {
+ name: "missing_policy_variable_context",
+ principal: "dave",
+ action: "s3:GetObject",
+ resource: "arn:aws:s3:::user-dave/file.txt",
+ requestContext: map[string]interface{}{
+ // Missing aws:username context
+ },
+ expectedEffect: EffectDeny,
+ description: "Missing policy variable context should result in no match",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ evalCtx := &EvaluationContext{
+ Principal: tt.principal,
+ Action: tt.action,
+ Resource: tt.resource,
+ RequestContext: tt.requestContext,
+ }
+
+ result, err := engine.Evaluate(ctx, filerAddress, evalCtx, []string{"user-specific-policy"})
+ require.NoError(t, err, "Policy evaluation should not error")
+
+ assert.Equal(t, tt.expectedEffect, result.Effect,
+ "Test %s: %s. Expected %s but got %s",
+ tt.name, tt.description, tt.expectedEffect, result.Effect)
+ })
+ }
+}
+
+// TestActionResourceConsistencyWithStringConditions verifies that Actions, Resources,
+// and string conditions all use the same AWS IAM-compliant matching logic
+func TestActionResourceConsistencyWithStringConditions(t *testing.T) {
+ engine := NewPolicyEngine()
+ config := &PolicyEngineConfig{
+ DefaultEffect: "Deny",
+ StoreType: "memory",
+ }
+
+ err := engine.Initialize(config)
+ require.NoError(t, err)
+
+ ctx := context.Background()
+ filerAddress := ""
+
+ // Policy that uses case-insensitive matching in all three areas
+ policyDoc := &PolicyDocument{
+ Version: "2012-10-17",
+ Statement: []Statement{
+ {
+ Sid: "CaseInsensitiveMatching",
+ Effect: "Allow",
+ Action: []string{"S3:GET*"}, // Uppercase action pattern
+ Resource: []string{"arn:aws:s3:::TEST-BUCKET/*"}, // Uppercase resource pattern
+ Condition: map[string]map[string]interface{}{
+ "StringLike": {
+ "s3:RequestedRegion": "US-*", // Uppercase condition pattern
+ },
+ },
+ },
+ },
+ }
+
+ err = engine.AddPolicy(filerAddress, "case-insensitive-policy", policyDoc)
+ require.NoError(t, err)
+
+ evalCtx := &EvaluationContext{
+ Principal: "test-user",
+ Action: "s3:getobject", // lowercase action
+ Resource: "arn:aws:s3:::test-bucket/file.txt", // lowercase resource
+ RequestContext: map[string]interface{}{
+ "s3:RequestedRegion": "us-east-1", // lowercase condition value
+ },
+ }
+
+ result, err := engine.Evaluate(ctx, filerAddress, evalCtx, []string{"case-insensitive-policy"})
+ require.NoError(t, err)
+
+ // All should match due to case-insensitive AWS IAM-compliant matching
+ assert.Equal(t, EffectAllow, result.Effect,
+ "Actions, Resources, and Conditions should all use case-insensitive AWS IAM matching")
+
+ // Verify that matching statements were found
+ assert.Len(t, result.MatchingStatements, 1,
+ "Should have exactly one matching statement")
+ assert.Equal(t, "Allow", string(result.MatchingStatements[0].Effect),
+ "Matching statement should have Allow effect")
+}