aboutsummaryrefslogtreecommitdiff
path: root/weed/s3api/auth_signature_v2_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'weed/s3api/auth_signature_v2_test.go')
-rw-r--r--weed/s3api/auth_signature_v2_test.go284
1 files changed, 284 insertions, 0 deletions
diff --git a/weed/s3api/auth_signature_v2_test.go b/weed/s3api/auth_signature_v2_test.go
new file mode 100644
index 000000000..d876c5abe
--- /dev/null
+++ b/weed/s3api/auth_signature_v2_test.go
@@ -0,0 +1,284 @@
+package s3api
+
+import (
+ "net/http"
+ "testing"
+
+ "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
+ "github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
+)
+
+func setupTestIAMForV2Auth() *IdentityAccessManagement {
+ iam := &IdentityAccessManagement{
+ identities: []*Identity{},
+ accessKeyIdent: make(map[string]*Identity),
+ }
+
+ testCred := &Credential{
+ AccessKey: "AKIAIOSFODNN7EXAMPLE",
+ SecretKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
+ }
+
+ testIdentity := &Identity{
+ Name: "testUser",
+ Account: &AccountAdmin,
+ Credentials: []*Credential{testCred},
+ Actions: []Action{
+ s3_constants.ACTION_ADMIN,
+ },
+ }
+
+ iam.identities = append(iam.identities, testIdentity)
+ iam.accessKeyIdent[testCred.AccessKey] = testIdentity
+
+ return iam
+}
+
+func TestValidateV2AuthHeader(t *testing.T) {
+ tests := []struct {
+ name string
+ authHeader string
+ expectedAccessKey string
+ expectedError s3err.ErrorCode
+ }{
+ {
+ name: "valid auth header with space",
+ authHeader: "AWS AKIAIOSFODNN7EXAMPLE:frJIUN8DYpKDtOLCwo//yllqDzg=",
+ expectedAccessKey: "AKIAIOSFODNN7EXAMPLE",
+ expectedError: s3err.ErrNone,
+ },
+ {
+ name: "empty auth header",
+ authHeader: "",
+ expectedError: s3err.ErrAuthHeaderEmpty,
+ },
+ {
+ name: "wrong algorithm prefix",
+ authHeader: "HMAC AKIAIOSFODNN7EXAMPLE:signature",
+ expectedError: s3err.ErrSignatureVersionNotSupported,
+ },
+ {
+ name: "missing colon separator",
+ authHeader: "AWS AKIAIOSFODNN7EXAMPLE",
+ expectedError: s3err.ErrMissingFields,
+ },
+ {
+ name: "empty access key",
+ authHeader: "AWS :signature",
+ expectedError: s3err.ErrInvalidAccessKeyID,
+ },
+ {
+ name: "empty signature",
+ authHeader: "AWS AKIAIOSFODNN7EXAMPLE:",
+ expectedError: s3err.ErrMissingFields,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ accessKey, errCode := validateV2AuthHeader(tt.authHeader)
+
+ if errCode != tt.expectedError {
+ t.Errorf("validateV2AuthHeader() error = %v, want %v", errCode, tt.expectedError)
+ }
+
+ if errCode == s3err.ErrNone && accessKey != tt.expectedAccessKey {
+ t.Errorf("validateV2AuthHeader() accessKey = %q, want %q", accessKey, tt.expectedAccessKey)
+ }
+ })
+ }
+}
+
+func TestSignatureV2Format(t *testing.T) {
+ cred := &Credential{
+ AccessKey: "AKIAIOSFODNN7EXAMPLE",
+ SecretKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
+ }
+
+ headers := http.Header{}
+ headers.Set("Date", "Mon, 09 Sep 2011 23:36:00 GMT")
+
+ signature := signatureV2(cred, "GET", "/bucket/object", "", headers)
+
+ // Verify format: "AWS <AccessKey>:<Signature>" with space after AWS
+ expectedPrefix := "AWS " + cred.AccessKey + ":"
+ if len(signature) < len(expectedPrefix) {
+ t.Fatalf("Signature too short: %s", signature)
+ }
+
+ actualPrefix := signature[:len(expectedPrefix)]
+ if actualPrefix != expectedPrefix {
+ t.Errorf("Signature prefix = %q, want %q", actualPrefix, expectedPrefix)
+ }
+}
+
+func TestDoesSignV2Match(t *testing.T) {
+ iam := setupTestIAMForV2Auth()
+ cred := &Credential{
+ AccessKey: "AKIAIOSFODNN7EXAMPLE",
+ SecretKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
+ }
+
+ tests := []struct {
+ name string
+ method string
+ path string
+ query string
+ headers map[string]string
+ authOverride string
+ expectedError s3err.ErrorCode
+ expectIdent bool
+ }{
+ {
+ name: "valid GET request",
+ method: "GET",
+ path: "/bucket/object",
+ query: "",
+ headers: map[string]string{"Date": "Mon, 09 Sep 2011 23:36:00 GMT"},
+ expectedError: s3err.ErrNone,
+ expectIdent: true,
+ },
+ {
+ name: "valid PUT request with content headers",
+ method: "PUT",
+ path: "/bucket/object",
+ query: "",
+ headers: map[string]string{
+ "Date": "Mon, 09 Sep 2011 23:36:00 GMT",
+ "Content-Type": "text/plain",
+ "Content-Md5": "c8fdb181845a4ca6b8fec737b3581d76",
+ },
+ expectedError: s3err.ErrNone,
+ expectIdent: true,
+ },
+ {
+ name: "request with query parameters",
+ method: "GET",
+ path: "/bucket/object",
+ query: "acl&versionId=123",
+ headers: map[string]string{
+ "Date": "Mon, 09 Sep 2011 23:36:00 GMT",
+ },
+ expectedError: s3err.ErrNone,
+ expectIdent: true,
+ },
+ {
+ name: "request with x-amz headers",
+ method: "PUT",
+ path: "/bucket/object",
+ query: "",
+ headers: map[string]string{
+ "Date": "Mon, 09 Sep 2011 23:36:00 GMT",
+ "x-amz-storage-class": "REDUCED_REDUNDANCY",
+ "x-amz-meta-custom": "value",
+ },
+ expectedError: s3err.ErrNone,
+ expectIdent: true,
+ },
+ {
+ name: "invalid signature",
+ method: "GET",
+ path: "/bucket/object",
+ query: "",
+ headers: map[string]string{"Date": "Mon, 09 Sep 2011 23:36:00 GMT"},
+ authOverride: "AWS AKIAIOSFODNN7EXAMPLE:invalidSignature123456==",
+ expectedError: s3err.ErrSignatureDoesNotMatch,
+ expectIdent: false,
+ },
+ {
+ name: "non-existent access key",
+ method: "GET",
+ path: "/bucket/object",
+ query: "",
+ headers: map[string]string{"Date": "Mon, 09 Sep 2011 23:36:00 GMT"},
+ authOverride: "AWS NONEXISTENTKEY:signature==",
+ expectedError: s3err.ErrInvalidAccessKeyID,
+ expectIdent: false,
+ },
+ {
+ name: "empty authorization header",
+ method: "GET",
+ path: "/bucket/object",
+ query: "",
+ headers: map[string]string{"Date": "Mon, 09 Sep 2011 23:36:00 GMT"},
+ authOverride: "",
+ expectedError: s3err.ErrAuthHeaderEmpty,
+ expectIdent: false,
+ },
+ {
+ name: "malformed auth - missing signature",
+ method: "GET",
+ path: "/bucket/object",
+ query: "",
+ headers: map[string]string{"Date": "Mon, 09 Sep 2011 23:36:00 GMT"},
+ authOverride: "AWS AKIAIOSFODNN7EXAMPLE",
+ expectedError: s3err.ErrMissingFields,
+ expectIdent: false,
+ },
+ {
+ name: "malformed auth - wrong prefix",
+ method: "GET",
+ path: "/bucket/object",
+ query: "",
+ headers: map[string]string{"Date": "Mon, 09 Sep 2011 23:36:00 GMT"},
+ authOverride: "HMAC AKIAIOSFODNN7EXAMPLE:sig",
+ expectedError: s3err.ErrSignatureVersionNotSupported,
+ expectIdent: false,
+ },
+ {
+ name: "malformed auth - no space after AWS",
+ method: "GET",
+ path: "/bucket/object",
+ query: "",
+ headers: map[string]string{"Date": "Mon, 09 Sep 2011 23:36:00 GMT"},
+ authOverride: "AWSAKIAIOSFODNN7EXAMPLE:signature==",
+ expectedError: s3err.ErrInvalidAccessKeyID,
+ expectIdent: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ url := "http://example.com" + tt.path
+ if tt.query != "" {
+ url += "?" + tt.query
+ }
+ req, err := http.NewRequest(tt.method, url, nil)
+ if err != nil {
+ t.Fatalf("Failed to create request: %v", err)
+ }
+
+ for key, value := range tt.headers {
+ req.Header.Set(key, value)
+ }
+
+ var authHeader string
+ if tt.authOverride != "" {
+ authHeader = tt.authOverride
+ } else {
+ authHeader = signatureV2(cred, req.Method, req.URL.Path, req.URL.Query().Encode(), req.Header)
+ }
+ if tt.name != "empty authorization header" {
+ req.Header.Set("Authorization", authHeader)
+ }
+
+ identity, errCode := iam.doesSignV2Match(req)
+
+ if errCode != tt.expectedError {
+ t.Errorf("doesSignV2Match() error = %v, want %v", errCode, tt.expectedError)
+ }
+
+ if tt.expectIdent && identity == nil {
+ t.Error("Expected non-nil identity")
+ }
+
+ if !tt.expectIdent && identity != nil {
+ t.Error("Expected nil identity")
+ }
+
+ if identity != nil && identity.Name != "testUser" {
+ t.Errorf("Identity name = %q, want %q", identity.Name, "testUser")
+ }
+ })
+ }
+}