diff options
| author | chrislu <chris.lu@gmail.com> | 2025-12-08 23:58:13 -0800 |
|---|---|---|
| committer | chrislu <chris.lu@gmail.com> | 2025-12-09 00:01:31 -0800 |
| commit | c6721bb18d8f70ec9e86b6aa043b488d2d2f0239 (patch) | |
| tree | df61724296cab7caa59d2b2627956bc02a839b04 | |
| parent | d5f21fd8ba6ee20c2504455093a9ceeaa178b826 (diff) | |
| download | seaweedfs-c6721bb18d8f70ec9e86b6aa043b488d2d2f0239.tar.xz seaweedfs-c6721bb18d8f70ec9e86b6aa043b488d2d2f0239.zip | |
s3: add s3:ExistingObjectTag condition support in policy engine
Add support for s3:ExistingObjectTag/<tag-key> condition keys in bucket
policies, allowing access control based on object tags.
Changes:
- Add ObjectEntry field to PolicyEvaluationArgs (entry.Extended metadata)
- Update EvaluateConditions to handle s3:ExistingObjectTag/<key> format
- Extract tag value from entry metadata using X-Amz-Tagging-<key> prefix
This enables policies like:
{
"Condition": {
"StringEquals": {
"s3:ExistingObjectTag/status": ["public"]
}
}
}
Fixes: https://github.com/seaweedfs/seaweedfs/issues/7447
| -rw-r--r-- | weed/s3api/policy_engine/conditions.go | 56 | ||||
| -rw-r--r-- | weed/s3api/policy_engine/engine.go | 2 | ||||
| -rw-r--r-- | weed/s3api/policy_engine/types.go | 5 |
3 files changed, 54 insertions, 9 deletions
diff --git a/weed/s3api/policy_engine/conditions.go b/weed/s3api/policy_engine/conditions.go index fc8005fd0..feb582e89 100644 --- a/weed/s3api/policy_engine/conditions.go +++ b/weed/s3api/policy_engine/conditions.go @@ -705,8 +705,15 @@ func GetConditionEvaluator(operator string) (ConditionEvaluator, error) { } } +// ExistingObjectTagPrefix is the prefix for object tag condition keys +const ExistingObjectTagPrefix = "s3:ExistingObjectTag/" + +// ObjectTagMetadataPrefix is the prefix used to store tags in entry.Extended +const ObjectTagMetadataPrefix = "X-Amz-Tagging-" + // EvaluateConditions evaluates all conditions in a policy statement -func EvaluateConditions(conditions PolicyConditions, contextValues map[string][]string) bool { +// objectEntry is the object's metadata from entry.Extended (can be nil) +func EvaluateConditions(conditions PolicyConditions, contextValues map[string][]string, objectEntry map[string][]byte) bool { if len(conditions) == 0 { return true // No conditions means always true } @@ -719,9 +726,27 @@ func EvaluateConditions(conditions PolicyConditions, contextValues map[string][] } for key, value := range conditionMap { - contextVals, exists := contextValues[key] - if !exists { - contextVals = []string{} + var contextVals []string + + // Handle s3:ExistingObjectTag/<tag-key> condition keys + // These refer to tags that already exist on the object + if strings.HasPrefix(key, ExistingObjectTagPrefix) { + // Extract tag value from entry.Extended using the tag prefix + tagKey := key[len(ExistingObjectTagPrefix):] + metadataKey := ObjectTagMetadataPrefix + tagKey + if objectEntry != nil { + if tagValue, exists := objectEntry[metadataKey]; exists { + contextVals = []string{string(tagValue)} + } + } + // If tag doesn't exist, contextVals remains empty + } else { + // Regular condition key lookup + var exists bool + contextVals, exists = contextValues[key] + if !exists { + contextVals = []string{} + } } if !conditionEvaluator.Evaluate(value.Strings(), contextVals) { @@ -734,7 +759,8 @@ func EvaluateConditions(conditions PolicyConditions, contextValues map[string][] } // EvaluateConditionsLegacy evaluates conditions using the old interface{} format for backward compatibility -func EvaluateConditionsLegacy(conditions map[string]interface{}, contextValues map[string][]string) bool { +// objectEntry is the object's metadata from entry.Extended (can be nil) +func EvaluateConditionsLegacy(conditions map[string]interface{}, contextValues map[string][]string, objectEntry map[string][]byte) bool { if len(conditions) == 0 { return true // No conditions means always true } @@ -753,9 +779,23 @@ func EvaluateConditionsLegacy(conditions map[string]interface{}, contextValues m } for key, value := range conditionMapTyped { - contextVals, exists := contextValues[key] - if !exists { - contextVals = []string{} + var contextVals []string + + // Handle s3:ExistingObjectTag/<tag-key> condition keys + if strings.HasPrefix(key, ExistingObjectTagPrefix) { + tagKey := key[len(ExistingObjectTagPrefix):] + metadataKey := ObjectTagMetadataPrefix + tagKey + if objectEntry != nil { + if tagValue, exists := objectEntry[metadataKey]; exists { + contextVals = []string{string(tagValue)} + } + } + } else { + var exists bool + contextVals, exists = contextValues[key] + if !exists { + contextVals = []string{} + } } if !conditionEvaluator.Evaluate(value, contextVals) { diff --git a/weed/s3api/policy_engine/engine.go b/weed/s3api/policy_engine/engine.go index 01af3c240..57a13881c 100644 --- a/weed/s3api/policy_engine/engine.go +++ b/weed/s3api/policy_engine/engine.go @@ -154,7 +154,7 @@ func (engine *PolicyEngine) evaluateStatement(stmt *CompiledStatement, args *Pol // Check conditions if len(stmt.Statement.Condition) > 0 { - if !EvaluateConditions(stmt.Statement.Condition, args.Conditions) { + if !EvaluateConditions(stmt.Statement.Condition, args.Conditions, args.ObjectEntry) { return false } } diff --git a/weed/s3api/policy_engine/types.go b/weed/s3api/policy_engine/types.go index d68b1f297..a8f822fb8 100644 --- a/weed/s3api/policy_engine/types.go +++ b/weed/s3api/policy_engine/types.go @@ -106,6 +106,11 @@ type PolicyEvaluationArgs struct { Resource string Principal string Conditions map[string][]string + // ObjectEntry is the object's metadata from entry.Extended. + // Used for evaluating conditions like s3:ExistingObjectTag/<tag-key>. + // Tags are stored as "X-Amz-Tagging-<key>" -> value. + // Can be nil for bucket-level operations or when object doesn't exist. + ObjectEntry map[string][]byte } // PolicyCache for caching compiled policies |
