mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-05-23 10:11:28 +00:00
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
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user