authentik.providers.saml.tests.test_tasks

Tests for SAML provider tasks

  1"""Tests for SAML provider tasks"""
  2
  3from unittest.mock import MagicMock, patch
  4
  5from django.test import TestCase
  6from requests.exceptions import ConnectionError, HTTPError
  7
  8from authentik.common.saml.constants import SAML_NAME_ID_FORMAT_EMAIL
  9from authentik.core.tests.utils import create_test_cert, create_test_flow
 10from authentik.providers.saml.models import SAMLProvider
 11from authentik.providers.saml.tasks import (
 12    send_post_logout_request,
 13    send_saml_logout_request,
 14    send_saml_logout_response,
 15)
 16
 17
 18class TestSendSamlLogoutResponse(TestCase):
 19    """Tests for send_saml_logout_response task"""
 20
 21    def setUp(self):
 22        """Set up test fixtures"""
 23        self.cert = create_test_cert()
 24        self.flow = create_test_flow()
 25
 26        self.provider = SAMLProvider.objects.create(
 27            name="test-provider",
 28            authorization_flow=self.flow,
 29            acs_url="https://sp.example.com/acs",
 30            sls_url="https://sp.example.com/sls",
 31            issuer_override="https://idp.example.com",
 32            signing_kp=self.cert,
 33        )
 34
 35    @patch("authentik.providers.saml.tasks.requests.post")
 36    def test_successful_logout_response(self, mock_post):
 37        """Test successful POST to SP returns True"""
 38        mock_response = MagicMock()
 39        mock_response.status_code = 200
 40        mock_response.raise_for_status = MagicMock()
 41        mock_post.return_value = mock_response
 42
 43        result = send_saml_logout_response(
 44            provider_pk=self.provider.pk,
 45            sls_url=self.provider.sls_url,
 46            logout_request_id="test-request-id",
 47            relay_state="https://sp.example.com/return",
 48        )
 49
 50        self.assertTrue(result)
 51        mock_post.assert_called_once()
 52
 53        # Verify the POST was made with correct data
 54        call_kwargs = mock_post.call_args[1]
 55        self.assertEqual(call_kwargs["timeout"], 10)
 56        self.assertEqual(
 57            call_kwargs["headers"]["Content-Type"], "application/x-www-form-urlencoded"
 58        )
 59
 60        # Verify form data contains SAMLResponse and RelayState
 61        form_data = call_kwargs["data"]
 62        self.assertIn("SAMLResponse", form_data)
 63        self.assertIn("RelayState", form_data)
 64        self.assertEqual(form_data["RelayState"], "https://sp.example.com/return")
 65
 66    @patch("authentik.providers.saml.tasks.requests.post")
 67    def test_successful_logout_response_no_relay_state(self, mock_post):
 68        """Test successful POST without relay_state"""
 69        mock_response = MagicMock()
 70        mock_response.status_code = 200
 71        mock_response.raise_for_status = MagicMock()
 72        mock_post.return_value = mock_response
 73
 74        result = send_saml_logout_response(
 75            provider_pk=self.provider.pk,
 76            sls_url=self.provider.sls_url,
 77            logout_request_id="test-request-id",
 78            relay_state=None,
 79        )
 80
 81        self.assertTrue(result)
 82
 83        # Verify form data does not contain RelayState
 84        form_data = mock_post.call_args[1]["data"]
 85        self.assertIn("SAMLResponse", form_data)
 86        self.assertNotIn("RelayState", form_data)
 87
 88    def test_provider_not_found(self):
 89        """Test returns False when provider doesn't exist"""
 90        result = send_saml_logout_response(
 91            provider_pk=99999,  # Non-existent provider
 92            sls_url="https://sp.example.com/sls",
 93            logout_request_id="test-request-id",
 94            relay_state=None,
 95        )
 96
 97        self.assertFalse(result)
 98
 99    @patch("authentik.providers.saml.tasks.Event")
