diff --git a/src/satosa/backends/openid_connect.py b/src/satosa/backends/openid_connect.py index 435b1f857..0faa5cb55 100644 --- a/src/satosa/backends/openid_connect.py +++ b/src/satosa/backends/openid_connect.py @@ -83,6 +83,7 @@ def start_auth(self, context, request_info): args.update(self.config["client"]["auth_req_params"]) auth_req = self.client.construct_AuthorizationRequest(request_args=args) login_url = auth_req.request(self.client.authorization_endpoint) + context.state['target_backend'] = self.name return Redirect(login_url) def register_endpoints(self): diff --git a/src/satosa/backends/saml2.py b/src/satosa/backends/saml2.py index b6b8b7285..aaa3d29d4 100644 --- a/src/satosa/backends/saml2.py +++ b/src/satosa/backends/saml2.py @@ -189,6 +189,7 @@ def authn_request(self, context, entity_id): self.outstanding_queries[req_id] = req context.state[self.name] = {"relay_state": relay_state} + context.state['target_backend'] = self.name return make_saml_response(binding, ht_args) def authn_response(self, context, binding): @@ -445,3 +446,34 @@ def to_dict(self): _dict['name_id'] = None return _dict + +class SAMLMirrorBackend(SAMLBackend): + """ + A saml2 backend module (acting as a SP) that gives out requesting SP dependant entityID in authnRequest. + """ + def start_auth(self, context, internal_req): + """ + See super class method satosa.backends.base.BackendModule#start_auth + :type context: satosa.context.Context + :type internal_req: satosa.internal_data.InternalRequest + :rtype: satosa.response.Response + """ + + satosa_logging(logger, logging.DEBUG, "Target Frontend: %s" % context.target_frontend, context.state) + satosa_logging(logger, logging.DEBUG, "SP entityid: %s" % internal_req.requester, context.state) + satosa_logging(logger, logging.DEBUG, "Internal SP: %s" % self.sp.config.entityid, context.state) + + requester_desc = self._urlenc(internal_req.requester) + self.sp.config.entityid = self.config["sp_config"]["entityid"] + "/" + context.target_frontend + "/" + requester_desc + + satosa_logging(logger, logging.DEBUG, "New Internal SP: %s" % self.sp.config.entityid, context.state) + + return super().start_auth(context, internal_req) + + def _urlenc(self, s): + """ + Create short unique url-safe hash of string + """ + + desc = urlsafe_b64encode(s.encode('utf-8')).decode('utf-8') + return desc diff --git a/src/satosa/frontends/saml2.py b/src/satosa/frontends/saml2.py index 25f43c15e..52b925088 100644 --- a/src/satosa/frontends/saml2.py +++ b/src/satosa/frontends/saml2.py @@ -6,6 +6,7 @@ import json import logging from urllib.parse import urlparse +from base64 import urlsafe_b64encode, urlsafe_b64decode from saml2 import SAMLError, xmldsig from saml2.config import IdPConfig @@ -25,6 +26,8 @@ from ..saml_util import make_saml_response import satosa.util as util +from ..metadata_creation.description import (MetadataDescription, OrganizationDesc, + ContactPersonDesc, UIInfoDesc) logger = logging.getLogger(__name__) @@ -461,6 +464,65 @@ def _get_sp_display_name(self, idp, entity_id): return None + def get_metadata_desc(self): + """ + See super class satosa.frontends.frontend_base.FrontendModule#get_metadata_desc + :rtype: satosa.metadata_creation.description.MetadataDescription + """ + entity_descriptions = [] + + sp_entities = self.idp.metadata.with_descriptor("spsso") + for entity_id, entity in sp_entities.items(): + description = MetadataDescription(urlsafe_b64encode(entity_id.encode("utf-8")).decode("utf-8")) + + # Add organization info + try: + organization_info = entity["organization"] + except KeyError: + pass + else: + organization = OrganizationDesc() + for name_info in organization_info.get("organization_name", []): + organization.add_name(name_info["text"], name_info["lang"]) + for display_name_info in organization_info.get("organization_display_name", []): + organization.add_display_name(display_name_info["text"], display_name_info["lang"]) + for url_info in organization_info.get("organization_url", []): + organization.add_url(url_info["text"], url_info["lang"]) + description.organization = organization + + # Add contact person info + try: + contact_persons = entity["contact_person"] + except KeyError: + pass + else: + for person in contact_persons: + person_desc = ContactPersonDesc() + person_desc.contact_type = person.get("contact_type") + for address in person.get('email_address', []): + person_desc.add_email_address(address["text"]) + if "given_name" in person: + person_desc.given_name = person["given_name"]["text"] + if "sur_name" in person: + person_desc.sur_name = person["sur_name"]["text"] + + description.add_contact_person(person_desc) + + # Add UI info + ui_info = self.idp.metadata.extension(entity_id, "spsso_descriptor", "{}&UIInfo".format(UI_NAMESPACE)) + if ui_info: + ui_info = ui_info[0] + ui_info_desc = UIInfoDesc() + for desc in ui_info.get("description", []): + ui_info_desc.add_description(desc["text"], desc["lang"]) + for name in ui_info.get("display_name", []): + ui_info_desc.add_display_name(name["text"], name["lang"]) + for logo in ui_info.get("logo", []): + ui_info_desc.add_logo(logo["text"], logo["width"], logo["height"], logo.get("lang")) + description.ui_info = ui_info_desc + + entity_descriptions.append(description) + return entity_descriptions class SAMLMirrorFrontend(SAMLFrontend): """ @@ -521,7 +583,7 @@ def _load_idp_dynamic_entity_id(self, state): """ # Change the idp entity id dynamically idp_config_file = copy.deepcopy(self.idp_config) - idp_config_file["entityid"] = "{}/{}".format(self.idp_config["entityid"], state[self.name]["target_entity_id"]) + idp_config_file["entityid"] = "{}/{}/{}".format(self.idp_config["entityid"], state["target_backend"], state[self.name]["target_entity_id"]) idp_config = IdPConfig().load(idp_config_file, metadata_construction=False) return Server(config=idp_config) diff --git a/src/satosa/metadata_creation/description.py b/src/satosa/metadata_creation/description.py index ac243c278..7a8907a8e 100644 --- a/src/satosa/metadata_creation/description.py +++ b/src/satosa/metadata_creation/description.py @@ -110,7 +110,7 @@ def to_dict(self): ui_info["display_name"] = self._display_name if self._logos: ui_info["logo"] = self._logos - return {"service": {"idp": {"ui_info": ui_info}}} if ui_info else {} + return {"service": {"ui_info": ui_info}} if ui_info else {} class OrganizationDesc(object): diff --git a/src/satosa/metadata_creation/saml_metadata.py b/src/satosa/metadata_creation/saml_metadata.py index 99dd1a45d..8e29e5731 100644 --- a/src/satosa/metadata_creation/saml_metadata.py +++ b/src/satosa/metadata_creation/saml_metadata.py @@ -8,30 +8,49 @@ from saml2.validate import valid_instance from ..backends.saml2 import SAMLBackend +from ..backends.saml2 import SAMLMirrorBackend +from ..backends.openid_connect import OpenIDConnectBackend from ..frontends.saml2 import SAMLFrontend from ..frontends.saml2 import SAMLMirrorFrontend +from ..frontends.openid_connect import OpenIDConnectFrontend from ..plugin_loader import load_frontends, load_backends +from ..metadata_creation.description import MetadataDescription +from base64 import urlsafe_b64encode, urlsafe_b64decode + logger = logging.getLogger(__name__) +def urlenc(s): + enc = urlsafe_b64encode(s.encode('utf-8')).decode('utf-8') + return enc def _create_entity_descriptor(entity_config): cnf = Config().load(copy.deepcopy(entity_config), metadata_construction=True) return entity_descriptor(cnf) +def _create_mirrored_sp_entity_config(backend_instance, target_metadata_info, frontend_name): + def _merge_dicts(a, b): + for key, value in b.items(): + #if key in ["organization", "contact_person"]: + # avoid copying contact info from the target provider + #continue + if key in a and isinstance(value, dict): + a[key] = _merge_dicts(a[key], b[key]) + else: + a[key] = value -def _create_backend_metadata(backend_modules): - backend_metadata = {} - - for plugin_module in backend_modules: - if isinstance(plugin_module, SAMLBackend): - logger.info("Generating SAML backend '%s' metadata", plugin_module.name) - backend_metadata[plugin_module.name] = [_create_entity_descriptor(plugin_module.config["sp_config"])] - - return backend_metadata + return a + if "service" in target_metadata_info: + if not "sp" in target_metadata_info["service"]: + target_metadata_info["service"]["sp"] = dict() + target_metadata_info["service"]["sp"]["ui_info"] = target_metadata_info["service"].pop("ui_info") + merged_conf = _merge_dicts(copy.deepcopy(backend_instance.config["sp_config"]), target_metadata_info) + proxy_entity_id = backend_instance.config["sp_config"]["entityid"] + merged_conf["entityid"] = "{}/{}/{}".format(proxy_entity_id, frontend_name, target_metadata_info["entityid"]) + return merged_conf -def _create_mirrored_entity_config(frontend_instance, target_metadata_info, backend_name): +def _create_mirrored_idp_entity_config(frontend_instance, target_metadata_info, backend_name): def _merge_dicts(a, b): for key, value in b.items(): if key in ["organization", "contact_person"]: @@ -44,14 +63,49 @@ def _merge_dicts(a, b): return a + if "service" in target_metadata_info: + if not "idp" in target_metadata_info["service"]: + target_metadata_info["service"]["idp"] = dict() + target_metadata_info["service"]["idp"]["ui_info"] = target_metadata_info["service"].pop("ui_info") merged_conf = _merge_dicts(copy.deepcopy(frontend_instance.config["idp_config"]), target_metadata_info) full_config = frontend_instance._load_endpoints_to_config(backend_name, target_metadata_info["entityid"], config=merged_conf) proxy_entity_id = frontend_instance.config["idp_config"]["entityid"] - full_config["entityid"] = "{}/{}".format(proxy_entity_id, target_metadata_info["entityid"]) + full_config["entityid"] = "{}/{}/{}".format(proxy_entity_id, backend_name, target_metadata_info["entityid"]) return full_config +def _create_backend_metadata(backend_modules, frontend_modules): + backend_metadata = defaultdict(list) + + for backend in backend_modules: + if isinstance(backend, SAMLMirrorBackend): + backend_entityid = backend.config["sp_config"]["entityid"] + frontend_metadata = defaultdict(list) + for frontend in frontend_modules: + if isinstance(frontend, SAMLFrontend): + logger.info("Creating SAML backend Mirror metadata for '{}' and frontend '{}'".format(backend.name, frontend.name)) + frontend.register_endpoints([backend.name]) + meta_desc = frontend.get_metadata_desc() + for desc in meta_desc: + logger.info("Backend %s EntityID %s" % (backend.name, urlsafe_b64decode(desc.to_dict()["entityid"]).decode("utf-8"))) + mirrored_sp_entity_config = _create_mirrored_sp_entity_config(backend, desc.to_dict(), frontend.name) + entity_desc = _create_entity_descriptor(mirrored_sp_entity_config) + backend_metadata[backend.name].append(entity_desc) + elif isinstance(frontend, OpenIDConnectFrontend): + logger.info("Creating SAML backend Mirror metadata for '{}' and OIDC frontend '{}'".format(backend.name, frontend.name)) + frontend.register_endpoints([backend.name]) + for client_id, client in frontend.provider.clients.items(): + logger.info("OIDC client_id %s %s" % (client_id, client.get("client_name"))) + backend.config["sp_config"]["entityid"] = backend_entityid + "/" + frontend.name + "/" + urlenc(client_id) + backend_metadata[backend.name].append(_create_entity_descriptor(backend.config["sp_config"])) + elif isinstance(backend, SAMLBackend): + logger.info("Creating SAML backend '%s' metadata", backend.name) + logger.info("Backend %s EntityID %s" % (backend.name, backend.config["sp_config"]["entityid"])) + backend_metadata[backend.name].append(_create_entity_descriptor(backend.config["sp_config"])) + + return backend_metadata + def _create_frontend_metadata(frontend_modules, backend_modules): frontend_metadata = defaultdict(list) @@ -59,15 +113,24 @@ def _create_frontend_metadata(frontend_modules, backend_modules): for frontend in frontend_modules: if isinstance(frontend, SAMLMirrorFrontend): for backend in backend_modules: - logger.info("Creating metadata for frontend '%s' and backend '%s'".format(frontend.name, backend.name)) - meta_desc = backend.get_metadata_desc() - for desc in meta_desc: - entity_desc = _create_entity_descriptor( - _create_mirrored_entity_config(frontend, desc.to_dict(), backend.name)) - frontend_metadata[frontend.name].append(entity_desc) + if isinstance(backend, SAMLBackend): + logger.info("Creating SAML Mirrored metadata for frontend '{}' and backend '{}'".format(frontend.name, backend.name)) + meta_desc = backend.get_metadata_desc() + for desc in meta_desc: + mirrored_idp_entity_config = _create_mirrored_idp_entity_config(frontend, desc.to_dict(), backend.name) + entity_desc = _create_entity_descriptor(mirrored_idp_entity_config) + frontend_metadata[frontend.name].append(entity_desc) + if isinstance(backend, OpenIDConnectBackend): + logger.info("Creating SAML Mirrored metadata for frontend '{}' and OIDC backend '{}'".format(frontend.name, backend.name)) + meta_desc = backend.get_metadata_desc() + for desc in meta_desc: + mirrored_idp_entity_config = _create_mirrored_idp_entity_config(frontend, desc.to_dict(), backend.name) + entity_desc = _create_entity_descriptor(mirrored_idp_entity_config) + frontend_metadata[frontend.name].append(entity_desc) elif isinstance(frontend, SAMLFrontend): + logger.info("Creating SAML frontend '%s' metadata" % frontend.name) frontend.register_endpoints([backend.name for backend in backend_modules]) - entity_desc = _create_entity_descriptor(frontend.idp_config) + entity_desc = _create_entity_descriptor(frontend.config["idp_config"]) frontend_metadata[frontend.name].append(entity_desc) return frontend_metadata @@ -88,7 +151,7 @@ def create_entity_descriptors(satosa_config): logger.info("Loaded frontend plugins: {}".format([frontend.name for frontend in frontend_modules])) logger.info("Loaded backend plugins: {}".format([backend.name for backend in backend_modules])) - backend_metadata = _create_backend_metadata(backend_modules) + backend_metadata = _create_backend_metadata(backend_modules, frontend_modules) frontend_metadata = _create_frontend_metadata(frontend_modules, backend_modules) return frontend_metadata, backend_metadata @@ -132,4 +195,4 @@ def create_signed_entity_descriptor(entity_descriptor, security_context, valid_f if not valid_instance(entity_desc): raise ValueError("Could not construct valid EntityDescriptor tag") - return xmldoc \ No newline at end of file + return xmldoc diff --git a/src/satosa/scripts/satosa_saml_metadata.py b/src/satosa/scripts/satosa_saml_metadata.py index 0f31e61f0..9fbc3a05c 100644 --- a/src/satosa/scripts/satosa_saml_metadata.py +++ b/src/satosa/scripts/satosa_saml_metadata.py @@ -1,4 +1,6 @@ import os +import logging +import sys import click from saml2.config import Config @@ -9,6 +11,7 @@ from ..metadata_creation.saml_metadata import create_signed_entity_descriptor from ..satosa_config import SATOSAConfig +logger = logging.getLogger(__name__) def _get_security_context(key, cert): conf = Config() @@ -28,9 +31,10 @@ def _create_split_entity_descriptors(entities, secc, valid): def _create_merged_entities_descriptors(entities, secc, valid, name): output = [] - frontend_entity_descriptors = [e for sublist in entities.values() for e in sublist] - for frontend in frontend_entity_descriptors: - output.append((create_signed_entity_descriptor(frontend, secc, valid), name)) + entity_descriptors = [e for sublist in entities.values() for e in sublist] + for entity in entity_descriptors: + print("entityID: {}".format(entity.entity_id)) + output.append((create_signed_entities_descriptor(entity_descriptors, secc, valid), name)) return output @@ -40,6 +44,14 @@ def create_and_write_saml_metadata(proxy_conf, key, cert, dir, valid, split_fron """ Generates SAML metadata for the given PROXY_CONF, signed with the given KEY and associated CERT. """ + + stderr_handler = logging.StreamHandler(sys.stderr) + stderr_handler.setLevel(logging.INFO) + + root_logger = logging.getLogger("") + root_logger.addHandler(stderr_handler) + root_logger.setLevel(logging.INFO) + satosa_config = SATOSAConfig(proxy_conf) secc = _get_security_context(key, cert) frontend_entities, backend_entities = create_entity_descriptors(satosa_config) diff --git a/tests/conftest.py b/tests/conftest.py index dd6cca858..8f907019b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -51,12 +51,30 @@ def sp_conf(cert_and_key): }, "want_response_signed": False, "allow_unsolicited": True, - "name_id_format": [NAMEID_FORMAT_PERSISTENT] + "name_id_format": [NAMEID_FORMAT_PERSISTENT], + "ui_info": { + "display_name": [{"text": "SATOSA Test SP", "lang": "en"}], + "description": [{"text": "Test SP for SATOSA unit tests.", "lang": "en"}], + "logo": [{"text": "https://sp.example.com/static/logo.png", "width": "120", "height": "60", + "lang": "en"}], + }, }, }, "cert_file": cert_and_key[0], "key_file": cert_and_key[1], "metadata": {"inline": []}, + "organization": { + "name": [["Test SP Org.", "en"]], + "display_name": [["Test SP", "en"]], + "url": [["https://sp.example.com/about", "en"]] + }, + "contact_person": [ + {"given_name": "Test SP", "sur_name": "Support", "email_address": ["help@sp.example.com"], + "contact_type": "support" + }, + {"given_name": "Test SP", "sur_name": "Tech support", + "email_address": ["tech@sp.example.com"], "contact_type": "technical"} + ] } return spconfig diff --git a/tests/satosa/backends/test_saml2.py b/tests/satosa/backends/test_saml2.py index 9e114589c..86efc12f0 100644 --- a/tests/satosa/backends/test_saml2.py +++ b/tests/satosa/backends/test_saml2.py @@ -15,7 +15,7 @@ from saml2.config import IdPConfig, SPConfig from saml2.s_utils import deflate_and_base64_encode -from satosa.backends.saml2 import SAMLBackend +from satosa.backends.saml2 import SAMLBackend, SAMLMirrorBackend from satosa.context import Context from satosa.internal_data import InternalRequest from tests.users import USERS @@ -260,7 +260,7 @@ def test_get_metadata_desc(self, sp_conf, idp_conf): assert idp_desc["organization"]["url"][0] == tuple(idp_conf["organization"]["url"][0]) expected_ui_info = idp_conf["service"]["idp"]["ui_info"] - ui_info = idp_desc["service"]["idp"]["ui_info"] + ui_info = idp_desc["service"]["ui_info"] assert ui_info["display_name"] == expected_ui_info["display_name"] assert ui_info["description"] == expected_ui_info["description"] assert ui_info["logo"] == expected_ui_info["logo"] @@ -287,7 +287,37 @@ def test_get_metadata_desc_with_logo_without_lang(self, sp_conf, idp_conf): assert idp_desc["organization"]["url"][0] == tuple(idp_conf["organization"]["url"][0]) expected_ui_info = idp_conf["service"]["idp"]["ui_info"] - ui_info = idp_desc["service"]["idp"]["ui_info"] + ui_info = idp_desc["service"]["ui_info"] assert ui_info["display_name"] == expected_ui_info["display_name"] assert ui_info["description"] == expected_ui_info["description"] assert ui_info["logo"] == expected_ui_info["logo"] + +class TestSAMLMirrorBackend: + def setup_test_config(self, sp_conf, idp_conf): + idp_metadata_str = create_metadata_from_config_dict(idp_conf) + sp_conf["metadata"]["inline"].append(idp_metadata_str) + idp2_config = idp_conf.copy() + idp2_config["entityid"] = "just_an_extra_idp" + idp_metadata_str2 = create_metadata_from_config_dict(idp2_config) + sp_conf["metadata"]["inline"].append(idp_metadata_str2) + + sp_metadata_str = create_metadata_from_config_dict(sp_conf) + idp_conf["metadata"]["inline"] = [sp_metadata_str] + + @pytest.fixture(autouse=True) + def create_backend(self, sp_conf, idp_conf): + self.setup_test_config(sp_conf, idp_conf) + self.samlbackend = SAMLMirrorBackend(Mock(), INTERNAL_ATTRIBUTES, {"sp_config": sp_conf, + "disco_srv": DISCOSRV_URL}, + "base_url", + "samlbackend") + + def test_dynamic_entityid(self, context, sp_conf, idp_conf): + test_state_key = "test_state_key_456afgrh" + context.state[test_state_key] = "my_state" + context.target_frontend = "target_frontend" + requester = "requester_id" + + self.samlbackend.start_auth(context, InternalRequest(None, requester)) + assert self.samlbackend.sp.config.entityid == sp_conf['entityid'] + '/' + context.target_frontend + '/' + urlsafe_b64encode(requester.encode('utf-8')).decode('utf-8') + diff --git a/tests/satosa/frontends/test_saml2.py b/tests/satosa/frontends/test_saml2.py index a7e223585..48863c6fa 100644 --- a/tests/satosa/frontends/test_saml2.py +++ b/tests/satosa/frontends/test_saml2.py @@ -3,6 +3,7 @@ """ import itertools import re +from base64 import urlsafe_b64encode from collections import Counter from urllib.parse import urlparse, parse_qs @@ -284,6 +285,7 @@ def test_sp_metadata_including_uiinfo_without_display_name(self, context, idp_co assert samlfrontend._get_sp_display_name(samlfrontend.idp, sp_conf["entityid"]) is None def test_sp_metadata_without_uiinfo(self, context, idp_conf, sp_conf): + sp_conf["service"]["sp"].pop("ui_info") samlfrontend = self.setup_for_authn_req(context, idp_conf, sp_conf) assert samlfrontend._get_sp_display_name(samlfrontend.idp, sp_conf["entityid"]) is None @@ -318,6 +320,57 @@ def test_custom_attribute_release_with_less_attributes_than_entity_category(self resp = self.get_auth_response(samlfrontend, context, internal_response, sp_conf, idp_metadata_str) assert len(resp.ava.keys()) == 0 + def test_get_metadata_desc(self, context, idp_conf, sp_conf): + idp_conf["metadata"]["inline"] = [create_metadata_from_config_dict(sp_conf)] + # instantiate new frontend, with a single backing SP + samlfrontend = SAMLFrontend(None, INTERNAL_ATTRIBUTES, {"idp_config": idp_conf, "endpoints": ENDPOINTS}, "base_url", "saml_frontend") + samlfrontend.register_endpoints(["saml"]) + entity_descriptions = samlfrontend.get_metadata_desc() + + assert len(entity_descriptions) == 1 + + sp_desc = entity_descriptions[0].to_dict() + + assert sp_desc["entityid"] == urlsafe_b64encode(sp_conf["entityid"].encode("utf-8")).decode("utf-8") + assert sp_desc["contact_person"] == sp_conf["contact_person"] + + assert sp_desc["organization"]["name"][0] == tuple(sp_conf["organization"]["name"][0]) + assert sp_desc["organization"]["display_name"][0] == tuple(sp_conf["organization"]["display_name"][0]) + assert sp_desc["organization"]["url"][0] == tuple(sp_conf["organization"]["url"][0]) + + expected_ui_info = sp_conf["service"]["sp"]["ui_info"] + ui_info = sp_desc["service"]["ui_info"] + assert ui_info["display_name"] == expected_ui_info["display_name"] + assert ui_info["description"] == expected_ui_info["description"] + assert ui_info["logo"] == expected_ui_info["logo"] + + def test_get_metadata_desc_with_logo_without_lang(self, context, idp_conf, sp_conf): + # add logo without 'lang' + sp_conf["service"]["sp"]["ui_info"]["logo"] = [{"text": "https://idp.example.com/static/logo.png", + "width": "120", "height": "60"}] + + idp_conf["metadata"]["inline"] = [create_metadata_from_config_dict(sp_conf)] + # instantiate new backend, with a single backing IdP + samlfrontend = SAMLFrontend(None, INTERNAL_ATTRIBUTES, {"idp_config": idp_conf, "endpoints": ENDPOINTS}, "base_url", "saml_frontend") + samlfrontend.register_endpoints(["saml"]) + entity_descriptions = samlfrontend.get_metadata_desc() + + assert len(entity_descriptions) == 1 + + sp_desc = entity_descriptions[0].to_dict() + + assert sp_desc["entityid"] == urlsafe_b64encode(sp_conf["entityid"].encode("utf-8")).decode("utf-8") + assert sp_desc["contact_person"] == sp_conf["contact_person"] + + assert sp_desc["organization"]["name"][0] == tuple(sp_conf["organization"]["name"][0]) + assert sp_desc["organization"]["display_name"][0] == tuple(sp_conf["organization"]["display_name"][0]) + assert sp_desc["organization"]["url"][0] == tuple(sp_conf["organization"]["url"][0]) + + expected_ui_info = sp_conf["service"]["sp"]["ui_info"] + ui_info = sp_desc["service"]["ui_info"] + assert ui_info["display_name"] == expected_ui_info["display_name"] + assert ui_info["description"] == expected_ui_info["description"] + assert ui_info["logo"] == expected_ui_info["logo"] class TestSAMLMirrorFrontend: BACKEND = "test_backend" @@ -352,8 +405,9 @@ def test_load_idp_dynamic_endpoints(self, context): def test_load_idp_dynamic_entity_id(self, idp_conf): state = State() state[self.frontend.name] = {"target_entity_id": self.TARGET_ENTITY_ID} + state['target_backend'] = self.BACKEND idp = self.frontend._load_idp_dynamic_entity_id(state) - assert idp.config.entityid == "{}/{}".format(idp_conf["entityid"], self.TARGET_ENTITY_ID) + assert idp.config.entityid == "{}/{}/{}".format(idp_conf["entityid"], self.BACKEND, self.TARGET_ENTITY_ID) class TestSamlNameIdFormatToHashType: diff --git a/tests/satosa/metadata_creation/test_description.py b/tests/satosa/metadata_creation/test_description.py index 8b73ec923..478749a1f 100644 --- a/tests/satosa/metadata_creation/test_description.py +++ b/tests/satosa/metadata_creation/test_description.py @@ -28,7 +28,7 @@ def test_to_dict(self): desc.add_logo("logo.jpg", 80, 80, "en") serialized = desc.to_dict() - ui_info = serialized["service"]["idp"]["ui_info"] + ui_info = serialized["service"]["ui_info"] assert ui_info["description"] == [{"text": "test", "lang": "en"}] assert ui_info["display_name"] == [{"text": "my company", "lang": "en"}] assert ui_info["logo"] == [{"text": "logo.jpg", "width": 80, "height": 80, "lang": "en"}] @@ -38,7 +38,7 @@ def test_to_dict_for_logo_without_lang(self): desc.add_logo("logo.jpg", 80, 80, None) serialized = desc.to_dict() - ui_info = serialized["service"]["idp"]["ui_info"] + ui_info = serialized["service"]["ui_info"] assert ui_info["logo"] == [{"text": "logo.jpg", "width": 80, "height": 80}] def test_to_dict_with_empty(self): @@ -91,7 +91,7 @@ def test_to_dict(self): assert serialized["entityid"] == "my_entity" assert serialized["organization"] assert serialized["contact_person"] - assert serialized["service"]["idp"]["ui_info"] + assert serialized["service"]["ui_info"] def test_set_organization_rejects_bad_input(self): desc = MetadataDescription("my_entity") diff --git a/tests/satosa/metadata_creation/test_saml_metadata.py b/tests/satosa/metadata_creation/test_saml_metadata.py index 49cff97a4..5c27a565c 100644 --- a/tests/satosa/metadata_creation/test_saml_metadata.py +++ b/tests/satosa/metadata_creation/test_saml_metadata.py @@ -30,14 +30,18 @@ def assert_single_sign_on_endpoints_for_saml_frontend(self, entity_descriptor, s def assert_single_sign_on_endpoints_for_saml_mirror_frontend(self, entity_descriptors, encoded_target_entity_id, saml_mirror_frontend_config, backend_names): - expected_entity_id = saml_mirror_frontend_config["config"]["idp_config"][ - "entityid"] + "/" + encoded_target_entity_id - metadata = InMemoryMetaData(None, None) - for ed in entity_descriptors: - metadata.parse(str(ed)) - sso = metadata.service(expected_entity_id, "idpsso_descriptor", "single_sign_on_service") - for backend_name in backend_names: + expected_entity_id = saml_mirror_frontend_config["config"]["idp_config"][ + "entityid"] + "/" + backend_name + "/" + encoded_target_entity_id + metadata = InMemoryMetaData(None, None) + for ed in entity_descriptors: + print("ed: {}".format(ed)) + metadata.parse(str(ed)) + sso = metadata.service(expected_entity_id, "idpsso_descriptor", "single_sign_on_service") + + print("expected_entity_id: {}".format(expected_entity_id)) + print("sso: {}".format(sso)) + for binding, path in saml_mirror_frontend_config["config"]["endpoints"]["single_sign_on_service"].items(): sso_urls_for_binding = [endpoint["location"] for endpoint in sso[binding]] expected_url = "{}/{}/{}/{}".format(BASE_URL, backend_name, encoded_target_entity_id, path)