Fix S3 bucket policy ARN validation to accept AWS ARNs and simplified formats (#7409)
* Fix S3 bucket policy ARN validation to accept AWS ARNs and simplified formats Fixes #7252 The bucket policy validation was rejecting valid AWS-style ARNs and simplified resource formats, causing validation failures with the error 'resource X does not match bucket X' even when they were identical strings. Changes: - Updated validateResourceForBucket() to accept three formats: 1. AWS-style ARNs: arn:aws:s3:::bucket-name[/*|/path] 2. SeaweedFS ARNs: arn:seaweed:s3:::bucket-name[/*|/path] 3. Simplified formats: bucket-name[/*|/path] - Added comprehensive test coverage for all three formats - Added specific test cases from issue #7252 to prevent regression This ensures compatibility with standard AWS S3 bucket policies while maintaining support for SeaweedFS-specific ARN format. * Refactor validateResourceForBucket to reduce code duplication Simplified the validation logic by stripping ARN prefixes first, then performing validation on the remaining resource path. This reduces code duplication and improves maintainability while maintaining identical functionality. Addresses review feedback from Gemini Code Assist. * Use strings.CutPrefix for cleaner ARN prefix stripping Replace strings.HasPrefix checks with strings.CutPrefix for more idiomatic Go code. This function is available in Go 1.20+ and provides cleaner conditional logic with the combined check and extraction. Addresses review feedback from Gemini Code Assist.
This commit is contained in:
@@ -274,18 +274,38 @@ func (s3a *S3ApiServer) validateBucketPolicy(policyDoc *policy.PolicyDocument, b
|
||||
|
||||
// validateResourceForBucket checks if a resource ARN is valid for the given bucket
|
||||
func (s3a *S3ApiServer) validateResourceForBucket(resource, bucket string) bool {
|
||||
// Expected formats:
|
||||
// arn:seaweed:s3:::bucket-name
|
||||
// arn:seaweed:s3:::bucket-name/*
|
||||
// arn:seaweed:s3:::bucket-name/path/to/object
|
||||
// Accepted formats for S3 bucket policies:
|
||||
// AWS-style ARNs:
|
||||
// arn:aws:s3:::bucket-name
|
||||
// arn:aws:s3:::bucket-name/*
|
||||
// arn:aws:s3:::bucket-name/path/to/object
|
||||
// SeaweedFS ARNs:
|
||||
// arn:seaweed:s3:::bucket-name
|
||||
// arn:seaweed:s3:::bucket-name/*
|
||||
// arn:seaweed:s3:::bucket-name/path/to/object
|
||||
// Simplified formats (for convenience):
|
||||
// bucket-name
|
||||
// bucket-name/*
|
||||
// bucket-name/path/to/object
|
||||
|
||||
expectedBucketArn := fmt.Sprintf("arn:seaweed:s3:::%s", bucket)
|
||||
expectedBucketWildcard := fmt.Sprintf("arn:seaweed:s3:::%s/*", bucket)
|
||||
expectedBucketPath := fmt.Sprintf("arn:seaweed:s3:::%s/", bucket)
|
||||
var resourcePath string
|
||||
const awsPrefix = "arn:aws:s3:::"
|
||||
const seaweedPrefix = "arn:seaweed:s3:::"
|
||||
|
||||
return resource == expectedBucketArn ||
|
||||
resource == expectedBucketWildcard ||
|
||||
strings.HasPrefix(resource, expectedBucketPath)
|
||||
// Strip the optional ARN prefix to get the resource path
|
||||
if path, ok := strings.CutPrefix(resource, awsPrefix); ok {
|
||||
resourcePath = path
|
||||
} else if path, ok := strings.CutPrefix(resource, seaweedPrefix); ok {
|
||||
resourcePath = path
|
||||
} else {
|
||||
resourcePath = resource
|
||||
}
|
||||
|
||||
// After stripping the optional ARN prefix, the resource path must
|
||||
// either match the bucket name exactly, or be a path within the bucket.
|
||||
return resourcePath == bucket ||
|
||||
resourcePath == bucket+"/*" ||
|
||||
strings.HasPrefix(resourcePath, bucket+"/")
|
||||
}
|
||||
|
||||
// IAM integration functions
|
||||
|
||||
Reference in New Issue
Block a user