100    @patch("authentik.providers.saml.tasks.requests.post")
101    def test_http_error_creates_event(self, mock_post, mock_event_class):
102        """Test HTTP error creates an error event"""
103        mock_response = MagicMock()
104        mock_response.status_code = 500
105        mock_response.raise_for_status.side_effect = HTTPError("500 Server Error")
106        mock_post.return_value = mock_response
107
108        mock_event = MagicMock()
109        mock_event_class.new.return_value = mock_event
110
111        result = send_saml_logout_response(
112            provider_pk=self.provider.pk,
113            sls_url=self.provider.sls_url,
114            logout_request_id="test-request-id",
115            relay_state=None,
116        )
117
118        self.assertFalse(result)
119
120        # Verify error event was created
121        mock_event_class.new.assert_called_once()
122        call_kwargs = mock_event_class.new.call_args[1]
123        self.assertIn("Backchannel logout response failed", call_kwargs["message"])
124        mock_event.save.assert_called_once()
125
126
127class TestSendSamlLogoutRequest(TestCase):
128    """Tests for send_saml_logout_request task"""
129
130    def setUp(self):
131        """Set up test fixtures"""
132        self.cert = create_test_cert()
133        self.flow = create_test_flow()
134
135        self.provider = SAMLProvider.objects.create(
136            name="test-provider",
137            authorization_flow=self.flow,
138            acs_url="https://sp.example.com/acs",
139            sls_url="https://sp.example.com/sls",
140            issuer_override="https://idp.example.com",
141            signing_kp=self.cert,
142        )
143
144    @patch("authentik.providers.saml.tasks.requests.post")
145    def test_successful_logout_request(self, mock_post):
146        """Test successful POST logout request returns True"""
147        mock_response = MagicMock()
148        mock_response.status_code = 200
149        mock_response.raise_for_status = MagicMock()
150        mock_post.return_value = mock_response
151
152        result = send_saml_logout_request(
153            provider_pk=self.provider.pk,
154            sls_url=self.provider.sls_url,
155            name_id="test@example.com",
156            name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
157            session_index="test-session-123",
158            issuer="https://idp.example.com",
159        )
160
161        self.assertTrue(result)
162        mock_post.assert_called_once()
163
164        # Verify the POST was made with correct data
165        call_kwargs = mock_post.call_args[1]
166        self.assertEqual(call_kwargs["timeout"], 10)
167        self.assertEqual(
168            call_kwargs["headers"]["Content-Type"], "application/x-www-form-urlencoded"
169        )
170
171        # Verify form data contains SAMLRequest
172        form_data = call_kwargs["data"]
173        self.assertIn("SAMLRequest", form_data)
174
175    def test_provider_not_found(self):
176        """Test returns False when provider doesn't exist"""
177        result = send_saml_logout_request(
178            provider_pk=99999,  # Non-existent provider
179            sls_url="https://sp.example.com/sls",
180            name_id="test@example.com",
181            name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
182            session_index="test-session-123",
183            issuer="https://idp.example.com",
184        )
185
186        self.assertFalse(result)
187
188    @patch("authentik.providers.saml.tasks.requests.post")
189    def test_http_error_raises(self, mock_post):
190        """Test HTTP error raises exception (no try/catch in send_post_logout_request)"""
191        mock_response = MagicMock()
192        mock_response.status_code = 500
193        mock_response.raise_for_status.side_effect = HTTPError("500 Server Error")
194        mock_post.return_value = mock_response
195
196        with self.assertRaises(HTTPError):
197            send_saml_logout_request(
198                provider_pk=self.provider.pk,
199                sls_url=self.provider.sls_url,
200                name_id="test@example.com",
201                name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
202                session_index="test-session-123",
203                issuer="https://idp.example.com",
204            )
205
206
207class TestSendPostLogoutRequest(TestCase):
208    """Tests for send_post_logout_request function"""
209
210    def setUp(self):
211        """Set up test fixtures"""
212        self.cert = create_test_cert()
213        self.flow = create_test_flow()
214
215        self.provider = SAMLProvider.objects.create(
216            name="test-provider",
217            authorization_flow=self.flow,
218            acs_url="https://sp.example.com/acs",
219            sls_url="https://sp.example.com/sls",
220            issuer_override="https://idp.example.com",
221            signing_kp=self.cert,
222        )
223
224    @patch("authentik.providers.saml.tasks.requests.post")
225    def test_successful_post(self, mock_post):
226        """Test successful POST returns True"""
227        from authentik.providers.saml.processors.logout_request import LogoutRequestProcessor
228
229        mock_response = MagicMock()
230        mock_response.status_code = 200
231        mock_response.raise_for_status = MagicMock()
232        mock_post.return_value = mock_response
233
234        processor = LogoutRequestProcessor(
235            provider=self.provider,
236            user=None,
237            destination=self.provider.sls_url,
238            name_id="test@example.com",
239            name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
240            session_index="test-session-123",
241        )
242
243        result = send_post_logout_request(self.provider, processor)
244
245        self.assertTrue(result)
246        mock_post.assert_called_once()
247
248    @patch("authentik.providers.saml.tasks.requests.post")
249    def test_with_relay_state(self, mock_post):
250        """Test POST includes RelayState when present"""
251        from authentik.providers.saml.processors.logout_request import LogoutRequestProcessor
252
253        mock_response = MagicMock()
254        mock_response.status_code = 200
255        mock_response.raise_for_status = MagicMock()
256        mock_post.return_value = mock_response
257
258        processor = LogoutRequestProcessor(
259            provider=self.provider,
260            user=None,
261            destination=self.provider.sls_url,
262            name_id="test@example.com",
263            name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
264            session_index="test-session-123",
265            relay_state="https://sp.example.com/return",
266        )
267
268        result = send_post_logout_request(self.provider, processor)
269
270        self.assertTrue(result)
271
272        # Verify RelayState is included
273        form_data = mock_post.call_args[1]["data"]
274        self.assertIn("RelayState", form_data)
275        self.assertEqual(form_data["RelayState"], "https://sp.example.com/return")
276
277    @patch("authentik.providers.saml.tasks.requests.post")
278    def test_connection_error_raises(self, mock_post):
279        """Test connection error raises exception"""
280        from authentik.providers.saml.processors.logout_request import LogoutRequestProcessor
281
282        mock_post.side_effect = ConnectionError("Connection refused")
283
284        processor = LogoutRequestProcessor(
285            provider=self.provider,
286            user=None,
287            destination=self.provider.sls_url,
288            name_id="test@example.com",
289            name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
290            session_index="test-session-123",
291        )
292
293        with self.assertRaises(ConnectionError):
294            send_post_logout_request(self.provider, processor)
class TestSendSamlLogoutResponse(django.test.testcases.TestCase):
 19class TestSendSamlLogoutResponse(TestCase):
 20    """Tests for send_saml_logout_response task"""
 21
 22    def setUp(self):
 23        """Set up test fixtures"""
 24        self.cert = create_test_cert()
 25        self.flow = create_test_flow()
 26
 27        self.provider = SAMLProvider.objects.create(
 28            name="test-provider",
 29            authorization_flow=self.flow,
 30            acs_url="https://sp.example.com/acs",
 31            sls_url="https://sp.example.com/sls",
 32            issuer_override="https://idp.example.com",
 33            signing_kp=self.cert,
 34        )
 35
 36    @patch("authentik.providers.saml.tasks.requests.post")
 37    def test_successful_logout_response(self, mock_post):
 38        """Test successful POST to SP returns True"""
 39        mock_response = MagicMock()
 40        mock_response.status_code = 200
 41        mock_response.raise_for_status = MagicMock()
 42        mock_post.return_value = mock_response
 43
 44        result = send_saml_logout_response(
 45            provider_pk=self.provider.pk,
 46            sls_url=self.provider.sls_url,
 47            logout_request_id="test-request-id",
 48            relay_state="https://sp.example.com/return",
 49        )
 50
 51        self.assertTrue(result)
 52        mock_post.assert_called_once()
 53
 54        # Verify the POST was made with correct data
 55        call_kwargs = mock_post.call_args[1]
 56        self.assertEqual(call_kwargs["timeout"], 10)
 57        self.assertEqual(
 58            call_kwargs["headers"]["Content-Type"], "application/x-www-form-urlencoded"
 59        )
 60
 61        # Verify form data contains SAMLResponse and RelayState
 62        form_data = call_kwargs["data"]
 63        self.assertIn("SAMLResponse", form_data)
 64        self.assertIn("RelayState", form_data)
 65        self.assertEqual(form_data["RelayState"], "https://sp.example.com/return")
 66
 67    @patch("authentik.providers.saml.tasks.requests.post")
 68    def test_successful_logout_response_no_relay_state(self, mock_post):
 69        """Test successful POST without relay_state"""
 70        mock_response = MagicMock()
 71        mock_response.status_code = 200
 72        mock_response.raise_for_status = MagicMock()
 73        mock_post.return_value = mock_response
 74
 75        result = send_saml_logout_response(
 76            provider_pk=self.provider.pk,
 77            sls_url=self.provider.sls_url,
 78            logout_request_id="test-request-id",
 79            relay_state=None,
 80        )
 81
 82        self.assertTrue(result)
 83
 84        # Verify form data does not contain RelayState
 85        form_data = mock_post.call_args[1]["data"]
 86        self.assertIn("SAMLResponse", form_data)
 87        self.assertNotIn("RelayState", form_data)
 88
 89    def test_provider_not_found(self):
 90        """Test returns False when provider doesn't exist"""
 91        result = send_saml_logout_response(
 92            provider_pk=99999,  # Non-existent provider
 93            sls_url="https://sp.example.com/sls",
 94            logout_request_id="test-request-id",
 95            relay_state=None,
 96        )
 97
 98        self.assertFalse(result)
 99
