From fec59a0142b8998f5ab19e0aaa09940f5d1034af Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Sat, 9 Jan 2021 00:46:51 +0100 Subject: [PATCH] Adding get_idp_sso_url, get_idp_slo_url and get_idp_slo_response_url methods to the Settings class and use it in the toolkit --- README.md | 13 ++++--- src/onelogin/saml2/auth.py | 22 +++++++----- src/onelogin/saml2/logout_request.py | 2 +- src/onelogin/saml2/logout_response.py | 3 +- src/onelogin/saml2/settings.py | 34 ++++++++++++++++++ tests/src/OneLogin/saml2_tests/auth_test.py | 15 ++++++++ .../src/OneLogin/saml2_tests/settings_test.py | 35 +++++++++++++++++++ 7 files changed, 108 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index c481c2b8..6d411fd1 100644 --- a/README.md +++ b/README.md @@ -282,11 +282,13 @@ This is the ``settings.json`` file: } ] }, - // Specifies info about where and how the message MUST be - // returned to the requester, in this case our SP. + // Specifies info about where and how the message MUST be sent. "singleLogoutService": { - // URL Location where the from the IdP will be returned + // URL Location where the from the IdP will be sent (IdP-initiated logout) "url": "https:///?sls", + // URL Location where the from the IdP will sent (SP-initiated logout, reply) + // OPTIONAL: only specify if different from url parameter + //"responseUrl": "https:///?sls", // SAML protocol binding to be used when returning the // message. OneLogin Toolkit supports the HTTP-Redirect binding // only for this endpoint. @@ -327,8 +329,11 @@ This is the ``settings.json`` file: }, // SLO endpoint info of the IdP. "singleLogoutService": { - // URL Location of the IdP where SLO Request will be sent. + // URL Location where the from the IdP will be sent (IdP-initiated logout) "url": "https://app.onelogin.com/trust/saml2/http-redirect/slo/", + // URL Location where the from the IdP will sent (SP-initiated logout, reply) + // OPTIONAL: only specify if different from url parameter + "responseUrl": "https://app.onelogin.com/trust/saml2/http-redirect/slo_return/", // SAML protocol binding to be used when returning the // message. OneLogin Toolkit supports the HTTP-Redirect binding // only for this endpoint. diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index 188e289e..707c16a4 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -446,26 +446,30 @@ def logout(self, return_to=None, name_id=None, session_index=None, nq=None, name def get_sso_url(self): """ - Gets the SSO URL. + Gets the IdP SSO URL. :returns: An URL, the SSO endpoint of the IdP :rtype: string """ - idp_data = self.__settings.get_idp_data() - return idp_data['singleSignOnService']['url'] + return self.__settings.get_idp_sso_url() def get_slo_url(self): """ - Gets the SLO URL. + Gets the IdP SLO URL. :returns: An URL, the SLO endpoint of the IdP :rtype: string """ - url = None - idp_data = self.__settings.get_idp_data() - if 'singleLogoutService' in idp_data.keys() and 'url' in idp_data['singleLogoutService']: - url = idp_data['singleLogoutService']['url'] - return url + return self.__settings.get_idp_slo_url() + + def get_slo_response_url(self): + """ + Gets the SLO return URL for IdP-initiated logout. + + :returns: an URL, the SLO return endpoint of the IdP + :rtype: string + """ + return self.__settings.get_idp_slo_response_url() def build_request_signature(self, saml_request, relay_state, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1): """ diff --git a/src/onelogin/saml2/logout_request.py b/src/onelogin/saml2/logout_request.py index 1cb75efd..826d96fe 100644 --- a/src/onelogin/saml2/logout_request.py +++ b/src/onelogin/saml2/logout_request.py @@ -125,7 +125,7 @@ def __init__(self, settings, request=None, name_id=None, session_index=None, nq= { 'id': uid, 'issue_instant': issue_instant, - 'single_logout_url': idp_data['singleLogoutService']['url'], + 'single_logout_url': self.__settings.get_idp_slo_url(), 'entity_id': sp_data['entityId'], 'name_id': name_id_obj, 'session_index': session_index_str, diff --git a/src/onelogin/saml2/logout_response.py b/src/onelogin/saml2/logout_response.py index 6d326caf..0c1cd178 100644 --- a/src/onelogin/saml2/logout_response.py +++ b/src/onelogin/saml2/logout_response.py @@ -208,7 +208,6 @@ def build(self, in_response_to): :type in_response_to: string """ sp_data = self.__settings.get_sp_data() - idp_data = self.__settings.get_idp_data() uid = OneLogin_Saml2_Utils.generate_unique_id() issue_instant = OneLogin_Saml2_Utils.parse_time_to_SAML(OneLogin_Saml2_Utils.now()) @@ -229,7 +228,7 @@ def build(self, in_response_to): { 'id': uid, 'issue_instant': issue_instant, - 'destination': idp_data['singleLogoutService']['url'], + 'destination': self.__settings.get_idp_slo_response_url(), 'in_response_to': in_response_to, 'entity_id': sp_data['entityId'], } diff --git a/src/onelogin/saml2/settings.py b/src/onelogin/saml2/settings.py index a9e863fb..357058be 100644 --- a/src/onelogin/saml2/settings.py +++ b/src/onelogin/saml2/settings.py @@ -259,6 +259,8 @@ def __add_default_values(self): self.__sp.setdefault('singleLogoutService', {}) self.__sp['singleLogoutService'].setdefault('binding', OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT) + self.__idp.setdefault('singleLogoutService', {}) + # Related to nameID self.__sp.setdefault('NameIDFormat', OneLogin_Saml2_Constants.NAMEID_UNSPECIFIED) self.__security.setdefault('nameIdEncrypted', False) @@ -506,6 +508,38 @@ def check_sp_certs(self): cert = self.get_sp_cert() return key is not None and cert is not None + def get_idp_sso_url(self): + """ + Gets the IdP SSO URL. + + :returns: An URL, the SSO endpoint of the IdP + :rtype: string + """ + idp_data = self.get_idp_data() + return idp_data['singleSignOnService']['url'] + + def get_idp_slo_url(self): + """ + Gets the IdP SLO URL. + + :returns: An URL, the SLO endpoint of the IdP + :rtype: string + """ + idp_data = self.get_idp_data() + if 'url' in idp_data['singleLogoutService']: + return idp_data['singleLogoutService']['url'] + + def get_idp_slo_response_url(self): + """ + Gets the IdP SLO return URL for IdP-initiated logout. + + :returns: an URL, the SLO return endpoint of the IdP + :rtype: string + """ + idp_data = self.get_idp_data() + if 'url' in idp_data['singleLogoutService']: + return idp_data['singleLogoutService'].get('responseUrl', self.get_idp_slo_url()) + def get_sp_key(self): """ Returns the x509 private key of the SP. diff --git a/tests/src/OneLogin/saml2_tests/auth_test.py b/tests/src/OneLogin/saml2_tests/auth_test.py index 3a57c438..6051c72e 100644 --- a/tests/src/OneLogin/saml2_tests/auth_test.py +++ b/tests/src/OneLogin/saml2_tests/auth_test.py @@ -78,6 +78,21 @@ def testGetSLOurl(self): slo_url = settings_info['idp']['singleLogoutService']['url'] self.assertEqual(auth.get_slo_url(), slo_url) + def testGetSLOresponseUrl(self): + """ + Tests the get_slo_response_url method of the OneLogin_Saml2_Auth class + """ + settings_info = self.loadSettingsJSON() + settings_info['idp']['singleLogoutService']['responseUrl'] = "http://idp.example.com/SingleLogoutReturn.php" + auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) + slo_url = settings_info['idp']['singleLogoutService']['responseUrl'] + self.assertEqual(auth.get_slo_response_url(), slo_url) + # test that the function falls back to the url setting if responseUrl is not set + settings_info['idp']['singleLogoutService'].pop('responseUrl') + auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) + slo_url = settings_info['idp']['singleLogoutService']['url'] + self.assertEqual(auth.get_slo_response_url(), slo_url) + def testGetSessionIndex(self): """ Tests the get_session_index method of the OneLogin_Saml2_Auth class diff --git a/tests/src/OneLogin/saml2_tests/settings_test.py b/tests/src/OneLogin/saml2_tests/settings_test.py index c4a9d7bc..9027c425 100644 --- a/tests/src/OneLogin/saml2_tests/settings_test.py +++ b/tests/src/OneLogin/saml2_tests/settings_test.py @@ -142,6 +142,41 @@ def testGetSchemasPath(self): base = settings.get_base_path() self.assertEqual(join(base, 'lib', 'schemas') + sep, settings.get_schemas_path()) + def testGetIdPSSOurl(self): + """ + Tests the get_idp_sso_url method of the OneLogin_Saml2_Settings class + """ + settings_info = self.loadSettingsJSON() + settings = OneLogin_Saml2_Settings(settings_info) + + sso_url = settings_info['idp']['singleSignOnService']['url'] + self.assertEqual(settings.get_idp_sso_url(), sso_url) + + def testGetIdPSLOurl(self): + """ + Tests the get_idp_slo_url method of the OneLogin_Saml2_Settings class + """ + settings_info = self.loadSettingsJSON() + settings = OneLogin_Saml2_Settings(settings_info) + + slo_url = settings_info['idp']['singleLogoutService']['url'] + self.assertEqual(settings.get_idp_slo_url(), slo_url) + + def testGetIdPSLOresponseUrl(self): + """ + Tests the get_idp_slo_response_url method of the OneLogin_Saml2_Settings class + """ + settings_info = self.loadSettingsJSON() + settings_info['idp']['singleLogoutService']['responseUrl'] = "http://idp.example.com/SingleLogoutReturn.php" + settings = OneLogin_Saml2_Settings(settings_info) + slo_url = settings_info['idp']['singleLogoutService']['responseUrl'] + self.assertEqual(settings.get_idp_slo_response_url(), slo_url) + # test that the function falls back to the url setting if responseUrl is not set + settings_info['idp']['singleLogoutService'].pop('responseUrl') + settings = OneLogin_Saml2_Settings(settings_info) + slo_url = settings_info['idp']['singleLogoutService']['url'] + self.assertEqual(settings.get_idp_slo_response_url(), slo_url) + def testGetSPCert(self): """ Tests the get_sp_cert method of the OneLogin_Saml2_Settings