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="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="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        )
159
160        self.assertTrue(result)
161        mock_post.assert_called_once()
162
163        # Verify the POST was made with correct data
164        call_kwargs = mock_post.call_args[1]
165        self.assertEqual(call_kwargs["timeout"], 10)
166        self.assertEqual(
167            call_kwargs["headers"]["Content-Type"], "application/x-www-form-urlencoded"
168        )
169
170        # Verify form data contains SAMLRequest
171        form_data = call_kwargs["data"]
172        self.assertIn("SAMLRequest", form_data)
173
174    def test_provider_not_found(self):
175        """Test returns False when provider doesn't exist"""
176        result = send_saml_logout_request(
177            provider_pk=99999,  # Non-existent provider
178            sls_url="https://sp.example.com/sls",
179            name_id="test@example.com",
180            name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
181            session_index="test-session-123",
182        )
183
184        self.assertFalse(result)
185
186    @patch("authentik.providers.saml.tasks.requests.post")
187    def test_http_error_raises(self, mock_post):
188        """Test HTTP error raises exception (no try/catch in send_post_logout_request)"""
189        mock_response = MagicMock()
190        mock_response.status_code = 500
191        mock_response.raise_for_status.side_effect = HTTPError("500 Server Error")
192        mock_post.return_value = mock_response
193
194        with self.assertRaises(HTTPError):
195            send_saml_logout_request(
196                provider_pk=self.provider.pk,
197                sls_url=self.provider.sls_url,
198                name_id="test@example.com",
199                name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
200                session_index="test-session-123",
201            )
202
203
204class TestSendPostLogoutRequest(TestCase):
205    """Tests for send_post_logout_request function"""
206
207    def setUp(self):
208        """Set up test fixtures"""
209        self.cert = create_test_cert()
210        self.flow = create_test_flow()
211
212        self.provider = SAMLProvider.objects.create(
213            name="test-provider",
214            authorization_flow=self.flow,
215            acs_url="https://sp.example.com/acs",
216            sls_url="https://sp.example.com/sls",
217            issuer="https://idp.example.com",
218            signing_kp=self.cert,
219        )
220
221    @patch("authentik.providers.saml.tasks.requests.post")
222    def test_successful_post(self, mock_post):
223        """Test successful POST returns True"""
224        from authentik.providers.saml.processors.logout_request import LogoutRequestProcessor
225
226        mock_response = MagicMock()
227        mock_response.status_code = 200
228        mock_response.raise_for_status = MagicMock()
229        mock_post.return_value = mock_response
230
231        processor = LogoutRequestProcessor(
232            provider=self.provider,
233            user=None,
234            destination=self.provider.sls_url,
235            name_id="test@example.com",
236            name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
237            session_index="test-session-123",
238        )
239
240        result = send_post_logout_request(self.provider, processor)
241
242        self.assertTrue(result)
243        mock_post.assert_called_once()
244
245    @patch("authentik.providers.saml.tasks.requests.post")
246    def test_with_relay_state(self, mock_post):
247        """Test POST includes RelayState when present"""
248        from authentik.providers.saml.processors.logout_request import LogoutRequestProcessor
249
250        mock_response = MagicMock()
251        mock_response.status_code = 200
252        mock_response.raise_for_status = MagicMock()
253        mock_post.return_value = mock_response
254
255        processor = LogoutRequestProcessor(
256            provider=self.provider,
257            user=None,
258            destination=self.provider.sls_url,
259            name_id="test@example.com",
260            name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
261            session_index="test-session-123",
262            relay_state="https://sp.example.com/return",
263        )
264
265        result = send_post_logout_request(self.provider, processor)
266
267        self.assertTrue(result)
268
269        # Verify RelayState is included
270        form_data = mock_post.call_args[1]["data"]
271        self.assertIn("RelayState", form_data)
272        self.assertEqual(form_data["RelayState"], "https://sp.example.com/return")
273
274    @patch("authentik.providers.saml.tasks.requests.post")
275    def test_connection_error_raises(self, mock_post):
276        """Test connection error raises exception"""
277        from authentik.providers.saml.processors.logout_request import LogoutRequestProcessor
278
279        mock_post.side_effect = ConnectionError("Connection refused")
280
281        processor = LogoutRequestProcessor(
282            provider=self.provider,
283            user=None,
284            destination=self.provider.sls_url,
285            name_id="test@example.com",
286            name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
287            session_index="test-session-123",
288        )
289
290        with self.assertRaises(ConnectionError):
291            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="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="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="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        )
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        )
184
185        self.assertFalse(result)
186
187    @patch("authentik.providers.saml.tasks.requests.post")
188    def test_http_error_raises(self, mock_post):
189        """Test HTTP error raises exception (no try/catch in send_post_logout_request)"""
190        mock_response = MagicMock()
191        mock_response.status_code = 500
192        mock_response.raise_for_status.side_effect = HTTPError("500 Server Error")
193        mock_post.return_value = mock_response
194
195        with self.assertRaises(HTTPError):
196            send_saml_logout_request(
197                provider_pk=self.provider.pk,
198                sls_url=self.provider.sls_url,
199                name_id="test@example.com",
200                name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
201                session_index="test-session-123",
202            )

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="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        )
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)

Test successful POST logout request returns True

def test_provider_not_found(self):
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        )
184
185        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):
187    @patch("authentik.providers.saml.tasks.requests.post")
188    def test_http_error_raises(self, mock_post):
189        """Test HTTP error raises exception (no try/catch in send_post_logout_request)"""
190        mock_response = MagicMock()
191        mock_response.status_code = 500
192        mock_response.raise_for_status.side_effect = HTTPError("500 Server Error")
193        mock_post.return_value = mock_response
194
195        with self.assertRaises(HTTPError):
196            send_saml_logout_request(
197                provider_pk=self.provider.pk,
198                sls_url=self.provider.sls_url,
199                name_id="test@example.com",
200                name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
201                session_index="test-session-123",
202            )

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

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

Tests for send_post_logout_request function

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

Set up test fixtures

@patch('authentik.providers.saml.tasks.requests.post')
def test_successful_post(self, mock_post):
222    @patch("authentik.providers.saml.tasks.requests.post")
223    def test_successful_post(self, mock_post):
224        """Test successful POST returns True"""
225        from authentik.providers.saml.processors.logout_request import LogoutRequestProcessor
226
227        mock_response = MagicMock()
228        mock_response.status_code = 200
229        mock_response.raise_for_status = MagicMock()
230        mock_post.return_value = mock_response
231
232        processor = LogoutRequestProcessor(
233            provider=self.provider,
234            user=None,
235            destination=self.provider.sls_url,
236            name_id="test@example.com",
237            name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
238            session_index="test-session-123",
239        )
240
241        result = send_post_logout_request(self.provider, processor)
242
243        self.assertTrue(result)
244        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):
246    @patch("authentik.providers.saml.tasks.requests.post")
247    def test_with_relay_state(self, mock_post):
248        """Test POST includes RelayState when present"""
249        from authentik.providers.saml.processors.logout_request import LogoutRequestProcessor
250
251        mock_response = MagicMock()
252        mock_response.status_code = 200
253        mock_response.raise_for_status = MagicMock()
254        mock_post.return_value = mock_response
255
256        processor = LogoutRequestProcessor(
257            provider=self.provider,
258            user=None,
259            destination=self.provider.sls_url,
260            name_id="test@example.com",
261            name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
262            session_index="test-session-123",
263            relay_state="https://sp.example.com/return",
264        )
265
266        result = send_post_logout_request(self.provider, processor)
267
268        self.assertTrue(result)
269
270        # Verify RelayState is included
271        form_data = mock_post.call_args[1]["data"]
272        self.assertIn("RelayState", form_data)
273        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):
275    @patch("authentik.providers.saml.tasks.requests.post")
276    def test_connection_error_raises(self, mock_post):
277        """Test connection error raises exception"""
278        from authentik.providers.saml.processors.logout_request import LogoutRequestProcessor
279
280        mock_post.side_effect = ConnectionError("Connection refused")
281
282        processor = LogoutRequestProcessor(
283            provider=self.provider,
284            user=None,
285            destination=self.provider.sls_url,
286            name_id="test@example.com",
287            name_id_format=SAML_NAME_ID_FORMAT_EMAIL,
288            session_index="test-session-123",
289        )
290
291        with self.assertRaises(ConnectionError):
292            send_post_logout_request(self.provider, processor)

Test connection error raises exception