100    @patch("authentik.providers.saml.tasks.Event")
101    @patch("authentik.providers.saml.tasks.requests.post")
102    def test_http_error_creates_event(self, mock_post, mock_event_class):
103        """Test HTTP error creates an error event"""
104        mock_response = MagicMock()
105        mock_response.status_code = 500
106        mock_response.raise_for_status.side_effect = HTTPError("500 Server Error")
107        mock_post.return_value = mock_response
108
109        mock_event = MagicMock()
110        mock_event_class.new.return_value = mock_event
111
112        result = send_saml_logout_response(
113            provider_pk=self.provider.pk,
114            sls_url=self.provider.sls_url,
115            logout_request_id="test-request-id",
116            relay_state=None,
117        )
118
119        self.assertFalse(result)
120
121        # Verify error event was created
122        mock_event_class.new.assert_called_once()
123        call_kwargs = mock_event_class.new.call_args[1]
124        self.assertIn("Backchannel logout response failed", call_kwargs["message"])
125        mock_event.save.assert_called_once()

Tests for send_saml_logout_response task

def setUp(self):
22    def setUp(self):
23        """Set up test fixtures"""
24        self.cert = create_test_cert()
25        self.flow = create_test_flow()
26
27        self.provider = SAMLProvider.objects.create(
28            name="test-provider",
29            authorization_flow=self.flow,
30            acs_url="https://sp.example.com/acs",
31            sls_url="https://sp.example.com/sls",
32            issuer_override="https://idp.example.com",
33            signing_kp=self.cert,
34        )

