package s3api import ( "encoding/xml" "errors" "io" "net/http" "github.com/seaweedfs/seaweedfs/weed/glog" "github.com/seaweedfs/seaweedfs/weed/s3api/policy_engine" "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants" "github.com/seaweedfs/seaweedfs/weed/s3api/s3err" ) // putBucketRequestPaymentMaxBodyBytes caps the request body for PutBucketRequestPayment // to prevent DoS via large payloads. The valid payload is a few hundred bytes. const putBucketRequestPaymentMaxBodyBytes = 64 * 1024 type policyStatusResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ PolicyStatus"` IsPublic bool `xml:"IsPublic"` } type accelerateConfigurationResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ AccelerateConfiguration"` Status string `xml:"Status"` } type bucketLoggingStatusResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ BucketLoggingStatus"` } // GetBucketPolicyStatusHandler reports whether the bucket policy grants public access. // https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketPolicyStatus.html func (s3a *S3ApiServer) GetBucketPolicyStatusHandler(w http.ResponseWriter, r *http.Request) { bucket, _ := s3_constants.GetBucketAndObject(r) if err := s3a.checkBucket(r, bucket); err != s3err.ErrNone { s3err.WriteErrorResponse(w, r, err) return } policyDoc, err := s3a.getBucketPolicy(bucket) if err != nil { if errors.Is(err, ErrPolicyNotFound) { s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchBucketPolicy) } else if errors.Is(err, ErrBucketNotFound) { s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchBucket) } else { glog.Errorf("GetBucketPolicyStatusHandler load policy %s: %v", bucket, err) s3err.WriteErrorResponse(w, r, s3err.ErrInternalError) } return } writeSuccessResponseXML(w, r, policyStatusResponse{IsPublic: isPolicyPublic(policyDoc)}) } // isPolicyPublic returns true if any Allow statement grants access to "*" without restricting conditions. func isPolicyPublic(doc *policy_engine.PolicyDocument) bool { if doc == nil { return false } for _, st := range doc.Statement { if st.Effect != policy_engine.PolicyEffectAllow { continue } if len(st.Condition) > 0 { continue } for _, p := range st.Principal.Strings() { if p == "*" { return true } } } return false } // PutBucketRequestPaymentHandler accepts only Payer=BucketOwner; Requester is rejected. // https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketRequestPayment.html func (s3a *S3ApiServer) PutBucketRequestPaymentHandler(w http.ResponseWriter, r *http.Request) { bucket, _ := s3_constants.GetBucketAndObject(r) if err := s3a.checkBucket(r, bucket); err != s3err.ErrNone { s3err.WriteErrorResponse(w, r, err) return } r.Body = http.MaxBytesReader(w, r.Body, putBucketRequestPaymentMaxBodyBytes) defer r.Body.Close() body, err := io.ReadAll(r.Body) if err != nil { s3err.WriteErrorResponse(w, r, s3err.ErrMalformedXML) return } var cfg RequestPaymentConfiguration if err := xml.Unmarshal(body, &cfg); err != nil { s3err.WriteErrorResponse(w, r, s3err.ErrMalformedXML) return } if cfg.Payer != "BucketOwner" { s3err.WriteErrorResponse(w, r, s3err.ErrMalformedXML) return } writeSuccessResponseEmpty(w, r) } // GetBucketAccelerateConfigurationHandler returns a static Suspended status. // https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketAccelerateConfiguration.html func (s3a *S3ApiServer) GetBucketAccelerateConfigurationHandler(w http.ResponseWriter, r *http.Request) { bucket, _ := s3_constants.GetBucketAndObject(r) if err := s3a.checkBucket(r, bucket); err != s3err.ErrNone { s3err.WriteErrorResponse(w, r, err) return } writeSuccessResponseXML(w, r, accelerateConfigurationResponse{Status: "Suspended"}) } // GetBucketLoggingHandler returns an empty BucketLoggingStatus (logging disabled). // https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLogging.html func (s3a *S3ApiServer) GetBucketLoggingHandler(w http.ResponseWriter, r *http.Request) { bucket, _ := s3_constants.GetBucketAndObject(r) if err := s3a.checkBucket(r, bucket); err != s3err.ErrNone { s3err.WriteErrorResponse(w, r, err) return } writeSuccessResponseXML(w, r, bucketLoggingStatusResponse{}) }