Files
seaweedfs/weed/s3api/s3api_bucket_cors_handlers.go
Chris Lu 33b9017b48 fix listing objects (#7008)
* fix listing objects

* add more list testing

* address comments

* fix next marker

* fix isTruncated in listing

* fix tests

* address tests

* Update s3api_object_handlers_multipart.go

* fixes

* store json into bucket content, for tagging and cors

* switch bucket metadata from json to proto

* fix

* Update s3api_bucket_config.go

* fix test issue

* fix test_bucket_listv2_delimiter_prefix

* Update cors.go

* skip special characters

* passing listing

* fix test_bucket_list_delimiter_prefix

* ok. fix the xsd generated go code now

* fix cors tests

* fix test

* fix test_bucket_list_unordered and test_bucket_listv2_unordered

do not accept the allow-unordered and delimiter parameter combination

* fix test_bucket_list_objects_anonymous and test_bucket_listv2_objects_anonymous

The tests test_bucket_list_objects_anonymous and test_bucket_listv2_objects_anonymous were failing because they try to set bucket ACL to public-read, but SeaweedFS only supported private ACL.

Updated PutBucketAclHandler to use the existing ExtractAcl function which already supports all standard S3 canned ACLs
Replaced the hardcoded check for only private ACL with proper ACL parsing that handles public-read, public-read-write, authenticated-read, bucket-owner-read, bucket-owner-full-control, etc.
Added unit tests to verify all standard canned ACLs are accepted

* fix list unordered

The test is expecting the error code to be InvalidArgument instead of InvalidRequest

* allow anonymous listing( and head, get)

* fix test_bucket_list_maxkeys_invalid

Invalid values: max-keys=blah → Returns ErrInvalidMaxKeys (HTTP 400)

* updating IsPublicRead when parsing acl

* more logs

* CORS Test Fix

* fix test_bucket_list_return_data

* default to private

* fix test_bucket_list_delimiter_not_skip_special

* default no acl

* add debug logging

* more logs

* use basic http client

remove logs also

* fixes

* debug

* Update stats.go

* debugging

* fix anonymous test expectation

anonymous user can read, as configured in s3 json.
2025-07-22 01:07:15 -07:00

130 lines
4.2 KiB
Go

package s3api
import (
"encoding/xml"
"net/http"
"github.com/seaweedfs/seaweedfs/weed/glog"
"github.com/seaweedfs/seaweedfs/weed/s3api/cors"
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
"github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
)
// S3BucketChecker implements cors.BucketChecker interface
type S3BucketChecker struct {
server *S3ApiServer
}
func (c *S3BucketChecker) CheckBucket(r *http.Request, bucket string) s3err.ErrorCode {
return c.server.checkBucket(r, bucket)
}
// S3CORSConfigGetter implements cors.CORSConfigGetter interface
type S3CORSConfigGetter struct {
server *S3ApiServer
}
func (g *S3CORSConfigGetter) GetCORSConfiguration(bucket string) (*cors.CORSConfiguration, s3err.ErrorCode) {
return g.server.getCORSConfiguration(bucket)
}
// getCORSMiddleware returns a CORS middleware instance with caching
func (s3a *S3ApiServer) getCORSMiddleware() *cors.Middleware {
bucketChecker := &S3BucketChecker{server: s3a}
corsConfigGetter := &S3CORSConfigGetter{server: s3a}
return cors.NewMiddleware(bucketChecker, corsConfigGetter)
}
// GetBucketCorsHandler handles Get bucket CORS configuration
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketCors.html
func (s3a *S3ApiServer) GetBucketCorsHandler(w http.ResponseWriter, r *http.Request) {
bucket, _ := s3_constants.GetBucketAndObject(r)
glog.V(3).Infof("GetBucketCorsHandler %s", bucket)
if err := s3a.checkBucket(r, bucket); err != s3err.ErrNone {
s3err.WriteErrorResponse(w, r, err)
return
}
// Load CORS configuration from cache
config, errCode := s3a.getCORSConfiguration(bucket)
if errCode != s3err.ErrNone {
if errCode == s3err.ErrNoSuchBucket {
s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchBucket)
} else {
s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
}
return
}
if config == nil {
s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchCORSConfiguration)
return
}
// Return CORS configuration as XML
writeSuccessResponseXML(w, r, config)
}
// PutBucketCorsHandler handles Put bucket CORS configuration
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketCors.html
func (s3a *S3ApiServer) PutBucketCorsHandler(w http.ResponseWriter, r *http.Request) {
bucket, _ := s3_constants.GetBucketAndObject(r)
glog.V(3).Infof("PutBucketCorsHandler %s", bucket)
if err := s3a.checkBucket(r, bucket); err != s3err.ErrNone {
s3err.WriteErrorResponse(w, r, err)
return
}
// Parse CORS configuration from request body
var config cors.CORSConfiguration
if err := xml.NewDecoder(r.Body).Decode(&config); err != nil {
glog.V(1).Infof("Failed to parse CORS configuration: %v", err)
s3err.WriteErrorResponse(w, r, s3err.ErrMalformedXML)
return
}
// Validate CORS configuration
if err := cors.ValidateConfiguration(&config); err != nil {
glog.V(1).Infof("Invalid CORS configuration: %v", err)
s3err.WriteErrorResponse(w, r, s3err.ErrInvalidRequest)
return
}
// Store CORS configuration and update cache
// This handles both cache update and persistent storage through the unified bucket config system
if err := s3a.updateCORSConfiguration(bucket, &config); err != s3err.ErrNone {
glog.Errorf("Failed to update CORS configuration: %v", err)
s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
return
}
// Return success
writeSuccessResponseEmpty(w, r)
}
// DeleteBucketCorsHandler handles Delete bucket CORS configuration
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketCors.html
func (s3a *S3ApiServer) DeleteBucketCorsHandler(w http.ResponseWriter, r *http.Request) {
bucket, _ := s3_constants.GetBucketAndObject(r)
glog.V(3).Infof("DeleteBucketCorsHandler %s", bucket)
if err := s3a.checkBucket(r, bucket); err != s3err.ErrNone {
s3err.WriteErrorResponse(w, r, err)
return
}
// Remove CORS configuration from cache and persistent storage
// This handles both cache invalidation and persistent storage cleanup through the unified bucket config system
if err := s3a.removeCORSConfiguration(bucket); err != s3err.ErrNone {
glog.Errorf("Failed to remove CORS configuration: %v", err)
s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
return
}
// Return success (204 No Content)
w.WriteHeader(http.StatusNoContent)
}