Set up test fixtures

@patch('authentik.providers.saml.tasks.requests.post')
def test_successful_logout_response(self, mock_post):
36    @patch("authentik.providers.saml.tasks.requests.post")
37    def test_successful_logout_response(self, mock_post):
38        """Test successful POST to SP returns True"""
39        mock_response = MagicMock()
40        mock_response.status_code = 200
41        mock_response.raise_for_status = MagicMock()
42        mock_post.return_value = mock_response
43
44        result = send_saml_logout_response(
45            provider_pk=self.provider.pk,
46            sls_url=self.provider.sls_url,
47            logout_request_id="test-request-id",
48            relay_state="https://sp.example.com/return",
49        )
50
51        self.assertTrue(result)
52        mock_post.assert_called_once()
53
54        # Verify the POST was made with correct data
55        call_kwargs = mock_post.call_args[1]
56        self.assertEqual(call_kwargs["timeout"], 10)
57        self.assertEqual(
58            call_kwargs["headers"]["Content-Type"], "application/x-www-form-urlencoded"
59        )
60
61        # Verify form data contains SAMLResponse and RelayState
62        form_data = call_kwargs["data"]
63        self.assertIn("SAMLResponse", form_data)
64        self.assertIn("RelayState", form_data)
65        self.assertEqual(form_data["RelayState"], "https://sp.example.com/return")

