diff --git a/weed/iam/integration/iam_integration_test.go b/weed/iam/integration/iam_integration_test.go index 1a74ed9c2..9b3dc9473 100644 --- a/weed/iam/integration/iam_integration_test.go +++ b/weed/iam/integration/iam_integration_test.go @@ -271,10 +271,24 @@ func TestSessionExpiration(t *testing.T) { require.NoError(t, err) assert.True(t, allowed) - // TODO: Test actual expiration (would need time manipulation) - // For now, just verify the expiration time is set correctly + // Verify the expiration time is set correctly assert.True(t, response.Credentials.Expiration.After(time.Now())) assert.True(t, response.Credentials.Expiration.Before(time.Now().Add(16*time.Minute))) + + // Test actual session expiration + err = iamManager.ExpireSessionForTesting(ctx, sessionToken) + require.NoError(t, err) + + // Verify session is now expired and access is denied + allowed, err = iamManager.IsActionAllowed(ctx, &ActionRequest{ + Principal: response.AssumedRoleUser.Arn, + Action: "s3:GetObject", + Resource: "arn:seaweed:s3:::test-bucket/file.txt", + SessionToken: sessionToken, + }) + require.Error(t, err) + assert.False(t, allowed) + assert.Contains(t, err.Error(), "session has expired") } // TestTrustPolicyValidation tests role trust policy validation diff --git a/weed/iam/integration/iam_manager.go b/weed/iam/integration/iam_manager.go index ed5430023..4862c3038 100644 --- a/weed/iam/integration/iam_manager.go +++ b/weed/iam/integration/iam_manager.go @@ -347,3 +347,12 @@ func indexOf(s, substr string) int { } return -1 } + +// ExpireSessionForTesting manually expires a session for testing purposes +func (m *IAMManager) ExpireSessionForTesting(ctx context.Context, sessionToken string) error { + if !m.initialized { + return fmt.Errorf("IAM manager not initialized") + } + + return m.stsService.ExpireSessionForTesting(ctx, sessionToken) +} diff --git a/weed/iam/ldap/ldap_provider.go b/weed/iam/ldap/ldap_provider.go index f8f43bdff..a38b0f940 100644 --- a/weed/iam/ldap/ldap_provider.go +++ b/weed/iam/ldap/ldap_provider.go @@ -318,7 +318,7 @@ func (p *LDAPProvider) GetUserInfo(ctx context.Context, userID string) (*provide BaseDN: p.config.BaseDN, Scope: ScopeWholeSubtree, DerefAliases: NeverDerefAliases, - SizeLimit: 1, // We only need one user + SizeLimit: 1, // We only need one user TimeLimit: 30, // 30 second timeout TypesOnly: false, Filter: userFilter, @@ -407,7 +407,7 @@ func (p *LDAPProvider) ValidateToken(ctx context.Context, token string) (*provid BaseDN: p.config.BaseDN, Scope: ScopeWholeSubtree, DerefAliases: NeverDerefAliases, - SizeLimit: 1, // We only need one user + SizeLimit: 1, // We only need one user TimeLimit: 30, // 30 second timeout TypesOnly: false, Filter: userFilter, diff --git a/weed/iam/sts/session_store.go b/weed/iam/sts/session_store.go index dc2b9a98e..e0d96d322 100644 --- a/weed/iam/sts/session_store.go +++ b/weed/iam/sts/session_store.go @@ -94,6 +94,27 @@ func (m *MemorySessionStore) CleanupExpiredSessions(ctx context.Context) error { return nil } +// ExpireSessionForTesting manually expires a session for testing purposes +func (m *MemorySessionStore) ExpireSessionForTesting(ctx context.Context, sessionId string) error { + if sessionId == "" { + return fmt.Errorf("session ID cannot be empty") + } + + m.mutex.Lock() + defer m.mutex.Unlock() + + session, exists := m.sessions[sessionId] + if !exists { + return fmt.Errorf("session not found") + } + + // Set expiration to 1 minute in the past to ensure it's expired + session.ExpiresAt = time.Now().Add(-1 * time.Minute) + m.sessions[sessionId] = session + + return nil +} + // FilerSessionStore implements SessionStore using SeaweedFS filer type FilerSessionStore struct { filerGrpcAddress string diff --git a/weed/iam/sts/sts_service.go b/weed/iam/sts/sts_service.go index 036de499f..d706c50f2 100644 --- a/weed/iam/sts/sts_service.go +++ b/weed/iam/sts/sts_service.go @@ -608,3 +608,29 @@ func (s *STSService) validateAssumeRoleWithCredentialsRequest(request *AssumeRol return nil } + +// ExpireSessionForTesting manually expires a session for testing purposes +func (s *STSService) ExpireSessionForTesting(ctx context.Context, sessionToken string) error { + if !s.initialized { + return fmt.Errorf("STS service not initialized") + } + + if sessionToken == "" { + return fmt.Errorf("session token cannot be empty") + } + + // Extract session ID from token + sessionId := s.extractSessionIdFromToken(sessionToken) + if sessionId == "" { + return fmt.Errorf("invalid session token format") + } + + // Check if session store supports manual expiration (for MemorySessionStore) + if memStore, ok := s.sessionStore.(*MemorySessionStore); ok { + return memStore.ExpireSessionForTesting(ctx, sessionId) + } + + // For other session stores, we could implement similar functionality + // For now, just return an error indicating it's not supported + return fmt.Errorf("manual session expiration not supported for this session store type") +}