From 6a9e7360df71ff464feda8bc3774fe253b899137 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Fri, 30 Jan 2026 12:02:18 -0800 Subject: [PATCH] s3api: fix S3 Tables auth to allow auto-hashing of body (#8170) * s3api: allow auto-hashing of request body for s3tables * s3api: add unit test for s3tables body hashing --- weed/s3api/auth_signature_v4.go | 2 +- weed/s3api/auth_signature_v4_test.go | 61 ++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/weed/s3api/auth_signature_v4.go b/weed/s3api/auth_signature_v4.go index bf07df8c1..9c68a8359 100644 --- a/weed/s3api/auth_signature_v4.go +++ b/weed/s3api/auth_signature_v4.go @@ -477,7 +477,7 @@ func extractV4AuthInfoFromHeader(r *http.Request) (*v4AuthInfo, s3err.ErrorCode) } hashedPayload := getContentSha256Cksum(r) - if signV4Values.Credential.scope.service != "s3" && signV4Values.Credential.scope.service != "s3tables" && hashedPayload == emptySHA256 && r.Body != nil { + if signV4Values.Credential.scope.service != "s3" && hashedPayload == emptySHA256 && r.Body != nil { var hashErr error hashedPayload, hashErr = streamHashRequestBody(r, iamRequestBodyLimit) if hashErr != nil { diff --git a/weed/s3api/auth_signature_v4_test.go b/weed/s3api/auth_signature_v4_test.go index 9ec4f232e..849d895b5 100644 --- a/weed/s3api/auth_signature_v4_test.go +++ b/weed/s3api/auth_signature_v4_test.go @@ -1,10 +1,71 @@ package s3api import ( + "bytes" + "crypto/sha256" + "encoding/hex" + "fmt" "net/http" "testing" + "time" + + "github.com/seaweedfs/seaweedfs/weed/s3api/s3err" ) +func TestExtractV4AuthInfoFromHeader_S3Tables(t *testing.T) { + now := time.Now().UTC() + dateStr := now.Format(iso8601Format) + + tests := []struct { + name string + service string + body string + expectAutoHash bool + }{ + { + name: "s3 service should not auto-hash", + service: "s3", + body: "hello", + expectAutoHash: false, + }, + { + name: "s3tables service should auto-hash", + service: "s3tables", + body: "hello", + expectAutoHash: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + body := bytes.NewReader([]byte(tt.body)) + req, _ := http.NewRequest(http.MethodPost, "http://localhost/", body) + + authHeader := fmt.Sprintf("AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/%s/us-east-1/%s/aws4_request, SignedHeaders=host, Signature=dummy", + now.Format(yyyymmdd), tt.service) + req.Header.Set("Authorization", authHeader) + req.Header.Set("x-amz-date", dateStr) + + authInfo, errCode := extractV4AuthInfoFromHeader(req) + if errCode != s3err.ErrNone { + t.Fatalf("extractV4AuthInfoFromHeader failed: %v", errCode) + } + + if tt.expectAutoHash { + expectedHash := sha256.Sum256([]byte(tt.body)) + expectedHashStr := hex.EncodeToString(expectedHash[:]) + if authInfo.HashedPayload != expectedHashStr { + t.Errorf("Expected auto-hashed payload %s, got %s", expectedHashStr, authInfo.HashedPayload) + } + } else { + if authInfo.HashedPayload != emptySHA256 { + t.Errorf("Expected non-auto-hashed payload %s (emptySHA256), got %s", emptySHA256, authInfo.HashedPayload) + } + } + }) + } +} + func TestBuildPathWithForwardedPrefix(t *testing.T) { tests := []struct { name string