diff --git a/spec/enterprise/controllers/enterprise/devise_overrides/omniauth_callbacks_controller_spec.rb b/spec/enterprise/controllers/enterprise/devise_overrides/omniauth_callbacks_controller_spec.rb index af67e3009..45c08df2b 100644 --- a/spec/enterprise/controllers/enterprise/devise_overrides/omniauth_callbacks_controller_spec.rb +++ b/spec/enterprise/controllers/enterprise/devise_overrides/omniauth_callbacks_controller_spec.rb @@ -58,4 +58,68 @@ RSpec.describe 'Enterprise SAML OmniAuth Callbacks', type: :request do end end end + + describe 'SAML SLO (Single Logout)' do + let(:saml_user) { create(:user, email: 'saml_user@example.com', provider: 'saml', account: account) } + + before do + # Create some active sessions for the user + saml_user.tokens = { + 'client1' => { 'token' => 'token1', 'expiry' => 1.hour.from_now.to_i }, + 'client2' => { 'token' => 'token2', 'expiry' => 2.hours.from_now.to_i } + } + saml_user.save! + end + + describe 'SAML_SLO_SESSION_DESTROY_PROC' do + it 'destroys all user sessions when receiving IdP-initiated logout request' do + # Create a mock logout request with the user's email as name_id + logout_request = object_double(logout_request, name_id: saml_user.email) + + # Create a mock OmniAuth strategy + strategy = object_double(strategy, response_object: logout_request) + + # Create the environment hash that would be passed to the proc + env = { 'omniauth.strategy' => strategy } + + # Execute the SLO session destroy proc + SAML_SLO_SESSION_DESTROY_PROC.call(env, {}) + + # Verify all sessions were destroyed + saml_user.reload + expect(saml_user.tokens).to eq({}) + end + + it 'only destroys sessions for SAML provider users' do + # Create a non-SAML user + email_user = create(:user, email: 'email_user@example.com', provider: 'email', account: account) + email_user.tokens = { 'client1' => { 'token' => 'token1', 'expiry' => 1.hour.from_now.to_i } } + email_user.save! + + # Create another SAML user + saml_user2 = create(:user, email: 'another_saml@example.com', provider: 'saml', account: account) + saml_user2.tokens = { 'client2' => { 'token' => 'token2', 'expiry' => 1.hour.from_now.to_i } } + saml_user2.save! + + # Create logout request for the second SAML user + logout_request = object_double(logout_request, name_id: 'another_saml@example.com') + + strategy = object_double(strategy, response_object: logout_request) + + env = { 'omniauth.strategy' => strategy } + + # Execute the SLO session destroy proc + SAML_SLO_SESSION_DESTROY_PROC.call(env, {}) + + # Verify only the targeted SAML user sessions were destroyed + email_user.reload + saml_user.reload + saml_user2.reload + + expect(email_user.tokens).not_to be_empty # Non-SAML user unaffected + expect(saml_user.tokens).not_to be_empty # Different SAML user unaffected + expect(saml_user2.tokens).to eq({}) # Target SAML user logged out + end + end + end end