diff --git a/test/s3/iam/iam_config_distributed.json b/test/s3/iam/iam_config_distributed.json index 595172171..8284c9e43 100644 --- a/test/s3/iam/iam_config_distributed.json +++ b/test/s3/iam/iam_config_distributed.json @@ -5,9 +5,6 @@ "issuer": "seaweedfs-sts", "signingKey": "dGVzdC1zaWduaW5nLWtleS0zMi1jaGFyYWN0ZXJzLWxvbmc=", "sessionStoreType": "filer", - "sessionStoreConfig": { - "basePath": "/etc/iam/sessions" - }, "providers": [ { "name": "keycloak-oidc", @@ -38,16 +35,10 @@ }, "policy": { "defaultEffect": "Deny", - "storeType": "filer", - "storeConfig": { - "basePath": "/etc/iam/policies" - } + "storeType": "filer" }, "roleStore": { - "storeType": "filer", - "storeConfig": { - "basePath": "/etc/iam/roles" - } + "storeType": "filer" }, "roles": [ diff --git a/weed/iam/integration/iam_integration_test.go b/weed/iam/integration/iam_integration_test.go index 9b3dc9473..48d3b8149 100644 --- a/weed/iam/integration/iam_integration_test.go +++ b/weed/iam/integration/iam_integration_test.go @@ -63,7 +63,7 @@ func TestFullOIDCWorkflow(t *testing.T) { RoleSessionName: tt.sessionName, } - response, err := iamManager.AssumeRoleWithWebIdentity(ctx, assumeRequest) + response, err := iamManager.AssumeRoleWithWebIdentity(ctx, "localhost:8888", assumeRequest) if !tt.expectedAllow { assert.Error(t, err) @@ -78,7 +78,7 @@ func TestFullOIDCWorkflow(t *testing.T) { // Step 2: Test policy enforcement with assumed credentials if tt.testAction != "" && tt.testResource != "" { - allowed, err := iamManager.IsActionAllowed(ctx, &ActionRequest{ + allowed, err := iamManager.IsActionAllowed(ctx, "localhost:8888", &ActionRequest{ Principal: response.AssumedRoleUser.Arn, Action: tt.testAction, Resource: tt.testResource, @@ -139,7 +139,7 @@ func TestFullLDAPWorkflow(t *testing.T) { ProviderName: "test-ldap", } - response, err := iamManager.AssumeRoleWithCredentials(ctx, assumeRequest) + response, err := iamManager.AssumeRoleWithCredentials(ctx, "localhost:8888", assumeRequest) if !tt.expectedAllow { assert.Error(t, err) @@ -152,7 +152,7 @@ func TestFullLDAPWorkflow(t *testing.T) { // Step 2: Test policy enforcement if tt.testAction != "" && tt.testResource != "" { - allowed, err := iamManager.IsActionAllowed(ctx, &ActionRequest{ + allowed, err := iamManager.IsActionAllowed(ctx, "localhost:8888", &ActionRequest{ Principal: response.AssumedRoleUser.Arn, Action: tt.testAction, Resource: tt.testResource, @@ -178,7 +178,7 @@ func TestPolicyEnforcement(t *testing.T) { RoleSessionName: "policy-test-session", } - response, err := iamManager.AssumeRoleWithWebIdentity(ctx, assumeRequest) + response, err := iamManager.AssumeRoleWithWebIdentity(ctx, "localhost:8888", assumeRequest) require.NoError(t, err) sessionToken := response.Credentials.SessionToken @@ -230,7 +230,7 @@ func TestPolicyEnforcement(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - allowed, err := iamManager.IsActionAllowed(ctx, &ActionRequest{ + allowed, err := iamManager.IsActionAllowed(ctx, "localhost:8888", &ActionRequest{ Principal: principal, Action: tt.action, Resource: tt.resource, @@ -256,13 +256,13 @@ func TestSessionExpiration(t *testing.T) { DurationSeconds: int64Ptr(900), // 15 minutes } - response, err := iamManager.AssumeRoleWithWebIdentity(ctx, assumeRequest) + response, err := iamManager.AssumeRoleWithWebIdentity(ctx, "localhost:8888", assumeRequest) require.NoError(t, err) sessionToken := response.Credentials.SessionToken // Verify session is initially valid - allowed, err := iamManager.IsActionAllowed(ctx, &ActionRequest{ + allowed, err := iamManager.IsActionAllowed(ctx, "localhost:8888", &ActionRequest{ Principal: response.AssumedRoleUser.Arn, Action: "s3:GetObject", Resource: "arn:seaweed:s3:::test-bucket/file.txt", @@ -276,11 +276,11 @@ func TestSessionExpiration(t *testing.T) { assert.True(t, response.Credentials.Expiration.Before(time.Now().Add(16*time.Minute))) // Test actual session expiration - err = iamManager.ExpireSessionForTesting(ctx, sessionToken) + err = iamManager.ExpireSessionForTesting(ctx, "localhost:8888", sessionToken) require.NoError(t, err) // Verify session is now expired and access is denied - allowed, err = iamManager.IsActionAllowed(ctx, &ActionRequest{ + allowed, err = iamManager.IsActionAllowed(ctx, "localhost:8888", &ActionRequest{ Principal: response.AssumedRoleUser.Arn, Action: "s3:GetObject", Resource: "arn:seaweed:s3:::test-bucket/file.txt", diff --git a/weed/iam/integration/iam_manager.go b/weed/iam/integration/iam_manager.go index 780149aea..fc4665232 100644 --- a/weed/iam/integration/iam_manager.go +++ b/weed/iam/integration/iam_manager.go @@ -174,7 +174,7 @@ func (m *IAMManager) CreateRole(ctx context.Context, roleName string, roleDef *R } // AssumeRoleWithWebIdentity assumes a role using web identity (OIDC) -func (m *IAMManager) AssumeRoleWithWebIdentity(ctx context.Context, request *sts.AssumeRoleWithWebIdentityRequest) (*sts.AssumeRoleResponse, error) { +func (m *IAMManager) AssumeRoleWithWebIdentity(ctx context.Context, filerAddress string, request *sts.AssumeRoleWithWebIdentityRequest) (*sts.AssumeRoleResponse, error) { if !m.initialized { return nil, fmt.Errorf("IAM manager not initialized") } @@ -194,11 +194,11 @@ func (m *IAMManager) AssumeRoleWithWebIdentity(ctx context.Context, request *sts } // Use STS service to assume the role - return m.stsService.AssumeRoleWithWebIdentity(ctx, request) + return m.stsService.AssumeRoleWithWebIdentity(ctx, filerAddress, request) } // AssumeRoleWithCredentials assumes a role using credentials (LDAP) -func (m *IAMManager) AssumeRoleWithCredentials(ctx context.Context, request *sts.AssumeRoleWithCredentialsRequest) (*sts.AssumeRoleResponse, error) { +func (m *IAMManager) AssumeRoleWithCredentials(ctx context.Context, filerAddress string, request *sts.AssumeRoleWithCredentialsRequest) (*sts.AssumeRoleResponse, error) { if !m.initialized { return nil, fmt.Errorf("IAM manager not initialized") } @@ -218,17 +218,17 @@ func (m *IAMManager) AssumeRoleWithCredentials(ctx context.Context, request *sts } // Use STS service to assume the role - return m.stsService.AssumeRoleWithCredentials(ctx, request) + return m.stsService.AssumeRoleWithCredentials(ctx, filerAddress, request) } // IsActionAllowed checks if a principal is allowed to perform an action on a resource -func (m *IAMManager) IsActionAllowed(ctx context.Context, request *ActionRequest) (bool, error) { +func (m *IAMManager) IsActionAllowed(ctx context.Context, filerAddress string, request *ActionRequest) (bool, error) { if !m.initialized { return false, fmt.Errorf("IAM manager not initialized") } // Validate session token first - _, err := m.stsService.ValidateSessionToken(ctx, request.SessionToken) + _, err := m.stsService.ValidateSessionToken(ctx, filerAddress, request.SessionToken) if err != nil { return false, fmt.Errorf("invalid session: %w", err) } @@ -381,10 +381,10 @@ func indexOf(s, substr string) int { } // ExpireSessionForTesting manually expires a session for testing purposes -func (m *IAMManager) ExpireSessionForTesting(ctx context.Context, sessionToken string) error { +func (m *IAMManager) ExpireSessionForTesting(ctx context.Context, filerAddress string, sessionToken string) error { if !m.initialized { return fmt.Errorf("IAM manager not initialized") } - return m.stsService.ExpireSessionForTesting(ctx, sessionToken) + return m.stsService.ExpireSessionForTesting(ctx, filerAddress, sessionToken) } diff --git a/weed/iam/integration/role_store.go b/weed/iam/integration/role_store.go index 497b7de7a..3f3d96ee3 100644 --- a/weed/iam/integration/role_store.go +++ b/weed/iam/integration/role_store.go @@ -143,7 +143,7 @@ type FilerRoleStore struct { // NewFilerRoleStore creates a new filer-based role store func NewFilerRoleStore(config map[string]interface{}) (*FilerRoleStore, error) { store := &FilerRoleStore{ - basePath: "/seaweedfs/iam/roles", // Default path for role storage + basePath: "/etc/iam/roles", // Default path for role storage - aligned with /etc/ convention } // Parse configuration diff --git a/weed/iam/policy/policy_store.go b/weed/iam/policy/policy_store.go index 5e11af25d..405dcc83f 100644 --- a/weed/iam/policy/policy_store.go +++ b/weed/iam/policy/policy_store.go @@ -156,7 +156,7 @@ type FilerPolicyStore struct { // NewFilerPolicyStore creates a new filer-based policy store func NewFilerPolicyStore(config map[string]interface{}) (*FilerPolicyStore, error) { store := &FilerPolicyStore{ - basePath: "/seaweedfs/iam/policies", // Default path for policy storage + basePath: "/etc/iam/policies", // Default path for policy storage - aligned with /etc/ convention } // Parse configuration diff --git a/weed/s3api/s3_end_to_end_test.go b/weed/s3api/s3_end_to_end_test.go index 98a14a7ac..7b539b707 100644 --- a/weed/s3api/s3_end_to_end_test.go +++ b/weed/s3api/s3_end_to_end_test.go @@ -84,7 +84,7 @@ func TestS3EndToEndWithJWT(t *testing.T) { tt.setupRole(ctx, iamManager) // Assume role to get JWT token - response, err := iamManager.AssumeRoleWithWebIdentity(ctx, &sts.AssumeRoleWithWebIdentityRequest{ + response, err := iamManager.AssumeRoleWithWebIdentity(ctx, "localhost:8888", &sts.AssumeRoleWithWebIdentityRequest{ RoleArn: tt.roleArn, WebIdentityToken: "valid-oidc-token", RoleSessionName: tt.sessionName, diff --git a/weed/s3api/s3_iam_middleware.go b/weed/s3api/s3_iam_middleware.go index c2cc3ed5a..4270ab2f0 100644 --- a/weed/s3api/s3_iam_middleware.go +++ b/weed/s3api/s3_iam_middleware.go @@ -16,15 +16,17 @@ import ( // S3IAMIntegration provides IAM integration for S3 API type S3IAMIntegration struct { - iamManager *integration.IAMManager - enabled bool + iamManager *integration.IAMManager + filerAddress string + enabled bool } // NewS3IAMIntegration creates a new S3 IAM integration -func NewS3IAMIntegration(iamManager *integration.IAMManager) *S3IAMIntegration { +func NewS3IAMIntegration(iamManager *integration.IAMManager, filerAddress string) *S3IAMIntegration { return &S3IAMIntegration{ - iamManager: iamManager, - enabled: iamManager != nil, + iamManager: iamManager, + filerAddress: filerAddress, + enabled: iamManager != nil, } } @@ -93,7 +95,7 @@ func (s3iam *S3IAMIntegration) AuthenticateJWT(ctx context.Context, r *http.Requ } glog.V(0).Infof("AuthenticateJWT: calling IsActionAllowed for principal=%s", principalArn) - allowed, err := s3iam.iamManager.IsActionAllowed(ctx, testRequest) + allowed, err := s3iam.iamManager.IsActionAllowed(ctx, s3iam.filerAddress, testRequest) glog.V(0).Infof("AuthenticateJWT: IsActionAllowed returned allowed=%t, err=%v", allowed, err) if err != nil || !allowed { glog.V(0).Infof("IAM validation failed for %s: %v", principalArn, err) @@ -145,7 +147,7 @@ func (s3iam *S3IAMIntegration) AuthorizeAction(ctx context.Context, identity *IA } // Check if action is allowed using our policy engine - allowed, err := s3iam.iamManager.IsActionAllowed(ctx, actionRequest) + allowed, err := s3iam.iamManager.IsActionAllowed(ctx, s3iam.filerAddress, actionRequest) if err != nil { // Log the error but treat authentication/authorization failures as access denied // rather than internal errors to provide better user experience diff --git a/weed/s3api/s3api_server.go b/weed/s3api/s3api_server.go index f7bbb112d..5f477f3ba 100644 --- a/weed/s3api/s3api_server.go +++ b/weed/s3api/s3api_server.go @@ -107,7 +107,7 @@ func NewS3ApiServerWithStore(router *mux.Router, option *S3ApiServerOption, expl glog.Errorf("Failed to load IAM configuration: %v", err) } else { // Create S3 IAM integration with the loaded IAM manager - s3iam := NewS3IAMIntegration(iamManager) + s3iam := NewS3IAMIntegration(iamManager, string(option.Filer)) // Set IAM integration in server s3ApiServer.iamIntegration = s3iam