Test successful POST to SP returns True

@patch('authentik.providers.saml.tasks.requests.post')
def test_successful_logout_response_no_relay_state(self, mock_post):
67    @patch("authentik.providers.saml.tasks.requests.post")
68    def test_successful_logout_response_no_relay_state(self, mock_post):
69        """Test successful POST without relay_state"""
70        mock_response = MagicMock()
71        mock_response.status_code = 200
72        mock_response.raise_for_status = MagicMock()
73        mock_post.return_value = mock_response
74
75        result = send_saml_logout_response(
76            provider_pk=self.provider.pk,
77            sls_url=self.provider.sls_url,
78            logout_request_id="test-request-id",
79            relay_state=None,
80        )
81
82        self.assertTrue(result)
83
84        # Verify form data does not contain RelayState
85        form_data = mock_post.call_args[1]["data"]
86        self.assertIn("SAMLResponse", form_data)
87        self.assertNotIn("RelayState", form_data)

Test successful POST without relay_state

def test_provider_not_found(self):
89    def test_provider_not_found(self):
90        """Test returns False when provider doesn't exist"""
91        result = send_saml_logout_response(
92            provider_pk=99999,  # Non-existent provider
93            sls_url="https://sp.example.com/sls",
94            logout_request_id="test-request-id",
95            relay_state=None,
96        )
97
98        self.assertFalse(result)

Test returns False when provider doesn't exist

@patch('authentik.providers.saml.tasks.Event')
@patch('authentik.providers.saml.tasks.requests.post')
def test_http_error_creates_event(self, mock_post, mock_event_class):
100    @patch("authentik.providers.saml.tasks.Event")
101    @patch("authentik.providers.saml.tasks.requests.post")
102    def test_http_error_creates_event(self, mock_post, mock_event_class):
103        """Test HTTP error creates an error event"""
104        mock_response = MagicMock()
105        mock_response.status_code = 500
106        mock_response.raise_for_status.side_effect = HTTPError("500 Server Error")
107        mock_post.return_value = mock_response
108
109        mock_event = MagicMock()
110        mock_event_class.new.return_value = mock_event
111
112        result = send_saml_logout_response(
113            provider_pk=self.provider.pk,
114            sls_url=self.provider.sls_url,
115            logout_request_id="test-request-id",
116            relay_state=None,
117        )
118
119        self.assertFalse(result)
120
121        # Verify error event was created
122        mock_event_class.new.assert_called_once()
123        call_kwargs = mock_event_class.new.call_args[1]
124        self.assertIn("Backchannel logout response failed", call_kwargs["message"])
125        mock_event.save.assert_called_once()

Test HTTP error creates an error event

