Browse Source
S3: set identity to request context, and remove obsolete code (#7523)
S3: set identity to request context, and remove obsolete code (#7523)
* list owned buckets * simplify * add unit tests * no-owner buckets * set identity id * fallback to request header if iam is not enabled * refactor to test * fix comparing * fix security vulnerability * Update s3api_bucket_handlers.go * Update s3api_bucket_handlers.go * Update s3api_bucket_handlers.go * set identity to request context * remove SeaweedFSIsDirectoryKey * remove obsolete * simplify * reuse * refactor or remove obsolete logic on filer * Removed the redundant check in GetOrHeadHandler * surfacing invalid X-Amz-Tagging as a client error * clean up * constant * reuse * multiple header values * code reuse * err on duplicated tag keypull/7490/merge
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 152 additions and 157 deletions
-
4weed/s3api/auth_credentials.go
-
29weed/s3api/s3_constants/header.go
-
94weed/s3api/s3_metadata_util.go
-
20weed/s3api/s3api_bucket_handlers.go
-
8weed/s3api/s3api_object_handlers_multipart.go
-
2weed/s3api/s3err/audit_fluent.go
-
58weed/server/filer_server_handlers_read.go
-
94weed/server/filer_server_handlers_write_autochunk.go
@ -0,0 +1,94 @@ |
|||
package s3api |
|||
|
|||
import ( |
|||
"net/http" |
|||
"net/url" |
|||
"strings" |
|||
|
|||
"github.com/seaweedfs/seaweedfs/weed/glog" |
|||
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants" |
|||
"github.com/seaweedfs/seaweedfs/weed/s3api/s3err" |
|||
) |
|||
|
|||
// ParseS3Metadata extracts S3-specific metadata from HTTP request headers
|
|||
// This includes: storage class, tags, user metadata, SSE headers, and ACL headers
|
|||
// Used by S3 API handlers to prepare metadata before saving to filer
|
|||
// Returns an S3 error code if tag parsing fails
|
|||
func ParseS3Metadata(r *http.Request, existing map[string][]byte, isReplace bool) (metadata map[string][]byte, errCode s3err.ErrorCode) { |
|||
metadata = make(map[string][]byte) |
|||
|
|||
// Copy existing metadata unless replacing
|
|||
if !isReplace { |
|||
for k, v := range existing { |
|||
metadata[k] = v |
|||
} |
|||
} |
|||
|
|||
// Storage class
|
|||
if sc := r.Header.Get(s3_constants.AmzStorageClass); sc != "" { |
|||
metadata[s3_constants.AmzStorageClass] = []byte(sc) |
|||
} |
|||
|
|||
// Content-Encoding (standard HTTP header used by S3)
|
|||
if ce := r.Header.Get("Content-Encoding"); ce != "" { |
|||
metadata["Content-Encoding"] = []byte(ce) |
|||
} |
|||
|
|||
// Object tagging
|
|||
if tags := r.Header.Get(s3_constants.AmzObjectTagging); tags != "" { |
|||
// Use url.ParseQuery for robust parsing and automatic URL decoding
|
|||
parsedTags, err := url.ParseQuery(tags) |
|||
if err != nil { |
|||
// Return proper S3 error instead of silently dropping tags
|
|||
glog.Warningf("Invalid S3 tag format in header '%s': %v", tags, err) |
|||
return nil, s3err.ErrInvalidTag |
|||
} |
|||
|
|||
// Validate: S3 spec does not allow duplicate tag keys
|
|||
for key, values := range parsedTags { |
|||
if len(values) > 1 { |
|||
glog.Warningf("Duplicate tag key '%s' in header '%s'", key, tags) |
|||
return nil, s3err.ErrInvalidTag |
|||
} |
|||
// Tag value can be an empty string but not nil
|
|||
value := "" |
|||
if len(values) > 0 { |
|||
value = values[0] |
|||
} |
|||
metadata[s3_constants.AmzObjectTagging+"-"+key] = []byte(value) |
|||
} |
|||
} |
|||
|
|||
// User-defined metadata (x-amz-meta-* headers)
|
|||
for header, values := range r.Header { |
|||
if strings.HasPrefix(header, s3_constants.AmzUserMetaPrefix) { |
|||
// Go's HTTP server canonicalizes headers (e.g., x-amz-meta-foo → X-Amz-Meta-Foo)
|
|||
// Per HTTP and S3 spec: multiple header values are concatenated with commas
|
|||
// This ensures no metadata is lost when clients send duplicate header names
|
|||
metadata[header] = []byte(strings.Join(values, ",")) |
|||
} |
|||
} |
|||
|
|||
// SSE-C headers
|
|||
if algorithm := r.Header.Get(s3_constants.AmzServerSideEncryptionCustomerAlgorithm); algorithm != "" { |
|||
metadata[s3_constants.AmzServerSideEncryptionCustomerAlgorithm] = []byte(algorithm) |
|||
} |
|||
if keyMD5 := r.Header.Get(s3_constants.AmzServerSideEncryptionCustomerKeyMD5); keyMD5 != "" { |
|||
// Store as-is; SSE-C MD5 is base64 and case-sensitive
|
|||
metadata[s3_constants.AmzServerSideEncryptionCustomerKeyMD5] = []byte(keyMD5) |
|||
} |
|||
|
|||
// ACL owner
|
|||
acpOwner := r.Header.Get(s3_constants.ExtAmzOwnerKey) |
|||
if len(acpOwner) > 0 { |
|||
metadata[s3_constants.ExtAmzOwnerKey] = []byte(acpOwner) |
|||
} |
|||
|
|||
// ACL grants
|
|||
acpGrants := r.Header.Get(s3_constants.ExtAmzAclKey) |
|||
if len(acpGrants) > 0 { |
|||
metadata[s3_constants.ExtAmzAclKey] = []byte(acpGrants) |
|||
} |
|||
|
|||
return metadata, s3err.ErrNone |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue