39
39
DISCOSRV_URL = "https://my.dicso.com/role/idp.ds"
40
40
41
41
42
- class TestSAMLBackend :
43
- def assert_redirect_to_idp (self , redirect_response , idp_conf ):
44
- assert redirect_response .status == "303 See Other"
45
- parsed = urlparse (redirect_response .message )
46
- redirect_location = "{parsed.scheme}://{parsed.netloc}{parsed.path}" .format (parsed = parsed )
47
- assert redirect_location == idp_conf ["service" ]["idp" ]["endpoints" ]["single_sign_on_service" ][0 ][0 ]
48
- assert "SAMLRequest" in parse_qs (parsed .query )
49
-
50
- def assert_redirect_to_discovery_server (
51
- self , redirect_response , sp_conf , expected_discosrv_url
52
- ):
53
- assert redirect_response .status == "303 See Other"
54
- parsed = urlparse (redirect_response .message )
55
- redirect_location = "{parsed.scheme}://{parsed.netloc}{parsed.path}" .format (parsed = parsed )
56
- assert redirect_location == expected_discosrv_url
57
-
58
- request_params = dict (parse_qsl (parsed .query ))
59
- assert request_params ["return" ] == sp_conf ["service" ]["sp" ]["endpoints" ]["discovery_response" ][0 ][0 ]
60
- assert request_params ["entityID" ] == sp_conf ["entityid" ]
61
-
62
- def assert_authn_response (self , internal_resp ):
63
- assert internal_resp .auth_info .auth_class_ref == PASSWORD
64
- expected_data = {
'surname' : [
'Testsson 1' ],
'mail' : [
'[email protected] ' ],
65
- 'displayname' : ['Test Testsson' ], 'givenname' : ['Test 1' ],
66
- 'edupersontargetedid' : ['one!for!all' ]}
67
- assert expected_data == internal_resp .attributes
68
-
69
- def setup_test_config (self , sp_conf , idp_conf ):
70
- idp_metadata_str = create_metadata_from_config_dict (idp_conf )
71
- sp_conf ["metadata" ]["inline" ].append (idp_metadata_str )
72
- idp2_config = idp_conf .copy ()
73
- idp2_config ["entityid" ] = "just_an_extra_idp"
74
- idp_metadata_str2 = create_metadata_from_config_dict (idp2_config )
75
- sp_conf ["metadata" ]["inline" ].append (idp_metadata_str2 )
76
-
77
- sp_metadata_str = create_metadata_from_config_dict (sp_conf )
78
- idp_conf ["metadata" ]["inline" ] = [sp_metadata_str ]
42
+ def assert_redirect_to_discovery_server (
43
+ redirect_response , sp_conf , expected_discosrv_url
44
+ ):
45
+ assert redirect_response .status == "303 See Other"
46
+ parsed = urlparse (redirect_response .message )
47
+ redirect_location = "{parsed.scheme}://{parsed.netloc}{parsed.path}" .format (parsed = parsed )
48
+ assert redirect_location == expected_discosrv_url
49
+
50
+ request_params = dict (parse_qsl (parsed .query ))
51
+ assert request_params ["return" ] == sp_conf ["service" ]["sp" ]["endpoints" ]["discovery_response" ][0 ][0 ]
52
+ assert request_params ["entityID" ] == sp_conf ["entityid" ]
53
+
54
+
55
+ def assert_redirect_to_idp (redirect_response , idp_conf ):
56
+ assert redirect_response .status == "303 See Other"
57
+ parsed = urlparse (redirect_response .message )
58
+ redirect_location = "{parsed.scheme}://{parsed.netloc}{parsed.path}" .format (parsed = parsed )
59
+ assert redirect_location == idp_conf ["service" ]["idp" ]["endpoints" ]["single_sign_on_service" ][0 ][0 ]
60
+ assert "SAMLRequest" in parse_qs (parsed .query )
61
+
62
+
63
+ def assert_authn_response (internal_resp ):
64
+ assert internal_resp .auth_info .auth_class_ref == PASSWORD
65
+ expected_data = {
'surname' : [
'Testsson 1' ],
'mail' : [
'[email protected] ' ],
66
+ 'displayname' : ['Test Testsson' ], 'givenname' : ['Test 1' ],
67
+ 'edupersontargetedid' : ['one!for!all' ]}
68
+ assert expected_data == internal_resp .attributes
69
+
70
+
71
+ def setup_test_config (sp_conf , idp_conf ):
72
+ idp_metadata_str = create_metadata_from_config_dict (idp_conf )
73
+ sp_conf ["metadata" ]["inline" ].append (idp_metadata_str )
74
+ idp2_config = idp_conf .copy ()
75
+ idp2_config ["entityid" ] = "just_an_extra_idp"
76
+ idp_metadata_str2 = create_metadata_from_config_dict (idp2_config )
77
+ sp_conf ["metadata" ]["inline" ].append (idp_metadata_str2 )
78
+
79
+ sp_metadata_str = create_metadata_from_config_dict (sp_conf )
80
+ idp_conf ["metadata" ]["inline" ] = [sp_metadata_str ]
81
+
79
82
83
+ class TestSAMLBackend :
80
84
@pytest .fixture (autouse = True )
81
85
def create_backend (self , sp_conf , idp_conf ):
82
- self . setup_test_config (sp_conf , idp_conf )
86
+ setup_test_config (sp_conf , idp_conf )
83
87
self .samlbackend = SAMLBackend (Mock (), INTERNAL_ATTRIBUTES , {"sp_config" : sp_conf ,
84
88
"disco_srv" : DISCOSRV_URL },
85
89
"base_url" ,
@@ -101,15 +105,15 @@ def get_path_from_url(url):
101
105
102
106
def test_start_auth_defaults_to_redirecting_to_discovery_server (self , context , sp_conf ):
103
107
resp = self .samlbackend .start_auth (context , InternalData ())
104
- self . assert_redirect_to_discovery_server (resp , sp_conf , DISCOSRV_URL )
108
+ assert_redirect_to_discovery_server (resp , sp_conf , DISCOSRV_URL )
105
109
106
110
def test_discovery_server_set_in_context (self , context , sp_conf ):
107
111
discosrv_url = 'https://my.org/saml_discovery_service'
108
112
context .decorate (
109
113
SAMLBackend .KEY_SAML_DISCOVERY_SERVICE_URL , discosrv_url
110
114
)
111
115
resp = self .samlbackend .start_auth (context , InternalData ())
112
- self . assert_redirect_to_discovery_server (resp , sp_conf , discosrv_url )
116
+ assert_redirect_to_discovery_server (resp , sp_conf , discosrv_url )
113
117
114
118
def test_full_flow (self , context , idp_conf , sp_conf ):
115
119
test_state_key = "test_state_key_456afgrh"
@@ -120,7 +124,7 @@ def test_full_flow(self, context, idp_conf, sp_conf):
120
124
121
125
# start auth flow (redirecting to discovery server)
122
126
resp = self .samlbackend .start_auth (context , InternalData ())
123
- self . assert_redirect_to_discovery_server (resp , sp_conf , DISCOSRV_URL )
127
+ assert_redirect_to_discovery_server (resp , sp_conf , DISCOSRV_URL )
124
128
125
129
# fake response from discovery server
126
130
disco_resp = parse_qs (urlparse (resp .message ).query )
@@ -132,7 +136,7 @@ def test_full_flow(self, context, idp_conf, sp_conf):
132
136
133
137
# pass discovery response to backend and check that it redirects to the selected IdP
134
138
resp = self .samlbackend .disco_response (request_context )
135
- self . assert_redirect_to_idp (resp , idp_conf )
139
+ assert_redirect_to_idp (resp , idp_conf )
136
140
137
141
# fake auth response to the auth request
138
142
req_params = dict (parse_qsl (urlparse (resp .message ).query ))
@@ -151,95 +155,27 @@ def test_full_flow(self, context, idp_conf, sp_conf):
151
155
context , internal_resp = self .samlbackend .auth_callback_func .call_args [0 ]
152
156
assert self .samlbackend .name not in context .state
153
157
assert context .state [test_state_key ] == "my_state"
154
- self . assert_authn_response (internal_resp )
158
+ assert_authn_response (internal_resp )
155
159
156
160
def test_start_auth_redirects_directly_to_mirrored_idp (
157
161
self , context , idp_conf ):
158
162
entityid = idp_conf ["entityid" ]
159
163
context .decorate (Context .KEY_TARGET_ENTITYID , entityid )
160
164
161
165
resp = self .samlbackend .start_auth (context , InternalData ())
162
- self . assert_redirect_to_idp (resp , idp_conf )
166
+ assert_redirect_to_idp (resp , idp_conf )
163
167
164
168
def test_redirect_to_idp_if_only_one_idp_in_metadata (self , context , sp_conf , idp_conf ):
165
169
sp_conf ["metadata" ]["inline" ] = [create_metadata_from_config_dict (idp_conf )]
166
170
# instantiate new backend, without any discovery service configured
167
171
samlbackend = SAMLBackend (None , INTERNAL_ATTRIBUTES , {"sp_config" : sp_conf }, "base_url" , "saml_backend" )
168
172
169
173
resp = samlbackend .start_auth (context , InternalData ())
170
- self .assert_redirect_to_idp (resp , idp_conf )
171
-
172
- def test_default_redirect_to_discovery_service_if_using_mdq (self , context , sp_conf , idp_conf ):
173
- # one IdP in the metadata, but MDQ also configured so should always redirect to the discovery service
174
- sp_conf ["metadata" ]["inline" ] = [create_metadata_from_config_dict (idp_conf )]
175
- sp_conf ["metadata" ]["mdq" ] = ["https://mdq.example.com" ]
176
- samlbackend = SAMLBackend (None , INTERNAL_ATTRIBUTES , {"sp_config" : sp_conf , "disco_srv" : DISCOSRV_URL ,},
177
- "base_url" , "saml_backend" )
178
- resp = samlbackend .start_auth (context , InternalData ())
179
- self .assert_redirect_to_discovery_server (resp , sp_conf , DISCOSRV_URL )
180
-
181
- def test_use_of_disco_or_redirect_to_idp_when_using_mdq_and_forceauthn_is_not_set (
182
- self , context , sp_conf , idp_conf
183
- ):
184
- sp_conf ["metadata" ]["inline" ] = [create_metadata_from_config_dict (idp_conf )]
185
- sp_conf ["metadata" ]["mdq" ] = ["https://mdq.example.com" ]
186
-
187
- backend_conf = {
188
- SAMLBackend .KEY_SP_CONFIG : sp_conf ,
189
- SAMLBackend .KEY_DISCO_SRV : DISCOSRV_URL ,
190
- SAMLBackend .KEY_MEMORIZE_IDP : True ,
191
- }
192
- samlbackend = SAMLBackend (
193
- None , INTERNAL_ATTRIBUTES , backend_conf , "base_url" , "saml_backend"
194
- )
195
- resp = samlbackend .start_auth (context , InternalData ())
196
- self .assert_redirect_to_discovery_server (resp , sp_conf , DISCOSRV_URL )
197
-
198
- context .state [Context .KEY_MEMORIZED_IDP ] = idp_conf ["entityid" ]
199
- samlbackend = SAMLBackend (
200
- None , INTERNAL_ATTRIBUTES , backend_conf , "base_url" , "saml_backend"
201
- )
202
- resp = samlbackend .start_auth (context , InternalData ())
203
- self .assert_redirect_to_idp (resp , idp_conf )
204
-
205
- backend_conf [SAMLBackend .KEY_MEMORIZE_IDP ] = False
206
- samlbackend = SAMLBackend (
207
- None , INTERNAL_ATTRIBUTES , backend_conf , "base_url" , "saml_backend"
208
- )
209
- resp = samlbackend .start_auth (context , InternalData ())
210
- self .assert_redirect_to_discovery_server (resp , sp_conf , DISCOSRV_URL )
211
-
212
- def test_use_of_disco_or_redirect_to_idp_when_using_mdq_and_forceauthn_is_set (
213
- self , context , sp_conf , idp_conf
214
- ):
215
- sp_conf ["metadata" ]["inline" ] = [create_metadata_from_config_dict (idp_conf )]
216
- sp_conf ["metadata" ]["mdq" ] = ["https://mdq.example.com" ]
217
-
218
- context .decorate (Context .KEY_FORCE_AUTHN , "true" )
219
- context .state [Context .KEY_MEMORIZED_IDP ] = idp_conf ["entityid" ]
220
-
221
- backend_conf = {
222
- SAMLBackend .KEY_SP_CONFIG : sp_conf ,
223
- SAMLBackend .KEY_DISCO_SRV : DISCOSRV_URL ,
224
- SAMLBackend .KEY_MEMORIZE_IDP : True ,
225
- SAMLBackend .KEY_MIRROR_FORCE_AUTHN : True ,
226
- }
227
- samlbackend = SAMLBackend (
228
- None , INTERNAL_ATTRIBUTES , backend_conf , "base_url" , "saml_backend"
229
- )
230
- resp = samlbackend .start_auth (context , InternalData ())
231
- self .assert_redirect_to_discovery_server (resp , sp_conf , DISCOSRV_URL )
232
-
233
- backend_conf [SAMLBackend .KEY_USE_MEMORIZED_IDP_WHEN_FORCE_AUTHN ] = True
234
- samlbackend = SAMLBackend (
235
- None , INTERNAL_ATTRIBUTES , backend_conf , "base_url" , "saml_backend"
236
- )
237
- resp = samlbackend .start_auth (context , InternalData ())
238
- self .assert_redirect_to_idp (resp , idp_conf )
174
+ assert_redirect_to_idp (resp , idp_conf )
239
175
240
176
def test_authn_request (self , context , idp_conf ):
241
177
resp = self .samlbackend .authn_request (context , idp_conf ["entityid" ])
242
- self . assert_redirect_to_idp (resp , idp_conf )
178
+ assert_redirect_to_idp (resp , idp_conf )
243
179
req_params = dict (parse_qsl (urlparse (resp .message ).query ))
244
180
assert context .state [self .samlbackend .name ]["relay_state" ] == req_params ["RelayState" ]
245
181
@@ -257,7 +193,7 @@ def test_authn_response(self, context, idp_conf, sp_conf):
257
193
self .samlbackend .authn_response (context , response_binding )
258
194
259
195
context , internal_resp = self .samlbackend .auth_callback_func .call_args [0 ]
260
- self . assert_authn_response (internal_resp )
196
+ assert_authn_response (internal_resp )
261
197
assert self .samlbackend .name not in context .state
262
198
263
199
@pytest .mark .skipif (
@@ -293,7 +229,7 @@ def test_authn_response_no_name_id(self, context, idp_conf, sp_conf):
293
229
backend .authn_response (context , response_binding )
294
230
295
231
context , internal_resp = backend .auth_callback_func .call_args [0 ]
296
- self . assert_authn_response (internal_resp )
232
+ assert_authn_response (internal_resp )
297
233
assert backend .name not in context .state
298
234
299
235
def test_authn_response_with_encrypted_assertion (self , sp_conf , context ):
@@ -396,3 +332,112 @@ def test_get_metadata_desc_with_logo_without_lang(self, sp_conf, idp_conf):
396
332
assert ui_info ["display_name" ] == expected_ui_info ["display_name" ]
397
333
assert ui_info ["description" ] == expected_ui_info ["description" ]
398
334
assert ui_info ["logo" ] == expected_ui_info ["logo" ]
335
+
336
+
337
+ class TestSAMLBackendRedirects :
338
+ def test_default_redirect_to_discovery_service_if_using_mdq (
339
+ self , context , sp_conf , idp_conf
340
+ ):
341
+ # one IdP in the metadata, but MDQ also configured so should always redirect to the discovery service
342
+ sp_conf ["metadata" ]["inline" ] = [create_metadata_from_config_dict (idp_conf )]
343
+ sp_conf ["metadata" ]["mdq" ] = ["https://mdq.example.com" ]
344
+ samlbackend = SAMLBackend (None , INTERNAL_ATTRIBUTES , {"sp_config" : sp_conf , "disco_srv" : DISCOSRV_URL ,},
345
+ "base_url" , "saml_backend" )
346
+ resp = samlbackend .start_auth (context , InternalData ())
347
+ assert_redirect_to_discovery_server (resp , sp_conf , DISCOSRV_URL )
348
+
349
+ def test_use_of_disco_or_redirect_to_idp_when_using_mdq_and_forceauthn_is_not_set (
350
+ self , context , sp_conf , idp_conf
351
+ ):
352
+ sp_conf ["metadata" ]["inline" ] = [create_metadata_from_config_dict (idp_conf )]
353
+ sp_conf ["metadata" ]["mdq" ] = ["https://mdq.example.com" ]
354
+
355
+ backend_conf = {
356
+ SAMLBackend .KEY_SP_CONFIG : sp_conf ,
357
+ SAMLBackend .KEY_DISCO_SRV : DISCOSRV_URL ,
358
+ SAMLBackend .KEY_MEMORIZE_IDP : True ,
359
+ }
360
+ samlbackend = SAMLBackend (
361
+ None , INTERNAL_ATTRIBUTES , backend_conf , "base_url" , "saml_backend"
362
+ )
363
+ resp = samlbackend .start_auth (context , InternalData ())
364
+ assert_redirect_to_discovery_server (resp , sp_conf , DISCOSRV_URL )
365
+
366
+ context .state [Context .KEY_MEMORIZED_IDP ] = idp_conf ["entityid" ]
367
+ samlbackend = SAMLBackend (
368
+ None , INTERNAL_ATTRIBUTES , backend_conf , "base_url" , "saml_backend"
369
+ )
370
+ resp = samlbackend .start_auth (context , InternalData ())
371
+ assert_redirect_to_idp (resp , idp_conf )
372
+
373
+ backend_conf [SAMLBackend .KEY_MEMORIZE_IDP ] = False
374
+ samlbackend = SAMLBackend (
375
+ None , INTERNAL_ATTRIBUTES , backend_conf , "base_url" , "saml_backend"
376
+ )
377
+ resp = samlbackend .start_auth (context , InternalData ())
378
+ assert_redirect_to_discovery_server (resp , sp_conf , DISCOSRV_URL )
379
+
380
+ context .decorate (Context .KEY_FORCE_AUTHN , "0" )
381
+ context .state [Context .KEY_MEMORIZED_IDP ] = idp_conf ["entityid" ]
382
+ backend_conf [SAMLBackend .KEY_USE_MEMORIZED_IDP_WHEN_FORCE_AUTHN ] = True
383
+ samlbackend = SAMLBackend (
384
+ None , INTERNAL_ATTRIBUTES , backend_conf , "base_url" , "saml_backend"
385
+ )
386
+ resp = samlbackend .start_auth (context , InternalData ())
387
+ assert_redirect_to_discovery_server (resp , sp_conf , DISCOSRV_URL )
388
+
389
+ def test_use_of_disco_or_redirect_to_idp_when_using_mdq_and_forceauthn_is_set_true (
390
+ self , context , sp_conf , idp_conf
391
+ ):
392
+ sp_conf ["metadata" ]["inline" ] = [create_metadata_from_config_dict (idp_conf )]
393
+ sp_conf ["metadata" ]["mdq" ] = ["https://mdq.example.com" ]
394
+
395
+ context .decorate (Context .KEY_FORCE_AUTHN , "true" )
396
+ context .state [Context .KEY_MEMORIZED_IDP ] = idp_conf ["entityid" ]
397
+
398
+ backend_conf = {
399
+ SAMLBackend .KEY_SP_CONFIG : sp_conf ,
400
+ SAMLBackend .KEY_DISCO_SRV : DISCOSRV_URL ,
401
+ SAMLBackend .KEY_MEMORIZE_IDP : True ,
402
+ SAMLBackend .KEY_MIRROR_FORCE_AUTHN : True ,
403
+ }
404
+ samlbackend = SAMLBackend (
405
+ None , INTERNAL_ATTRIBUTES , backend_conf , "base_url" , "saml_backend"
406
+ )
407
+ resp = samlbackend .start_auth (context , InternalData ())
408
+ assert_redirect_to_discovery_server (resp , sp_conf , DISCOSRV_URL )
409
+
410
+ backend_conf [SAMLBackend .KEY_USE_MEMORIZED_IDP_WHEN_FORCE_AUTHN ] = True
411
+ samlbackend = SAMLBackend (
412
+ None , INTERNAL_ATTRIBUTES , backend_conf , "base_url" , "saml_backend"
413
+ )
414
+ resp = samlbackend .start_auth (context , InternalData ())
415
+ assert_redirect_to_idp (resp , idp_conf )
416
+
417
+ def test_use_of_disco_or_redirect_to_idp_when_using_mdq_and_forceauthn_is_set_1 (
418
+ self , context , sp_conf , idp_conf
419
+ ):
420
+ sp_conf ["metadata" ]["inline" ] = [create_metadata_from_config_dict (idp_conf )]
421
+ sp_conf ["metadata" ]["mdq" ] = ["https://mdq.example.com" ]
422
+
423
+ context .decorate (Context .KEY_FORCE_AUTHN , "1" )
424
+ context .state [Context .KEY_MEMORIZED_IDP ] = idp_conf ["entityid" ]
425
+
426
+ backend_conf = {
427
+ SAMLBackend .KEY_SP_CONFIG : sp_conf ,
428
+ SAMLBackend .KEY_DISCO_SRV : DISCOSRV_URL ,
429
+ SAMLBackend .KEY_MEMORIZE_IDP : True ,
430
+ SAMLBackend .KEY_MIRROR_FORCE_AUTHN : True ,
431
+ }
432
+ samlbackend = SAMLBackend (
433
+ None , INTERNAL_ATTRIBUTES , backend_conf , "base_url" , "saml_backend"
434
+ )
435
+ resp = samlbackend .start_auth (context , InternalData ())
436
+ assert_redirect_to_discovery_server (resp , sp_conf , DISCOSRV_URL )
437
+
438
+ backend_conf [SAMLBackend .KEY_USE_MEMORIZED_IDP_WHEN_FORCE_AUTHN ] = True
439
+ samlbackend = SAMLBackend (
440
+ None , INTERNAL_ATTRIBUTES , backend_conf , "base_url" , "saml_backend"
441
+ )
442
+ resp = samlbackend .start_auth (context , InternalData ())
443
+ assert_redirect_to_idp (resp , idp_conf )
0 commit comments