class TestSendSamlLogoutRequest(django.test.testcases.TestCase):
128class TestSendSamlLogoutRequest(TestCase):
129    """Tests for send_saml_logout_request task"""
130
131    def setUp(self):
132        """Set up test fixtures"""
133        self.cert = create_test_cert()
134        self.flow = create_test_flow()
135
136        self.provider = SAMLProvider.objects.create(
137            name="test-provider",
138            authorization_flow=self.flow,
139            acs_url="https://sp.example.com/acs",
140            sls_url="https://sp.example.com/sls",
141            issuer_override="https://idp.example.com",
142            signing_kp=self.cert,
143        )
144
145    @patch("authentik.providers.saml.tasks.requests.post")
146    def test_successful_logout_request(self, mock_post):
147        """Test successful POST logout request returns True"""
148        mock_response = MagicMock()
149        mock_response.status_code = 200
150        mock_response.raise_for_status = MagicMock()
151        mock_post.return_value = mock_response
152
153        result = send_saml_logout_request(
154            provider_pk=self.provider.pk,
155            sls_url=self.provider.sls_url,
156            name_id="test@example.com",
157            name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
158            session_index="test-session-123",
159            issuer="https://idp.example.com",
160        )
161
162        self.assertTrue(result)
163        mock_post.assert_called_once()
164
165        # Verify the POST was made with correct data
166        call_kwargs = mock_post.call_args[1]
167        self.assertEqual(call_kwargs["timeout"], 10)
168        self.assertEqual(
169            call_kwargs["headers"]["Content-Type"], "application/x-www-form-urlencoded"
170        )
171
172        # Verify form data contains SAMLRequest
173        form_data = call_kwargs["data"]
174        self.assertIn("SAMLRequest", form_data)
175
176    def test_provider_not_found(self):
177        """Test returns False when provider doesn't exist"""
178        result = send_saml_logout_request(
179            provider_pk=99999,  # Non-existent provider
180            sls_url="https://sp.example.com/sls",
181            name_id="test@example.com",
182            name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
183            session_index="test-session-123",
184            issuer="https://idp.example.com",
185        )
186
187        self.assertFalse(result)
188
189    @patch("authentik.providers.saml.tasks.requests.post")
190    def test_http_error_raises(self, mock_post):
191        """Test HTTP error raises exception (no try/catch in send_post_logout_request)"""
192        mock_response = MagicMock()
193        mock_response.status_code = 500
194        mock_response.raise_for_status.side_effect = HTTPError("500 Server Error")
195        mock_post.return_value = mock_response
196
197        with self.assertRaises(HTTPError):
198            send_saml_logout_request(
199                provider_pk=self.provider.pk,
200                sls_url=self.provider.sls_url,
201                name_id="test@example.com",
202                name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
203                session_index="test-session-123",
204                issuer="https://idp.example.com",
205            )

Tests for send_saml_logout_request task

def setUp(self):
131    def setUp(self):
132        """Set up test fixtures"""
133        self.cert = create_test_cert()
134        self.flow = create_test_flow()
135
136        self.provider = SAMLProvider.objects.create(
137            name="test-provider",
138            authorization_flow=self.flow,
139            acs_url="https://sp.example.com/acs",
140            sls_url="https://sp.example.com/sls",
141            issuer_override="https://idp.example.com",
142            signing_kp=self.cert,
143        )

Set up test fixtures

@patch('authentik.providers.saml.tasks.requests.post')
def test_successful_logout_request(self, mock_post):
145    @patch("authentik.providers.saml.tasks.requests.post")
146    def test_successful_logout_request(self, mock_post):
147        """Test successful POST logout request returns True"""
148        mock_response = MagicMock()
149        mock_response.status_code = 200
150        mock_response.raise_for_status = MagicMock()
151        mock_post.return_value = mock_response
152
153        result = send_saml_logout_request(
154            provider_pk=self.provider.pk,
155            sls_url=self.provider.sls_url,
156            name_id="test@example.com",
157            name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
158            session_index="test-session-123",
159            issuer="https://idp.example.com",
160        )
161
162        self.assertTrue(result)
163        mock_post.assert_called_once()
164
165        # Verify the POST was made with correct data
166        call_kwargs = mock_post.call_args[1]
167        self.assertEqual(call_kwargs["timeout"], 10)
168        self.assertEqual(
169            call_kwargs["headers"]["Content-Type"], "application/x-www-form-urlencoded"
170        )
171
172        # Verify form data contains SAMLRequest
173        form_data = call_kwargs["data"]
174        self.assertIn("SAMLRequest", form_data)

