mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-06-13 23:36:45 +03:00
cce98fcecf
* fix(s3): strip client-supplied X-SeaweedFS-Principal/Session-Token in AuthSignatureOnly AuthSignatureOnly is the only auth gate in front of S3Tables routes (incl. CreateTableBucket) and UnifiedPostHandler, but unlike authenticateRequestInternal it did not clear the internal IAM trust headers before running signature verification. S3Tables authorizeIAMAction reads X-SeaweedFS-Principal directly from the request and prefers it over the authenticated identity's PrincipalArn, so a signed low-privilege caller could append that header after signing (unsigned header, SigV4 still verifies) and have IAM policy evaluated against a spoofed principal, bypassing authorization. Clear both X-SeaweedFS-Principal and X-SeaweedFS-Session-Token at the top of AuthSignatureOnly, mirroring the existing guard in authenticateRequestInternal. Add a regression test covering the header-injection path. * refactor(s3): route AuthSignatureOnly through authenticateRequestInternal Addresses review feedback: both entry points were independently maintaining the internal-IAM-header stripping and the auth-type dispatch switch. Collapse AuthSignatureOnly into a thin wrapper around authenticateRequestInternal so the security-critical header scrub and the signature-verify switch live in one place. Post-auth behavior unique to AuthSignatureOnly (AmzAccountId header) stays inline. No functional change beyond two harmless telemetry tweaks that now match authenticateRequestInternal: the per-branch glog verbosity shifts from V(3) to V(4), and the anonymous-found path now sets AmzAuthType. * refactor(s3): centralize X-SeaweedFS-Principal/Session-Token header names Introduce SeaweedFSPrincipalHeader and SeaweedFSSessionTokenHeader in weed/s3api/s3_constants so the trust-header literals are defined once and referenced consistently by the auth scrub, JWT auth path, bucket policy principal resolution, IAM authorization, and S3Tables IAM evaluation. Replace every remaining usage in weed/s3api and weed/s3api/s3tables. This removes the drift risk the reviewer called out: adding another call site with a typo can no longer silently bypass the scrub. Pure rename, no behavior change. No-op integration-test helper in test/s3/iam/s3_iam_framework.go left untouched (separate module, and the server now strips the client-supplied value regardless).
560 lines
16 KiB
Go
560 lines
16 KiB
Go
package s3api
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/golang-jwt/jwt/v5"
|
|
"github.com/seaweedfs/seaweedfs/weed/iam/integration"
|
|
"github.com/seaweedfs/seaweedfs/weed/iam/ldap"
|
|
"github.com/seaweedfs/seaweedfs/weed/iam/oidc"
|
|
"github.com/seaweedfs/seaweedfs/weed/iam/policy"
|
|
"github.com/seaweedfs/seaweedfs/weed/iam/sts"
|
|
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
|
|
"github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// createTestJWTAuth creates a test JWT token with the specified issuer, subject and signing key
|
|
func createTestJWTAuth(t *testing.T, issuer, subject, signingKey string) string {
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
|
"iss": issuer,
|
|
"sub": subject,
|
|
"aud": "test-client-id",
|
|
"exp": time.Now().Add(time.Hour).Unix(),
|
|
"iat": time.Now().Unix(),
|
|
// Add claims that trust policy validation expects
|
|
"idp": "test-oidc", // Identity provider claim for trust policy matching
|
|
})
|
|
|
|
tokenString, err := token.SignedString([]byte(signingKey))
|
|
require.NoError(t, err)
|
|
return tokenString
|
|
}
|
|
|
|
// TestJWTAuthenticationFlow tests the JWT authentication flow without full S3 server
|
|
func TestJWTAuthenticationFlow(t *testing.T) {
|
|
// Set up IAM system
|
|
iamManager := setupTestIAMManager(t)
|
|
|
|
// Create IAM integration
|
|
s3iam := NewS3IAMIntegration(iamManager, "localhost:8888")
|
|
|
|
// Create IAM server with integration
|
|
iamServer := setupIAMWithIntegration(t, iamManager, s3iam)
|
|
|
|
// Test scenarios
|
|
tests := []struct {
|
|
name string
|
|
roleArn string
|
|
setupRole func(ctx context.Context, mgr *integration.IAMManager)
|
|
testOperations []JWTTestOperation
|
|
}{
|
|
{
|
|
name: "Read-Only JWT Authentication",
|
|
roleArn: "arn:aws:iam::role/S3ReadOnlyRole",
|
|
setupRole: setupTestReadOnlyRole,
|
|
testOperations: []JWTTestOperation{
|
|
{Action: s3_constants.ACTION_READ, Bucket: "test-bucket", Object: "test-file.txt", ExpectedAllow: true},
|
|
{Action: s3_constants.ACTION_WRITE, Bucket: "test-bucket", Object: "new-file.txt", ExpectedAllow: false},
|
|
{Action: s3_constants.ACTION_LIST, Bucket: "test-bucket", Object: "", ExpectedAllow: true},
|
|
},
|
|
},
|
|
{
|
|
name: "Admin JWT Authentication",
|
|
roleArn: "arn:aws:iam::role/S3AdminRole",
|
|
setupRole: setupTestAdminRole,
|
|
testOperations: []JWTTestOperation{
|
|
{Action: s3_constants.ACTION_READ, Bucket: "admin-bucket", Object: "admin-file.txt", ExpectedAllow: true},
|
|
{Action: s3_constants.ACTION_WRITE, Bucket: "admin-bucket", Object: "new-admin-file.txt", ExpectedAllow: true},
|
|
{Action: s3_constants.ACTION_DELETE_BUCKET, Bucket: "admin-bucket", Object: "", ExpectedAllow: true},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
// Set up role
|
|
tt.setupRole(ctx, iamManager)
|
|
|
|
// Create a valid JWT token for testing
|
|
validJWTToken := createTestJWTAuth(t, "https://test-issuer.com", "test-user-123", "test-signing-key")
|
|
|
|
// Assume role to get JWT
|
|
response, err := iamManager.AssumeRoleWithWebIdentity(ctx, &sts.AssumeRoleWithWebIdentityRequest{
|
|
RoleArn: tt.roleArn,
|
|
WebIdentityToken: validJWTToken,
|
|
RoleSessionName: "jwt-auth-test",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
jwtToken := response.Credentials.SessionToken
|
|
|
|
// Test each operation
|
|
for _, op := range tt.testOperations {
|
|
t.Run(string(op.Action), func(t *testing.T) {
|
|
// Test JWT authentication
|
|
identity, errCode := testJWTAuthentication(t, iamServer, jwtToken)
|
|
require.Equal(t, s3err.ErrNone, errCode, "JWT authentication should succeed")
|
|
require.NotNil(t, identity)
|
|
|
|
// Test authorization with appropriate role based on test case
|
|
var testRoleName string
|
|
if tt.name == "Read-Only JWT Authentication" {
|
|
testRoleName = "TestReadRole"
|
|
} else {
|
|
testRoleName = "TestAdminRole"
|
|
}
|
|
allowed := testJWTAuthorizationWithRole(t, iamServer, identity, op.Action, op.Bucket, op.Object, jwtToken, testRoleName)
|
|
assert.Equal(t, op.ExpectedAllow, allowed, "Operation %s should have expected result", op.Action)
|
|
})
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestJWTTokenValidation tests JWT token validation edge cases
|
|
func TestJWTTokenValidation(t *testing.T) {
|
|
iamManager := setupTestIAMManager(t)
|
|
s3iam := NewS3IAMIntegration(iamManager, "localhost:8888")
|
|
iamServer := setupIAMWithIntegration(t, iamManager, s3iam)
|
|
|
|
tests := []struct {
|
|
name string
|
|
token string
|
|
expectedErr s3err.ErrorCode
|
|
}{
|
|
{
|
|
name: "Empty token",
|
|
token: "",
|
|
expectedErr: s3err.ErrAccessDenied,
|
|
},
|
|
{
|
|
name: "Invalid token format",
|
|
token: "invalid-token",
|
|
expectedErr: s3err.ErrAccessDenied,
|
|
},
|
|
{
|
|
name: "Expired token",
|
|
token: "expired-session-token",
|
|
expectedErr: s3err.ErrAccessDenied,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
identity, errCode := testJWTAuthentication(t, iamServer, tt.token)
|
|
|
|
assert.Equal(t, tt.expectedErr, errCode)
|
|
assert.Nil(t, identity)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestRequestContextExtraction tests context extraction for policy conditions
|
|
func TestRequestContextExtraction(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
setupRequest func() *http.Request
|
|
expectedIP string
|
|
expectedUA string
|
|
}{
|
|
{
|
|
name: "Standard request with IP",
|
|
setupRequest: func() *http.Request {
|
|
req := httptest.NewRequest("GET", "/test-bucket/test-file.txt", http.NoBody)
|
|
req.Header.Set("X-Forwarded-For", "192.168.1.100")
|
|
req.Header.Set("User-Agent", "aws-sdk-go/1.0")
|
|
// Set RemoteAddr to private IP to simulate trusted proxy
|
|
req.RemoteAddr = "127.0.0.1:12345"
|
|
return req
|
|
},
|
|
expectedIP: "192.168.1.100",
|
|
expectedUA: "aws-sdk-go/1.0",
|
|
},
|
|
{
|
|
name: "Request with X-Real-IP",
|
|
setupRequest: func() *http.Request {
|
|
req := httptest.NewRequest("GET", "/test-bucket/test-file.txt", http.NoBody)
|
|
req.Header.Set("X-Real-IP", "10.0.0.1")
|
|
req.Header.Set("User-Agent", "boto3/1.0")
|
|
// Set RemoteAddr to private IP to simulate trusted proxy
|
|
req.RemoteAddr = "127.0.0.1:12345"
|
|
return req
|
|
},
|
|
expectedIP: "10.0.0.1",
|
|
expectedUA: "boto3/1.0",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
req := tt.setupRequest()
|
|
|
|
// Extract request context
|
|
context := extractRequestContext(req)
|
|
|
|
if tt.expectedIP != "" {
|
|
assert.Equal(t, tt.expectedIP, context["aws:SourceIp"])
|
|
}
|
|
|
|
if tt.expectedUA != "" {
|
|
assert.Equal(t, tt.expectedUA, context["userAgent"])
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestIPBasedPolicyEnforcement tests IP-based conditional policies
|
|
func TestIPBasedPolicyEnforcement(t *testing.T) {
|
|
iamManager := setupTestIAMManager(t)
|
|
s3iam := NewS3IAMIntegration(iamManager, "localhost:8888")
|
|
ctx := context.Background()
|
|
|
|
// Set up IP-restricted role
|
|
setupTestIPRestrictedRole(ctx, iamManager)
|
|
|
|
// Create a valid JWT token for testing
|
|
validJWTToken := createTestJWTAuth(t, "https://test-issuer.com", "test-user-123", "test-signing-key")
|
|
|
|
// Assume role
|
|
response, err := iamManager.AssumeRoleWithWebIdentity(ctx, &sts.AssumeRoleWithWebIdentityRequest{
|
|
RoleArn: "arn:aws:iam::role/S3IPRestrictedRole",
|
|
WebIdentityToken: validJWTToken,
|
|
RoleSessionName: "ip-test-session",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
sourceIP string
|
|
shouldAllow bool
|
|
}{
|
|
{
|
|
name: "Allow from office IP",
|
|
sourceIP: "192.168.1.100",
|
|
shouldAllow: true,
|
|
},
|
|
{
|
|
name: "Block from external IP",
|
|
sourceIP: "8.8.8.8",
|
|
shouldAllow: false,
|
|
},
|
|
{
|
|
name: "Allow from internal range",
|
|
sourceIP: "10.0.0.1",
|
|
shouldAllow: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Create request with specific IP
|
|
req := httptest.NewRequest("GET", "/restricted-bucket/file.txt", http.NoBody)
|
|
req.Header.Set("Authorization", "Bearer "+response.Credentials.SessionToken)
|
|
req.Header.Set("X-Forwarded-For", tt.sourceIP)
|
|
// Set RemoteAddr to private IP to simulate trusted proxy
|
|
req.RemoteAddr = "127.0.0.1:12345"
|
|
|
|
// Create IAM identity for testing
|
|
identity := &IAMIdentity{
|
|
Name: "test-user",
|
|
Principal: response.AssumedRoleUser.Arn,
|
|
SessionToken: response.Credentials.SessionToken,
|
|
}
|
|
|
|
// Test authorization with IP condition
|
|
errCode := s3iam.AuthorizeAction(ctx, identity, s3_constants.ACTION_READ, "restricted-bucket", "file.txt", req)
|
|
|
|
if tt.shouldAllow {
|
|
assert.Equal(t, s3err.ErrNone, errCode, "Should allow access from IP %s", tt.sourceIP)
|
|
} else {
|
|
assert.Equal(t, s3err.ErrAccessDenied, errCode, "Should deny access from IP %s", tt.sourceIP)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// JWTTestOperation represents a test operation for JWT testing
|
|
type JWTTestOperation struct {
|
|
Action Action
|
|
Bucket string
|
|
Object string
|
|
ExpectedAllow bool
|
|
}
|
|
|
|
// Helper functions
|
|
|
|
func setupTestIAMManager(t *testing.T) *integration.IAMManager {
|
|
// Create IAM manager
|
|
manager := integration.NewIAMManager()
|
|
|
|
// Initialize with test configuration
|
|
config := &integration.IAMConfig{
|
|
STS: &sts.STSConfig{
|
|
TokenDuration: sts.FlexibleDuration{Duration: time.Hour},
|
|
MaxSessionLength: sts.FlexibleDuration{Duration: time.Hour * 12},
|
|
Issuer: "test-sts",
|
|
SigningKey: []byte("test-signing-key-32-characters-long"),
|
|
},
|
|
Policy: &policy.PolicyEngineConfig{
|
|
DefaultEffect: "Deny",
|
|
StoreType: "memory",
|
|
},
|
|
Roles: &integration.RoleStoreConfig{
|
|
StoreType: "memory",
|
|
},
|
|
}
|
|
|
|
err := manager.Initialize(config, func() string {
|
|
return "localhost:8888" // Mock filer address for testing
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Set up test identity providers
|
|
setupTestIdentityProviders(t, manager)
|
|
|
|
return manager
|
|
}
|
|
|
|
func setupTestIdentityProviders(t *testing.T, manager *integration.IAMManager) {
|
|
// Set up OIDC provider
|
|
oidcProvider := oidc.NewMockOIDCProvider("test-oidc")
|
|
oidcConfig := &oidc.OIDCConfig{
|
|
Issuer: "https://test-issuer.com",
|
|
ClientID: "test-client-id",
|
|
}
|
|
err := oidcProvider.Initialize(oidcConfig)
|
|
require.NoError(t, err)
|
|
oidcProvider.SetupDefaultTestData()
|
|
|
|
// Set up LDAP provider
|
|
ldapProvider := ldap.NewMockLDAPProvider("test-ldap")
|
|
err = ldapProvider.Initialize(nil) // Mock doesn't need real config
|
|
require.NoError(t, err)
|
|
ldapProvider.SetupDefaultTestData()
|
|
|
|
// Register providers
|
|
err = manager.RegisterIdentityProvider(oidcProvider)
|
|
require.NoError(t, err)
|
|
err = manager.RegisterIdentityProvider(ldapProvider)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func setupIAMWithIntegration(t *testing.T, iamManager *integration.IAMManager, s3iam *S3IAMIntegration) *IdentityAccessManagement {
|
|
// Create a minimal IdentityAccessManagement for testing
|
|
iam := &IdentityAccessManagement{
|
|
isAuthEnabled: true,
|
|
}
|
|
|
|
// Set IAM integration
|
|
iam.SetIAMIntegration(s3iam)
|
|
|
|
return iam
|
|
}
|
|
|
|
func setupTestReadOnlyRole(ctx context.Context, manager *integration.IAMManager) {
|
|
// Create read-only policy
|
|
readPolicy := &policy.PolicyDocument{
|
|
Version: "2012-10-17",
|
|
Statement: []policy.Statement{
|
|
{
|
|
Sid: "AllowS3Read",
|
|
Effect: "Allow",
|
|
Action: []string{"s3:GetObject", "s3:ListBucket"},
|
|
Resource: []string{
|
|
"arn:aws:s3:::*",
|
|
"arn:aws:s3:::*/*",
|
|
},
|
|
},
|
|
{
|
|
Sid: "AllowSTSSessionValidation",
|
|
Effect: "Allow",
|
|
Action: []string{"sts:ValidateSession"},
|
|
Resource: []string{"*"},
|
|
},
|
|
},
|
|
}
|
|
|
|
manager.CreatePolicy(ctx, "", "S3ReadOnlyPolicy", readPolicy)
|
|
|
|
// Create role
|
|
manager.CreateRole(ctx, "", "S3ReadOnlyRole", &integration.RoleDefinition{
|
|
RoleName: "S3ReadOnlyRole",
|
|
TrustPolicy: &policy.PolicyDocument{
|
|
Version: "2012-10-17",
|
|
Statement: []policy.Statement{
|
|
{
|
|
Effect: "Allow",
|
|
Principal: map[string]interface{}{
|
|
"Federated": "test-oidc",
|
|
},
|
|
Action: []string{"sts:AssumeRoleWithWebIdentity"},
|
|
},
|
|
},
|
|
},
|
|
AttachedPolicies: []string{"S3ReadOnlyPolicy"},
|
|
})
|
|
|
|
// Also create a TestReadRole for read-only authorization testing
|
|
manager.CreateRole(ctx, "", "TestReadRole", &integration.RoleDefinition{
|
|
RoleName: "TestReadRole",
|
|
TrustPolicy: &policy.PolicyDocument{
|
|
Version: "2012-10-17",
|
|
Statement: []policy.Statement{
|
|
{
|
|
Effect: "Allow",
|
|
Principal: map[string]interface{}{
|
|
"Federated": "test-oidc",
|
|
},
|
|
Action: []string{"sts:AssumeRoleWithWebIdentity"},
|
|
},
|
|
},
|
|
},
|
|
AttachedPolicies: []string{"S3ReadOnlyPolicy"},
|
|
})
|
|
}
|
|
|
|
func setupTestAdminRole(ctx context.Context, manager *integration.IAMManager) {
|
|
// Create admin policy
|
|
adminPolicy := &policy.PolicyDocument{
|
|
Version: "2012-10-17",
|
|
Statement: []policy.Statement{
|
|
{
|
|
Sid: "AllowAllS3",
|
|
Effect: "Allow",
|
|
Action: []string{"s3:*"},
|
|
Resource: []string{
|
|
"arn:aws:s3:::*",
|
|
"arn:aws:s3:::*/*",
|
|
},
|
|
},
|
|
{
|
|
Sid: "AllowSTSSessionValidation",
|
|
Effect: "Allow",
|
|
Action: []string{"sts:ValidateSession"},
|
|
Resource: []string{"*"},
|
|
},
|
|
},
|
|
}
|
|
|
|
manager.CreatePolicy(ctx, "", "S3AdminPolicy", adminPolicy)
|
|
|
|
// Create role
|
|
manager.CreateRole(ctx, "", "S3AdminRole", &integration.RoleDefinition{
|
|
RoleName: "S3AdminRole",
|
|
TrustPolicy: &policy.PolicyDocument{
|
|
Version: "2012-10-17",
|
|
Statement: []policy.Statement{
|
|
{
|
|
Effect: "Allow",
|
|
Principal: map[string]interface{}{
|
|
"Federated": "test-oidc",
|
|
},
|
|
Action: []string{"sts:AssumeRoleWithWebIdentity"},
|
|
},
|
|
},
|
|
},
|
|
AttachedPolicies: []string{"S3AdminPolicy"},
|
|
})
|
|
|
|
// Also create a TestAdminRole with admin policy for authorization testing
|
|
manager.CreateRole(ctx, "", "TestAdminRole", &integration.RoleDefinition{
|
|
RoleName: "TestAdminRole",
|
|
TrustPolicy: &policy.PolicyDocument{
|
|
Version: "2012-10-17",
|
|
Statement: []policy.Statement{
|
|
{
|
|
Effect: "Allow",
|
|
Principal: map[string]interface{}{
|
|
"Federated": "test-oidc",
|
|
},
|
|
Action: []string{"sts:AssumeRoleWithWebIdentity"},
|
|
},
|
|
},
|
|
},
|
|
AttachedPolicies: []string{"S3AdminPolicy"}, // Admin gets full access
|
|
})
|
|
}
|
|
|
|
func setupTestIPRestrictedRole(ctx context.Context, manager *integration.IAMManager) {
|
|
// Create IP-restricted policy
|
|
restrictedPolicy := &policy.PolicyDocument{
|
|
Version: "2012-10-17",
|
|
Statement: []policy.Statement{
|
|
{
|
|
Sid: "AllowFromOffice",
|
|
Effect: "Allow",
|
|
Action: []string{"s3:GetObject", "s3:ListBucket"},
|
|
Resource: []string{
|
|
"arn:aws:s3:::*",
|
|
"arn:aws:s3:::*/*",
|
|
},
|
|
Condition: map[string]map[string]interface{}{
|
|
"IpAddress": {
|
|
"aws:SourceIp": []string{"192.168.1.0/24", "10.0.0.0/8"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
manager.CreatePolicy(ctx, "", "S3IPRestrictedPolicy", restrictedPolicy)
|
|
|
|
// Create role
|
|
manager.CreateRole(ctx, "", "S3IPRestrictedRole", &integration.RoleDefinition{
|
|
RoleName: "S3IPRestrictedRole",
|
|
TrustPolicy: &policy.PolicyDocument{
|
|
Version: "2012-10-17",
|
|
Statement: []policy.Statement{
|
|
{
|
|
Effect: "Allow",
|
|
Principal: map[string]interface{}{
|
|
"Federated": "test-oidc",
|
|
},
|
|
Action: []string{"sts:AssumeRoleWithWebIdentity"},
|
|
},
|
|
},
|
|
},
|
|
AttachedPolicies: []string{"S3IPRestrictedPolicy"},
|
|
})
|
|
}
|
|
|
|
func testJWTAuthentication(t *testing.T, iam *IdentityAccessManagement, token string) (*Identity, s3err.ErrorCode) {
|
|
// Create test request with JWT
|
|
req := httptest.NewRequest("GET", "/test-bucket/test-object", http.NoBody)
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
|
|
// Test authentication
|
|
if iam.iamIntegration == nil {
|
|
return nil, s3err.ErrNotImplemented
|
|
}
|
|
|
|
return iam.authenticateJWTWithIAM(req)
|
|
}
|
|
|
|
func testJWTAuthorizationWithRole(t *testing.T, iam *IdentityAccessManagement, identity *Identity, action Action, bucket, object, token, roleName string) bool {
|
|
// Create test request
|
|
req := httptest.NewRequest("GET", "/"+bucket+"/"+object, http.NoBody)
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
req.Header.Set(s3_constants.SeaweedFSSessionTokenHeader, token)
|
|
|
|
// Use a proper principal ARN format that matches what STS would generate
|
|
principalArn := "arn:aws:sts::assumed-role/" + roleName + "/test-session"
|
|
req.Header.Set(s3_constants.SeaweedFSPrincipalHeader, principalArn)
|
|
|
|
// Test authorization
|
|
if iam.iamIntegration == nil {
|
|
return false
|
|
}
|
|
|
|
errCode := iam.authorizeWithIAM(req, identity, action, bucket, object)
|
|
return errCode == s3err.ErrNone
|
|
}
|