11"""
22Tests for the SAML frontend module src/frontends/saml2.py.
33"""
4+ import copy
45import itertools
56import re
67from collections import Counter
2829from satosa .internal import AuthenticationInformation
2930from satosa .internal import InternalData
3031from satosa .state import State
31- from satosa .context import Context
3232from tests .users import USERS
3333from tests .util import FakeSP , create_metadata_from_config_dict
3434
@@ -298,14 +298,14 @@ def test_acr_mapping_per_idp_in_authn_response(self, context, idp_conf, sp_conf,
298298 ]
299299 )
300300 def test_respect_sp_entity_categories (
301- self ,
302- context ,
303- entity_category ,
304- entity_category_module ,
305- expected_attributes ,
306- idp_conf ,
307- sp_conf ,
308- internal_response
301+ self ,
302+ context ,
303+ entity_category ,
304+ entity_category_module ,
305+ expected_attributes ,
306+ idp_conf ,
307+ sp_conf ,
308+ internal_response
309309 ):
310310 idp_metadata_str = create_metadata_from_config_dict (idp_conf )
311311 idp_conf ["service" ]["idp" ]["policy" ]["default" ]["entity_categories" ] = [entity_category_module ]
@@ -365,7 +365,7 @@ def test_metadata_endpoint(self, context, idp_conf):
365365 assert idp_conf ["entityid" ] in resp .message
366366
367367 def test_custom_attribute_release_with_less_attributes_than_entity_category (
368- self , context , idp_conf , sp_conf , internal_response
368+ self , context , idp_conf , sp_conf , internal_response
369369 ):
370370 idp_metadata_str = create_metadata_from_config_dict (idp_conf )
371371 idp_conf ["service" ]["idp" ]["policy" ]["default" ]["entity_categories" ] = ["swamid" ]
@@ -387,8 +387,8 @@ def test_custom_attribute_release_with_less_attributes_than_entity_category(
387387 internal_response .requester = sp_conf ["entityid" ]
388388 resp = self .get_auth_response (samlfrontend , context , internal_response , sp_conf , idp_metadata_str )
389389 assert len (resp .ava .keys ()) == (
390- len (expected_attributes )
391- - len (custom_attributes [internal_response .auth_info .issuer ][internal_response .requester ]["exclude" ])
390+ len (expected_attributes )
391+ - len (custom_attributes [internal_response .auth_info .issuer ][internal_response .requester ]["exclude" ])
392392 )
393393
394394
@@ -431,6 +431,7 @@ def test_load_idp_dynamic_entity_id(self, idp_conf):
431431
432432class TestSAMLVirtualCoFrontend (TestSAMLFrontend ):
433433 BACKEND = "test_backend"
434+ BACKEND_1 = "test_backend_1"
434435 CO = "MESS"
435436 CO_O = "organization"
436437 CO_C = "countryname"
@@ -442,7 +443,7 @@ class TestSAMLVirtualCoFrontend(TestSAMLFrontend):
442443 CO_C : ["US" ],
443444 CO_CO : ["United States" ],
444445 CO_NOREDUORGACRONYM : ["MESS" ]
445- }
446+ }
446447 KEY_SSO = "single_sign_on_service"
447448
448449 @pytest .fixture
@@ -471,10 +472,10 @@ def frontend(self, idp_conf, sp_conf):
471472 # endpoints, and the collaborative organization configuration to
472473 # create the configuration for the frontend.
473474 conf = {
474- "idp_config" : idp_conf ,
475- "endpoints" : ENDPOINTS ,
476- "collaborative_organizations" : [collab_org ]
477- }
475+ "idp_config" : idp_conf ,
476+ "endpoints" : ENDPOINTS ,
477+ "collaborative_organizations" : [collab_org ]
478+ }
478479
479480 # Use a richer set of internal attributes than what is provided
480481 # for the parent class so that we can test for the static SAML
@@ -504,10 +505,13 @@ def context(self, context):
504505 that would be available during a SAML flow and that would include
505506 a path and target_backend that indicates the CO.
506507 """
507- context .path = "{}/{}/sso/redirect" .format (self .BACKEND , self .CO )
508- context .target_backend = self .BACKEND
508+ return self ._make_context (context , self .BACKEND , self .CO )
509509
510- return context
510+ def _make_context (self , context , backend , co_name ):
511+ _context = copy .deepcopy (context )
512+ _context .path = "{}/{}/sso/redirect" .format (backend , co_name )
513+ _context .target_backend = backend
514+ return _context
511515
512516 def test_create_state_data (self , frontend , context , idp_conf ):
513517 frontend ._create_co_virtual_idp (context )
@@ -542,6 +546,17 @@ def test_create_co_virtual_idp(self, frontend, context, idp_conf):
542546 assert idp_server .config .entityid == expected_entityid
543547 assert all (sso in sso_endpoints for sso in expected_endpoints )
544548
549+ def test_create_co_virtual_idp_with_entity_id_templates (self , frontend , context ):
550+ frontend .idp_config ['entityid' ] = "{}/Saml2IDP/proxy.xml" .format (BASE_URL )
551+ expected_entity_id = "{}/Saml2IDP/proxy.xml/{}" .format (BASE_URL , self .CO )
552+ idp_server = frontend ._create_co_virtual_idp (context )
553+ assert idp_server .config .entityid == expected_entity_id
554+
555+ frontend .idp_config ['entityid' ] = "{}/<backend_name>/idp/<co_name>" .format (BASE_URL )
556+ expected_entity_id = "{}/{}/idp/{}" .format (BASE_URL , context .target_backend , self .CO )
557+ idp_server = frontend ._create_co_virtual_idp (context )
558+ assert idp_server .config .entityid == expected_entity_id
559+
545560 def test_register_endpoints (self , frontend , context ):
546561 idp_server = frontend ._create_co_virtual_idp (context )
547562 url_map = frontend .register_endpoints ([self .BACKEND ])
@@ -553,6 +568,28 @@ def test_register_endpoints(self, frontend, context):
553568 for endpoint in all_idp_endpoints :
554569 assert any (pat .match (endpoint ) for pat in compiled_regex )
555570
571+ def test_register_endpoints_throws_error_in_case_duplicate_entity_ids (self , frontend ):
572+ with pytest .raises (ValueError ):
573+ frontend .register_endpoints ([self .BACKEND , self .BACKEND_1 ])
574+
575+ def test_register_endpoints_with_metadata_endpoints (self , frontend , context ):
576+ frontend .idp_config ['entityid' ] = "{}/<backend_name>/idp/<co_name>" .format (BASE_URL )
577+ frontend .config ['entityid_endpoint' ] = True
578+ idp_server_1 = frontend ._create_co_virtual_idp (context )
579+ context_2 = self ._make_context (context , self .BACKEND_1 , self .CO )
580+ idp_server_2 = frontend ._create_co_virtual_idp (context_2 )
581+
582+ url_map = frontend .register_endpoints ([self .BACKEND , self .BACKEND_1 ])
583+ expected_idp_endpoints = [urlparse (endpoint [0 ]).path [1 :] for server in [idp_server_1 , idp_server_2 ]
584+ for endpoint in server .config ._idp_endpoints [self .KEY_SSO ]]
585+ for server in [idp_server_1 , idp_server_2 ]:
586+ expected_idp_endpoints .append (urlparse (server .config .entityid ).path [1 :])
587+
588+ compiled_regex = [re .compile (regex ) for regex , _ in url_map ]
589+
590+ for endpoint in expected_idp_endpoints :
591+ assert any (pat .match (endpoint ) for pat in compiled_regex )
592+
556593 def test_co_static_attributes (self , frontend , context , internal_response ,
557594 idp_conf , sp_conf ):
558595 # Use the frontend and context fixtures to dynamically create the
@@ -563,9 +600,8 @@ def test_co_static_attributes(self, frontend, context, internal_response,
563600 # and then use those to dynamically update the ipd_conf fixture.
564601 co_name = frontend ._get_co_name (context )
565602 backend_name = context .target_backend
566- idp_conf = frontend ._add_endpoints_to_config (idp_conf , co_name ,
567- backend_name )
568- idp_conf = frontend ._add_entity_id (idp_conf , co_name )
603+ idp_conf = frontend ._add_endpoints_to_config (idp_conf , co_name , backend_name )
604+ idp_conf = frontend ._add_entity_id (idp_conf , co_name , backend_name )
569605
570606 # Use a utility function to serialize the idp_conf IdP configuration
571607 # fixture to a string and then dynamically update the sp_conf
@@ -597,9 +633,9 @@ def test_co_static_attributes(self, frontend, context, internal_response,
597633 "name_id_policy" : NameIDPolicy (format = NAMEID_FORMAT_TRANSIENT ),
598634 "in_response_to" : None ,
599635 "destination" : sp_config .endpoint (
600- "assertion_consumer_service" ,
601- binding = BINDING_HTTP_REDIRECT
602- )[0 ],
636+ "assertion_consumer_service" ,
637+ binding = BINDING_HTTP_REDIRECT
638+ )[0 ],
603639 "sp_entity_id" : sp_conf ["entityid" ],
604640 "binding" : BINDING_HTTP_REDIRECT
605641 }
@@ -616,42 +652,42 @@ def test_co_static_attributes(self, frontend, context, internal_response,
616652class TestSubjectTypeToSamlNameIdFormat :
617653 def test_should_default_to_persistent (self ):
618654 assert (
619- subject_type_to_saml_nameid_format ("unmatched" )
620- == NAMEID_FORMAT_PERSISTENT
655+ subject_type_to_saml_nameid_format ("unmatched" )
656+ == NAMEID_FORMAT_PERSISTENT
621657 )
622658
623659 def test_should_map_persistent (self ):
624660 assert (
625- subject_type_to_saml_nameid_format (NAMEID_FORMAT_PERSISTENT )
626- == NAMEID_FORMAT_PERSISTENT
661+ subject_type_to_saml_nameid_format (NAMEID_FORMAT_PERSISTENT )
662+ == NAMEID_FORMAT_PERSISTENT
627663 )
628664
629665 def test_should_map_transient (self ):
630666 assert (
631- subject_type_to_saml_nameid_format (NAMEID_FORMAT_TRANSIENT )
632- == NAMEID_FORMAT_TRANSIENT
667+ subject_type_to_saml_nameid_format (NAMEID_FORMAT_TRANSIENT )
668+ == NAMEID_FORMAT_TRANSIENT
633669 )
634670
635671 def test_should_map_emailaddress (self ):
636672 assert (
637- subject_type_to_saml_nameid_format (NAMEID_FORMAT_EMAILADDRESS )
638- == NAMEID_FORMAT_EMAILADDRESS
673+ subject_type_to_saml_nameid_format (NAMEID_FORMAT_EMAILADDRESS )
674+ == NAMEID_FORMAT_EMAILADDRESS
639675 )
640676
641677 def test_should_map_unspecified (self ):
642678 assert (
643- subject_type_to_saml_nameid_format (NAMEID_FORMAT_UNSPECIFIED )
644- == NAMEID_FORMAT_UNSPECIFIED
679+ subject_type_to_saml_nameid_format (NAMEID_FORMAT_UNSPECIFIED )
680+ == NAMEID_FORMAT_UNSPECIFIED
645681 )
646682
647683 def test_should_map_public (self ):
648684 assert (
649- subject_type_to_saml_nameid_format ("public" )
650- == NAMEID_FORMAT_PERSISTENT
685+ subject_type_to_saml_nameid_format ("public" )
686+ == NAMEID_FORMAT_PERSISTENT
651687 )
652688
653689 def test_should_map_pairwise (self ):
654690 assert (
655- subject_type_to_saml_nameid_format ("pairwise" )
656- == NAMEID_FORMAT_TRANSIENT
691+ subject_type_to_saml_nameid_format ("pairwise" )
692+ == NAMEID_FORMAT_TRANSIENT
657693 )
0 commit comments