Test successful POST logout request returns True

def test_provider_not_found(self):
176    def test_provider_not_found(self):
177        """Test returns False when provider doesn't exist"""
178        result = send_saml_logout_request(
179            provider_pk=99999,  # Non-existent provider
180            sls_url="https://sp.example.com/sls",
181            name_id="test@example.com",
182            name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
183            session_index="test-session-123",
184            issuer="https://idp.example.com",
185        )
186
187        self.assertFalse(result)

Test returns False when provider doesn't exist

@patch('authentik.providers.saml.tasks.requests.post')
def test_http_error_raises(self, mock_post):
189    @patch("authentik.providers.saml.tasks.requests.post")
190    def test_http_error_raises(self, mock_post):
191        """Test HTTP error raises exception (no try/catch in send_post_logout_request)"""
192        mock_response = MagicMock()
193        mock_response.status_code = 500
194        mock_response.raise_for_status.side_effect = HTTPError("500 Server Error")
195        mock_post.return_value = mock_response
196
197        with self.assertRaises(HTTPError):
198            send_saml_logout_request(
199                provider_pk=self.provider.pk,
200                sls_url=self.provider.sls_url,
201                name_id="test@example.com",
202                name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
203                session_index="test-session-123",
204                issuer="https://idp.example.com",
205            )

Test HTTP error raises exception (no try/catch in send_post_logout_request)

class TestSendPostLogoutRequest(django.test.testcases.TestCase):
208class TestSendPostLogoutRequest(TestCase):
209    """Tests for send_post_logout_request function"""
210
211    def setUp(self):
212        """Set up test fixtures"""
213        self.cert = create_test_cert()
214        self.flow = create_test_flow()
215
216        self.provider = SAMLProvider.objects.create(
217            name="test-provider",
218            authorization_flow=self.flow,
219            acs_url="https://sp.example.com/acs",
220            sls_url="https://sp.example.com/sls",
221            issuer_override="https://idp.example.com",
222            signing_kp=self.cert,
223        )
224
225    @patch("authentik.providers.saml.tasks.requests.post")
226    def test_successful_post(self, mock_post):
227        """Test successful POST returns True"""
228        from authentik.providers.saml.processors.logout_request import LogoutRequestProcessor
229
230        mock_response = MagicMock()
231        mock_response.status_code = 200
232        mock_response.raise_for_status = MagicMock()
233        mock_post.return_value = mock_response
234
235        processor = LogoutRequestProcessor(
236            provider=self.provider,
237            user=None,
238            destination=self.provider.sls_url,
239            name_id="test@example.com",
240            name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
241            session_index="test-session-123",
242        )
243
244        result = send_post_logout_request(self.provider, processor)
245
246        self.assertTrue(result)
247        mock_post.assert_called_once()
248
249    @patch("authentik.providers.saml.tasks.requests.post")
250    def test_with_relay_state(self, mock_post):
251        """Test POST includes RelayState when present"""
252        from authentik.providers.saml.processors.logout_request import LogoutRequestProcessor
253
254        mock_response = MagicMock()
255        mock_response.status_code = 200
256        mock_response.raise_for_status = MagicMock()
257        mock_post.return_value = mock_response
258
259        processor = LogoutRequestProcessor(
260            provider=self.provider,
261            user=None,
262            destination=self.provider.sls_url,
263            name_id="test@example.com",
264            name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
265            session_index="test-session-123",
266            relay_state="https://sp.example.com/return",
267        )
268
269        result = send_post_logout_request(self.provider, processor)
270
271        self.assertTrue(result)
272
273        # Verify RelayState is included
274        form_data = mock_post.call_args[1]["data"]
275        self.assertIn("RelayState", form_data)
276        self.assertEqual(form_data["RelayState"], "https://sp.example.com/return")
277
278    @patch("authentik.providers.saml.tasks.requests.post")
279    def test_connection_error_raises(self, mock_post):
280        """Test connection error raises exception"""
281        from authentik.providers.saml.processors.logout_request import LogoutRequestProcessor
282
283        mock_post.side_effect = ConnectionError("Connection refused")
284
285        processor = LogoutRequestProcessor(
286            provider=self.provider,
287            user=None,
288            destination=self.provider.sls_url,
289            name_id="test@example.com",
290            name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
291            session_index="test-session-123",
292        )
293
294        with self.assertRaises(ConnectionError):
295            send_post_logout_request(self.provider, processor)

Tests for send_post_logout_request function

def setUp(self):
211    def setUp(self):
212        """Set up test fixtures"""
213        self.cert = create_test_cert()
214        self.flow = create_test_flow()
215
216        self.provider = SAMLProvider.objects.create(
217            name="test-provider",
218            authorization_flow=self.flow,
219            acs_url="https://sp.example.com/acs",
220            sls_url="https://sp.example.com/sls",
221            issuer_override="https://idp.example.com",
222            signing_kp=self.cert,
223        )

Set up test fixtures

@patch('authentik.providers.saml.tasks.requests.post')
def test_successful_post(self, mock_post):
225    @patch("authentik.providers.saml.tasks.requests.post")
226    def test_successful_post(self, mock_post):
227        """Test successful POST returns True"""
228        from authentik.providers.saml.processors.logout_request import LogoutRequestProcessor
229
230        mock_response = MagicMock()
231        mock_response.status_code = 200
232        mock_response.raise_for_status = MagicMock()
233        mock_post.return_value = mock_response
234
235        processor = LogoutRequestProcessor(
236            provider=self.provider,
237            user=None,
238            destination=self.provider.sls_url,
239            name_id="test@example.com",
240            name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
241            session_index="test-session-123",
242        )
243
244        result = send_post_logout_request(self.provider, processor)
245
246        self.assertTrue(result)
247        mock_post.assert_called_once()

Test successful POST returns True

@patch('authentik.providers.saml.tasks.requests.post')
def test_with_relay_state(self, mock_post):
249    @patch("authentik.providers.saml.tasks.requests.post")
250    def test_with_relay_state(self, mock_post):
251        """Test POST includes RelayState when present"""
252        from authentik.providers.saml.processors.logout_request import LogoutRequestProcessor
253
254        mock_response = MagicMock()
255        mock_response.status_code = 200
256        mock_response.raise_for_status = MagicMock()
257        mock_post.return_value = mock_response
258
259        processor = LogoutRequestProcessor(
260            provider=self.provider,
261            user=None,
262            destination=self.provider.sls_url,
263            name_id="test@example.com",
264            name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
265            session_index="test-session-123",
266            relay_state="https://sp.example.com/return",
267        )
268
269        result = send_post_logout_request(self.provider, processor)
270
271        self.assertTrue(result)
272
273        # Verify RelayState is included
274        form_data = mock_post.call_args[1]["data"]
275        self.assertIn("RelayState", form_data)
276        self.assertEqual(form_data["RelayState"], "https://sp.example.com/return")

Test POST includes RelayState when present

@patch('authentik.providers.saml.tasks.requests.post')
def test_connection_error_raises(self, mock_post):
278    @patch("authentik.providers.saml.tasks.requests.post")
279    def test_connection_error_raises(self, mock_post):
280        """Test connection error raises exception"""
281        from authentik.providers.saml.processors.logout_request import LogoutRequestProcessor
282
283        mock_post.side_effect = ConnectionError("Connection refused")
284
285        processor = LogoutRequestProcessor(
286            provider=self.provider,
287            user=None,
288            destination=self.provider.sls_url,
289            name_id="test@example.com",
290            name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
291            session_index="test-session-123",
292        )
293
294        with self.assertRaises(ConnectionError):
295            send_post_logout_request(self.provider, processor)

Test connection error raises exception