Beispiel #1
0
    def testGetSelfURLNoQuery(self):
        """
        Tests the get_self_url_no_query method of the OneLogin_Saml2_Utils
        """
        request_data = {
            'http_host': 'example.com',
            'script_name': '/index.html'
        }
        url = OneLogin_Saml2_Utils.get_self_url_host(request_data) + request_data['script_name']
        self.assertEqual(url, OneLogin_Saml2_Utils.get_self_url_no_query(request_data))

        request_data['path_info'] = '/test'
        self.assertEqual(url + '/test', OneLogin_Saml2_Utils.get_self_url_no_query(request_data))
Beispiel #2
0
    def testGetSelfURLNoQuery(self):
        """
        Tests the get_self_url_no_query method of the OneLogin_Saml2_Utils
        """
        request_data = {
            'http_host': 'example.com',
            'script_name': '/index.html'
        }
        url = OneLogin_Saml2_Utils.get_self_url_host(request_data) + request_data['script_name']
        self.assertEqual(url, OneLogin_Saml2_Utils.get_self_url_no_query(request_data))

        request_data['path_info'] = '/test'
        self.assertEqual(url + '/test', OneLogin_Saml2_Utils.get_self_url_no_query(request_data))
Beispiel #3
0
    def testProcessResponseValid(self):
        """
        Tests the processResponse method of the OneLogin_Saml2_Auth class
        Case Valid Response, After processing the response the user
        is authenticated, attributes are returned, also has a nameID and
        the error array is empty
        """
        request_data = self.get_request()
        message = self.file_contents(join(self.data_path, 'responses', 'unsigned_response.xml.base64'))
        plain_message = b64decode(message)
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/acs.php', current_url)
        del request_data['get_data']
        request_data['post_data'] = {
            'SAMLResponse': b64encode(plain_message)
        }
        auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON())

        auth.process_response()

        self.assertTrue(auth.is_authenticated())
        self.assertEqual(len(auth.get_errors()), 0)
        self.assertEqual('*****@*****.**', auth.get_nameid())
        attributes = auth.get_attributes()
        self.assertNotEqual(len(attributes), 0)
        self.assertEqual(auth.get_attribute('mail'), attributes['mail'])

        auth.set_strict(True)
        auth.process_response()
        self.assertEqual(len(auth.get_errors()), 0)
Beispiel #4
0
    def testProcessResponseInvalidRequestId(self):
        """
        Tests the processResponse method of the OneLogin_Saml2_Auth class
        Case Invalid Response, Invalid requestID
        """
        request_data = self.get_request()
        message = self.file_contents(join(self.data_path, 'responses', 'unsigned_response.xml.base64'))
        plain_message = b64decode(message)
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/acs.php', current_url)
        del request_data['get_data']
        request_data['post_data'] = {
            'SAMLResponse': b64encode(plain_message)
        }
        auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON())
        request_id = 'invalid'
        auth.process_response(request_id)
        self.assertEqual(len(auth.get_errors()), 0)

        auth.set_strict(True)
        auth.process_response(request_id)
        self.assertEqual(auth.get_errors(), ['invalid_response'])

        valid_request_id = '_57bcbf70-7b1f-012e-c821-782bcb13bb38'
        auth.process_response(valid_request_id)
        self.assertEqual(len(auth.get_errors()), 0)
Beispiel #5
0
    def testProcessSLORequestSignedResponse(self):
        """
        Tests the process_slo method of the OneLogin_Saml2_Auth class
        Case Valid Logout Request, validating the relayState,
        a signed LogoutResponse is created and a redirection executed
        """
        settings_info = self.loadSettingsJSON()
        settings_info["security"]["logoutResponseSigned"] = True
        request_data = self.get_request()
        message = self.file_contents(join(self.data_path, "logout_requests", "logout_request_deflated.xml.base64"))
        # In order to avoid the destination problem
        plain_message = OneLogin_Saml2_Utils.decode_base64_and_inflate(message)
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        plain_message = plain_message.replace("http://stuff.com/endpoints/endpoints/sls.php", current_url)
        message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message)
        request_data["get_data"]["SAMLRequest"] = message
        request_data["get_data"]["RelayState"] = "http://relaystate.com"
        auth = OneLogin_Saml2_Auth(request_data, old_settings=settings_info)

        auth.set_strict(True)
        target_url = auth.process_slo(False)
        parsed_query = parse_qs(urlparse(target_url)[4])
        slo_url = settings_info["idp"]["singleLogoutService"]["url"]
        self.assertIn(slo_url, target_url)
        self.assertIn("SAMLResponse", parsed_query)
        self.assertIn("RelayState", parsed_query)
        self.assertIn("SigAlg", parsed_query)
        self.assertIn("Signature", parsed_query)
        self.assertIn("http://relaystate.com", parsed_query["RelayState"])
        self.assertIn(OneLogin_Saml2_Constants.RSA_SHA1, parsed_query["SigAlg"])
    def testIsInValidIssuer(self):
        """
        Tests the is_valid method of the OneLogin_Saml2_LogoutResponse
        Case invalid Issuer
        """
        request_data = {
            'http_host': 'example.com',
            'script_name': 'index.html',
            'get_data': {}
        }
        settings = OneLogin_Saml2_Settings(self.loadSettingsJSON())
        message = self.file_contents(join(self.data_path, 'logout_responses', 'logout_response_deflated.xml.base64'))

        plain_message = OneLogin_Saml2_Utils.decode_base64_and_inflate(message)
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url)
        plain_message = plain_message.replace('http://idp.example.com/', 'http://invalid.issuer.example.com')
        message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message)

        settings.set_strict(False)
        response = OneLogin_Saml2_Logout_Response(settings, message)
        self.assertTrue(response.is_valid(request_data))

        settings.set_strict(True)
        response_2 = OneLogin_Saml2_Logout_Response(settings, message)
        self.assertFalse(response_2.is_valid(request_data))
        self.assertIn('Invalid issuer in the Logout Response', response_2.get_error())
Beispiel #7
0
    def testProcessResponseInvalidRequestId(self):
        """
        Tests the process_response method of the OneLogin_Saml2_Auth class
        Case Invalid Response, Invalid requestID
        """
        request_data = self.get_request()
        message = self.file_contents(join(self.data_path, 'responses', 'unsigned_response.xml.base64'))
        plain_message = compat.to_string(b64decode(message))
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/acs.php', current_url)
        del request_data['get_data']
        request_data['post_data'] = {
            'SAMLResponse': compat.to_string(b64encode(compat.to_bytes(plain_message)))
        }
        auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON())
        request_id = 'invalid'
        auth.process_response(request_id)
        self.assertEqual('No Signature found. SAML Response rejected', auth.get_last_error_reason())

        auth.set_strict(True)
        auth.process_response(request_id)
        self.assertEqual(auth.get_errors(), ['invalid_response'])
        self.assertEqual('The InResponseTo of the Response: _57bcbf70-7b1f-012e-c821-782bcb13bb38, does not match the ID of the AuthNRequest sent by the SP: invalid', auth.get_last_error_reason())

        valid_request_id = '_57bcbf70-7b1f-012e-c821-782bcb13bb38'
        auth.process_response(valid_request_id)
        self.assertEqual('No Signature found. SAML Response rejected', auth.get_last_error_reason())
Beispiel #8
0
    def logout(self,
               return_to=None,
               name_id=None,
               session_index=None,
               nq=None,
               name_id_format=None):
        """
        Initiates the SLO process.

        :param return_to: Optional argument. The target URL the user should be redirected to after logout.
        :type return_to: string

        :param name_id: The NameID that will be set in the LogoutRequest.
        :type name_id: string

        :param session_index: SessionIndex that identifies the session of the user.
        :type session_index: string

        :param nq: IDP Name Qualifier
        :type: string

        :param name_id_format: The NameID Format that will be set in the LogoutRequest.
        :type: string

        :returns: Redirection URL
        """
        slo_url = self.get_slo_url()
        if slo_url is None:
            raise OneLogin_Saml2_Error(
                'The IdP does not support Single Log Out',
                OneLogin_Saml2_Error.SAML_SINGLE_LOGOUT_NOT_SUPPORTED)

        if name_id is None and self.__nameid is not None:
            name_id = self.__nameid

        if name_id_format is None and self.__nameid_format is not None:
            name_id_format = self.__nameid_format

        logout_request = OneLogin_Saml2_Logout_Request(
            self.__settings,
            name_id=name_id,
            session_index=session_index,
            nq=nq,
            name_id_format=name_id_format)
        self.__last_request = logout_request.get_xml()
        self.__last_request_id = logout_request.id

        parameters = {'SAMLRequest': logout_request.get_request()}
        if return_to is not None:
            parameters['RelayState'] = return_to
        else:
            parameters[
                'RelayState'] = OneLogin_Saml2_Utils.get_self_url_no_query(
                    self.__request_data)

        security = self.__settings.get_security_data()
        if security.get('logoutRequestSigned', False):
            self.add_request_signature(parameters,
                                       security['signatureAlgorithm'])
        return self.redirect_to(slo_url, parameters)
    def testIsValidWithCapitalization(self):
        """
        Tests the is_valid method of the OneLogin_Saml2_LogoutResponse
        """
        request_data = {
            'http_host': 'exaMPLe.com',
            'script_name': 'index.html',
            'get_data': {}
        }
        settings = OneLogin_Saml2_Settings(self.loadSettingsJSON())
        message = self.file_contents(
            join(self.data_path, 'logout_responses',
                 'logout_response_deflated.xml.base64'))

        response = OneLogin_Saml2_Logout_Response(settings, message)
        self.assertTrue(response.is_valid(request_data))

        settings.set_strict(True)
        response_2 = OneLogin_Saml2_Logout_Response(settings, message)
        with self.assertRaisesRegex(Exception,
                                    'The LogoutResponse was received at'):
            response_2.is_valid(request_data, raise_exceptions=True)

        plain_message = compat.to_string(
            OneLogin_Saml2_Utils.decode_base64_and_inflate(message))

        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(
            request_data).lower()
        plain_message = plain_message.replace(
            'http://stuff.com/endpoints/endpoints/sls.php', current_url)
        message_3 = OneLogin_Saml2_Utils.deflate_and_base64_encode(
            plain_message)

        response_3 = OneLogin_Saml2_Logout_Response(settings, message_3)
        self.assertTrue(response_3.is_valid(request_data))
Beispiel #10
0
    def testProcessSLORequestSignedResponse(self):
        """
        Tests the process_slo method of the OneLogin_Saml2_Auth class
        Case Valid Logout Request, validating the relayState,
        a signed LogoutResponse is created and a redirection executed
        """
        settings_info = self.loadSettingsJSON()
        settings_info['security']['logoutResponseSigned'] = True
        request_data = self.get_request()
        message = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request_deflated.xml.base64'))
        # In order to avoid the destination problem
        plain_message = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(message))
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url)
        message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message)
        request_data['get_data']['SAMLRequest'] = message
        request_data['get_data']['RelayState'] = 'http://relaystate.com'
        auth = OneLogin_Saml2_Auth(request_data, old_settings=settings_info)

        auth.set_strict(True)
        target_url = auth.process_slo(False)
        parsed_query = parse_qs(urlparse(target_url)[4])
        slo_url = settings_info['idp']['singleLogoutService']['url']
        self.assertIn(slo_url, target_url)
        self.assertIn('SAMLResponse', parsed_query)
        self.assertIn('RelayState', parsed_query)
        self.assertIn('SigAlg', parsed_query)
        self.assertIn('Signature', parsed_query)
        self.assertIn('http://relaystate.com', parsed_query['RelayState'])
        self.assertIn(OneLogin_Saml2_Constants.RSA_SHA1, parsed_query['SigAlg'])
Beispiel #11
0
    def login(self, return_to=None, force_authn=False, is_passive=False, set_nameid_policy=True):
        """
        Initiates the SSO process.

        :param return_to: Optional argument. The target URL the user should be redirected to after login.
        :type return_to: string

        :param force_authn: Optional argument. When true the AuthNRequest will set the ForceAuthn='true'.
        :type force_authn: bool

        :param is_passive: Optional argument. When true the AuthNRequest will set the Ispassive='true'.
        :type is_passive: bool

        :param set_nameid_policy: Optional argument. When true the AuthNRequest will set a nameIdPolicy element.
        :type set_nameid_policy: bool

        :returns: Redirection URL
        :rtype: string
        """
        authn_request = OneLogin_Saml2_Authn_Request(self.__settings, force_authn, is_passive, set_nameid_policy)
        self.__last_request = authn_request.get_xml()
        self.__last_request_id = authn_request.get_id()
        saml_request = authn_request.get_request()

        parameters = {'SAMLRequest': saml_request}
        if return_to is not None:
            parameters['RelayState'] = return_to
        else:
            parameters['RelayState'] = OneLogin_Saml2_Utils.get_self_url_no_query(self.__request_data)

        security = self.__settings.get_security_data()
        if security.get('authnRequestsSigned', False):
            parameters['SigAlg'] = security['signatureAlgorithm']
            parameters['Signature'] = self.build_request_signature(saml_request, parameters['RelayState'], security['signatureAlgorithm'])
        return self.redirect_to(self.get_sso_url(), parameters)
    def testIsInvalidNotOnOrAfter(self):
        """
        Tests the is_valid method of the OneLogin_Saml2_LogoutRequest
        Case Invalid NotOnOrAfter
        """
        request_data = {
            'http_host': 'example.com',
            'script_name': 'index.html'
        }
        request = self.file_contents(
            join(self.data_path, 'logout_requests', 'invalids',
                 'not_after_failed.xml'))
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        request = request.replace(
            'http://stuff.com/endpoints/endpoints/sls.php', current_url)
        settings = OneLogin_Saml2_Settings(self.loadSettingsJSON())

        logout_request = OneLogin_Saml2_Logout_Request(settings,
                                                       b64encode(request))
        self.assertTrue(logout_request.is_valid(request_data))

        settings.set_strict(True)
        logout_request2 = OneLogin_Saml2_Logout_Request(
            settings, b64encode(request))
        self.assertFalse(logout_request2.is_valid(request_data))
        self.assertIn(
            'Could not validate timestamp: expired. Check system clock.',
            logout_request2.get_error())
Beispiel #13
0
    def testProcessSLOResponseValidDeletingSession(self):
        """
        Tests the process_slo method of the OneLogin_Saml2_Auth class
        Case Valid Logout Response, validating deleting the local session
        """
        request_data = self.get_request()
        message = self.file_contents(join(self.data_path, 'logout_responses', 'logout_response_deflated.xml.base64'))

        # FIXME
        # if (!isset($_SESSION)) {
        #     $_SESSION = array();
        # }
        # $_SESSION['samltest'] = true;

        # In order to avoid the destination problem
        plain_message = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(message))
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url)
        message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message)
        request_data['get_data']['SAMLResponse'] = message
        auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON())

        auth.set_strict(True)
        auth.process_slo(False)

        self.assertEqual(len(auth.get_errors()), 0)
Beispiel #14
0
    def login(self, return_to=None, force_authn=False, is_passive=False):
        """
        Initiates the SSO process.

        :param return_to: Optional argument. The target URL the user should be redirected to after login.
        :type return_to: string

        :param force_authn: Optional argument. When true the AuthNReuqest will set the ForceAuthn='true'.
        :type force_authn: string

        :param is_passive: Optional argument. When true the AuthNReuqest will set the Ispassive='true'.
        :type is_passive: string

        :returns: Redirection url
        """
        authn_request = OneLogin_Saml2_Authn_Request(self.__settings, force_authn, is_passive)

        saml_request = authn_request.get_request()
        parameters = {'SAMLRequest': saml_request}

        if return_to is not None:
            parameters['RelayState'] = return_to
        else:
            parameters['RelayState'] = OneLogin_Saml2_Utils.get_self_url_no_query(self.__request_data)

        security = self.__settings.get_security_data()
        if security.get('authnRequestsSigned', False):
            parameters['SigAlg'] = OneLogin_Saml2_Constants.RSA_SHA1
            parameters['Signature'] = self.build_request_signature(saml_request, parameters['RelayState'])
        return self.redirect_to(self.get_sso_url(), parameters)
    def testIsValid(self):
        """
        Tests the is_valid method of the OneLogin_Saml2_LogoutResponse
        """
        request_data = {
            'http_host': 'example.com',
            'script_name': 'index.html',
            'get_data': {}
        }
        settings = OneLogin_Saml2_Settings(self.loadSettingsJSON())
        message = self.file_contents(join(self.data_path, 'logout_responses', 'logout_response_deflated.xml.base64'))

        response = OneLogin_Saml2_Logout_Response(settings, message)
        self.assertTrue(response.is_valid(request_data))

        settings.set_strict(True)
        response_2 = OneLogin_Saml2_Logout_Response(settings, message)
        try:
            valid = response_2.is_valid(request_data)
            self.assertFalse(valid)
        except Exception as e:
            self.assertIn('The LogoutRequest was received at', e.message)

        plain_message = OneLogin_Saml2_Utils.decode_base64_and_inflate(message)
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url)
        message_3 = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message)

        response_3 = OneLogin_Saml2_Logout_Response(settings, message_3)
        self.assertTrue(response_3.is_valid(request_data))
Beispiel #16
0
    def login(self, return_to=None, force_authn=False, is_passive=False):
        """
        Initiates the SSO process.

        :param return_to: Optional argument. The target URL the user should be redirected to after login.
        :type return_to: string

        :param force_authn: Optional argument. When true the AuthNReuqest will set the ForceAuthn='true'.
        :type force_authn: string

        :param is_passive: Optional argument. When true the AuthNReuqest will set the Ispassive='true'.
        :type is_passive: string

        :returns: Redirection url
        """
        authn_request = OneLogin_Saml2_Authn_Request(self.__settings,
                                                     force_authn, is_passive)

        saml_request = authn_request.get_request()
        parameters = {'SAMLRequest': saml_request}

        if return_to is not None:
            parameters['RelayState'] = return_to
        else:
            parameters[
                'RelayState'] = OneLogin_Saml2_Utils.get_self_url_no_query(
                    self.__request_data)

        security = self.__settings.get_security_data()
        if security.get('authnRequestsSigned', False):
            parameters['SigAlg'] = OneLogin_Saml2_Constants.RSA_SHA1
            parameters['Signature'] = self.build_request_signature(
                saml_request, parameters['RelayState'])
        return self.redirect_to(self.get_sso_url(), parameters)
Beispiel #17
0
    def testProcessResponseInvalidRequestId(self):
        """
        Tests the process_response method of the OneLogin_Saml2_Auth class
        Case Invalid Response, Invalid requestID
        """
        request_data = self.get_request()
        message = self.file_contents(join(self.data_path, "responses", "unsigned_response.xml.base64"))
        plain_message = b64decode(message)
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        plain_message = plain_message.replace("http://stuff.com/endpoints/endpoints/acs.php", current_url)
        del request_data["get_data"]
        request_data["post_data"] = {"SAMLResponse": b64encode(plain_message)}
        auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON())
        request_id = "invalid"
        auth.process_response(request_id)
        self.assertEqual("No Signature found. SAML Response rejected", auth.get_last_error_reason())

        auth.set_strict(True)
        auth.process_response(request_id)
        self.assertEqual(auth.get_errors(), ["invalid_response"])
        self.assertEqual(
            "The InResponseTo of the Response: _57bcbf70-7b1f-012e-c821-782bcb13bb38, does not match the ID of the AuthNRequest sent by the SP: invalid",
            auth.get_last_error_reason(),
        )

        valid_request_id = "_57bcbf70-7b1f-012e-c821-782bcb13bb38"
        auth.process_response(valid_request_id)
        self.assertEqual("No Signature found. SAML Response rejected", auth.get_last_error_reason())
Beispiel #18
0
    def testIsInValidIssuer(self):
        """
        Tests the is_valid method of the OneLogin_Saml2_LogoutResponse
        Case invalid Issuer
        """
        request_data = {
            'http_host': 'example.com',
            'script_name': 'index.html',
            'get_data': {}
        }
        settings = OneLogin_Saml2_Settings(self.loadSettingsJSON())
        message = self.file_contents(
            join(self.data_path, 'logout_responses',
                 'logout_response_deflated.xml.base64'))

        plain_message = compat.to_string(
            OneLogin_Saml2_Utils.decode_base64_and_inflate(message))
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        plain_message = plain_message.replace(
            'http://stuff.com/endpoints/endpoints/sls.php', current_url)
        plain_message = plain_message.replace(
            'http://idp.example.com/', 'http://invalid.issuer.example.com')
        message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message)

        settings.set_strict(False)
        response = OneLogin_Saml2_Logout_Response(settings, message)
        self.assertTrue(response.is_valid(request_data))

        settings.set_strict(True)
        response_2 = OneLogin_Saml2_Logout_Response(settings, message)
        with self.assertRaisesRegexp(Exception,
                                     'Invalid issuer in the Logout Request'):
            response_2.is_valid(request_data, raise_exceptions=True)
Beispiel #19
0
    def testProcessResponseInvalidRequestId(self):
        """
        Tests the process_response method of the OneLogin_Saml2_Auth class
        Case Invalid Response, Invalid requestID
        """
        request_data = self.get_request()
        message = self.file_contents(
            join(self.data_path, 'responses', 'unsigned_response.xml.base64'))
        plain_message = b64decode(message)
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        plain_message = plain_message.replace(
            'http://stuff.com/endpoints/endpoints/acs.php', current_url)
        del request_data['get_data']
        request_data['post_data'] = {'SAMLResponse': b64encode(plain_message)}
        auth = OneLogin_Saml2_Auth(request_data,
                                   old_settings=self.loadSettingsJSON())
        request_id = 'invalid'
        auth.process_response(request_id)
        self.assertEqual('No Signature found. SAML Response rejected',
                         auth.get_last_error_reason())

        auth.set_strict(True)
        auth.process_response(request_id)
        self.assertEqual(auth.get_errors(), ['invalid_response'])
        self.assertEqual(
            'The InResponseTo of the Response: _57bcbf70-7b1f-012e-c821-782bcb13bb38, does not match the ID of the AuthNRequest sent by the SP: invalid',
            auth.get_last_error_reason())

        valid_request_id = '_57bcbf70-7b1f-012e-c821-782bcb13bb38'
        auth.process_response(valid_request_id)
        self.assertEqual('No Signature found. SAML Response rejected',
                         auth.get_last_error_reason())
    def is_valid(self, request_data, request_id=None):
        """
        Determines if the SAML LogoutResponse is valid
        :param request_id: The ID of the LogoutRequest sent by this SP to the IdP
        :type request_id: string
        :return: Returns if the SAML LogoutResponse is or not valid
        :rtype: boolean
        """
        self.__error = None
        try:
            idp_data = self.__settings.get_idp_data()
            idp_entity_id = idp_data['entityId']
            get_data = request_data['get_data']

            if self.__settings.is_strict():
                res = OneLogin_Saml2_XML.validate_xml(
                    self.document, 'saml-schema-protocol-2.0.xsd',
                    self.__settings.is_debug_active())
                if isinstance(res, str):
                    raise Exception(
                        'Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd'
                    )

                security = self.__settings.get_security_data()

                # Check if the InResponseTo of the Logout Response matchs the ID of the Logout Request (requestId) if provided
                in_response_to = self.document.get('InResponseTo', None)
                if request_id is not None and in_response_to and in_response_to != request_id:
                    raise Exception(
                        'The InResponseTo of the Logout Response: %s, does not match the ID of the Logout request sent by the SP: %s'
                        % (in_response_to, request_id))

                # Check issuer
                issuer = self.get_issuer()
                if issuer is not None and issuer != idp_entity_id:
                    raise Exception('Invalid issuer in the Logout Request')

                current_url = OneLogin_Saml2_Utils.get_self_url_no_query(
                    request_data)

                # Check destination
                destination = self.document.get('Destination', None)
                if destination and current_url not in destination:
                    raise Exception(
                        'The LogoutRequest was received at $currentURL instead of $destination'
                    )

                if security['wantMessagesSigned']:
                    if 'Signature' not in get_data:
                        raise Exception(
                            'The Message of the Logout Response is not signed and the SP require it'
                        )
            return True
        # pylint: disable=R0801
        except Exception as err:
            self.__error = str(err)
            debug = self.__settings.is_debug_active()
            if debug:
                print(err)
            return False
Beispiel #21
0
    def testProcessSLOResponseValidDeletingSession(self):
        """
        Tests the process_slo method of the OneLogin_Saml2_Auth class
        Case Valid Logout Response, validating deleting the local session
        """
        request_data = self.get_request()
        message = self.file_contents(
            join(self.data_path, 'logout_responses',
                 'logout_response_deflated.xml.base64'))

        # FIXME
        # if (!isset($_SESSION)) {
        #     $_SESSION = array();
        # }
        # $_SESSION['samltest'] = true;

        # In order to avoid the destination problem
        plain_message = OneLogin_Saml2_Utils.decode_base64_and_inflate(message)
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        plain_message = plain_message.replace(
            'http://stuff.com/endpoints/endpoints/sls.php', current_url)
        message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message)
        request_data['get_data']['SAMLResponse'] = message
        auth = OneLogin_Saml2_Auth(request_data,
                                   old_settings=self.loadSettingsJSON())

        auth.set_strict(True)
        auth.process_slo(False)

        self.assertEqual(len(auth.get_errors()), 0)
Beispiel #22
0
    def testProcessSLOResponseRequestId(self):
        """
        Tests the process_slo method of the OneLogin_Saml2_Auth class
        Case Logout Response with valid and invalid Request ID
        """
        request_data = self.get_request()
        message = self.file_contents(
            join(self.data_path, 'logout_responses',
                 'logout_response_deflated.xml.base64'))
        # In order to avoid the destination problem
        plain_message = OneLogin_Saml2_Utils.decode_base64_and_inflate(message)
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        plain_message = plain_message.replace(
            'http://stuff.com/endpoints/endpoints/sls.php', current_url)
        message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message)
        request_data['get_data']['SAMLResponse'] = message
        auth = OneLogin_Saml2_Auth(request_data,
                                   old_settings=self.loadSettingsJSON())

        request_id = 'wrongID'
        auth.set_strict(True)
        auth.process_slo(True, request_id)
        self.assertEqual(auth.get_errors(), ['invalid_logout_response'])

        request_id = 'ONELOGIN_21584ccdfaca36a145ae990442dcd96bfe60151e'
        auth.process_slo(True, request_id)
        self.assertEqual(len(auth.get_errors()), 0)
Beispiel #23
0
    def testProcessResponseValid(self):
        """
        Tests the process_response method of the OneLogin_Saml2_Auth class
        Case Valid Response, After processing the response the user
        is authenticated, attributes are returned, also has a nameID and
        the error array is empty
        """
        request_data = self.get_request()
        message = self.file_contents(join(self.data_path, 'responses', 'unsigned_response.xml.base64'))
        plain_message = b64decode(message)
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/acs.php', current_url)
        del request_data['get_data']
        request_data['post_data'] = {
            'SAMLResponse': b64encode(plain_message)
        }
        auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON())

        auth.process_response()

        self.assertTrue(auth.is_authenticated())
        self.assertEqual(len(auth.get_errors()), 0)
        self.assertEqual('*****@*****.**', auth.get_nameid())
        attributes = auth.get_attributes()
        self.assertNotEqual(len(attributes), 0)
        self.assertEqual(auth.get_attribute('mail'), attributes['mail'])

        auth.set_strict(True)
        auth.process_response()
        self.assertEqual(len(auth.get_errors()), 0)
Beispiel #24
0
    def testProcessSLORequestSignedResponse(self):
        """
        Tests the process_slo method of the OneLogin_Saml2_Auth class
        Case Valid Logout Request, validating the relayState,
        a signed LogoutResponse is created and a redirection executed
        """
        settings_info = self.loadSettingsJSON()
        settings_info['security']['logoutResponseSigned'] = True
        request_data = self.get_request()
        message = self.file_contents(
            join(self.data_path, 'logout_requests',
                 'logout_request_deflated.xml.base64'))
        # In order to avoid the destination problem
        plain_message = OneLogin_Saml2_Utils.decode_base64_and_inflate(message)
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        plain_message = plain_message.replace(
            'http://stuff.com/endpoints/endpoints/sls.php', current_url)
        message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message)
        request_data['get_data']['SAMLRequest'] = message
        request_data['get_data']['RelayState'] = 'http://relaystate.com'
        auth = OneLogin_Saml2_Auth(request_data, old_settings=settings_info)

        auth.set_strict(True)
        target_url = auth.process_slo(False)
        parsed_query = parse_qs(urlparse(target_url)[4])
        slo_url = settings_info['idp']['singleLogoutService']['url']
        self.assertIn(slo_url, target_url)
        self.assertIn('SAMLResponse', parsed_query)
        self.assertIn('RelayState', parsed_query)
        self.assertIn('SigAlg', parsed_query)
        self.assertIn('Signature', parsed_query)
        self.assertIn('http://relaystate.com', parsed_query['RelayState'])
        self.assertIn(OneLogin_Saml2_Constants.RSA_SHA1,
                      parsed_query['SigAlg'])
Beispiel #25
0
    def login(self, return_to=None, force_authn=False, is_passive=False, set_nameid_policy=True):
        """
        Initiates the SSO process.

        :param return_to: Optional argument. The target URL the user should be redirected to after login.
        :type return_to: string

        :param force_authn: Optional argument. When true the AuthNReuqest will set the ForceAuthn='true'.
        :type force_authn: bool

        :param is_passive: Optional argument. When true the AuthNReuqest will set the Ispassive='true'.
        :type is_passive: bool

        :param set_nameid_policy: Optional argument. When true the AuthNReuqest will set a nameIdPolicy element.
        :type set_nameid_policy: bool

        :returns: Redirection url
        :rtype: string
        """
        authn_request = OneLogin_Saml2_Authn_Request(self.__settings, force_authn, is_passive, set_nameid_policy)

        saml_request = authn_request.get_request()
        parameters = {'SAMLRequest': saml_request}

        if return_to is not None:
            parameters['RelayState'] = return_to
        else:
            parameters['RelayState'] = OneLogin_Saml2_Utils.get_self_url_no_query(self.__request_data)

        security = self.__settings.get_security_data()
        if security.get('authnRequestsSigned', False):
            self.add_request_signature(parameters, security['signatureAlgorithm'])
        return self.redirect_to(self.get_sso_url(), parameters)
    def testIsInvalidIssuer(self):
        """
        Tests the is_valid method of the OneLogin_Saml2_LogoutRequest
        Case Invalid Issuer
        """
        request = self.file_contents(
            join(self.data_path, 'logout_requests', 'invalids',
                 'invalid_issuer.xml'))
        request_data = {
            'http_host': 'example.com',
            'script_name': 'index.html'
        }
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        request = request.replace(
            'http://stuff.com/endpoints/endpoints/sls.php', current_url)
        settings = OneLogin_Saml2_Settings(self.loadSettingsJSON())
        logout_request = OneLogin_Saml2_Logout_Request(settings,
                                                       b64encode(request))
        self.assertTrue(logout_request.is_valid(request_data))

        settings.set_strict(True)
        logout_request2 = OneLogin_Saml2_Logout_Request(
            settings, b64encode(request))
        self.assertFalse(logout_request2.is_valid(request_data))
        self.assertIn('Invalid issuer in the Logout Request',
                      logout_request2.get_error())
    def testIsValid(self):
        """
        Tests the is_valid method of the OneLogin_Saml2_LogoutResponse
        """
        request_data = {
            'http_host': 'example.com',
            'script_name': 'index.html',
            'get_data': {}
        }
        settings = OneLogin_Saml2_Settings(self.loadSettingsJSON())
        message = self.file_contents(join(self.data_path, 'logout_responses', 'logout_response_deflated.xml.base64'))

        response = OneLogin_Saml2_Logout_Response(settings, message)
        self.assertTrue(response.is_valid(request_data))

        settings.set_strict(True)
        response_2 = OneLogin_Saml2_Logout_Response(settings, message)
        self.assertFalse(response_2.is_valid(request_data))
        self.assertIn('The LogoutResponse was received at', response_2.get_error())

        plain_message = OneLogin_Saml2_Utils.decode_base64_and_inflate(message)
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url)
        message_3 = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message)

        response_3 = OneLogin_Saml2_Logout_Response(settings, message_3)
        self.assertTrue(response_3.is_valid(request_data))
    def testIsInvalidNotOnOrAfter(self):
        """
        Tests the is_valid method of the OneLogin_Saml2_LogoutRequest
        Case Invalid NotOnOrAfter
        """
        request_data = {
            'http_host': 'example.com',
            'script_name': 'index.html'
        }
        request = self.file_contents(
            join(self.data_path, 'logout_requests', 'invalids',
                 'not_after_failed.xml'))
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        request = request.replace(
            'http://stuff.com/endpoints/endpoints/sls.php', current_url)
        settings = OneLogin_Saml2_Settings(self.loadSettingsJSON())

        logout_request = OneLogin_Saml2_Logout_Request(settings,
                                                       b64encode(request))
        self.assertTrue(logout_request.is_valid(request_data))

        settings.set_strict(True)
        try:
            logout_request2 = OneLogin_Saml2_Logout_Request(
                settings, b64encode(request))
            valid = logout_request2.is_valid(request_data)
            self.assertFalse(valid)
        except Exception as e:
            self.assertIn('Timing issues (please check your clock settings)',
                          e.message)
Beispiel #29
0
    def testIsValid(self):
        """
        Tests the is_valid method of the OneLogin_Saml2_LogoutRequest
        """
        request_data = {
            'http_host': 'example.com',
            'script_name': 'index.html'
        }
        request = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request.xml'))
        settings = OneLogin_Saml2_Settings(self.loadSettingsJSON())

        logout_request = OneLogin_Saml2_Logout_Request(settings, b64encode(request))
        self.assertTrue(logout_request.is_valid(request_data))

        settings.set_strict(True)
        logout_request2 = OneLogin_Saml2_Logout_Request(settings, b64encode(request))
        self.assertFalse(logout_request2.is_valid(request_data))

        settings.set_strict(False)
        dom = parseString(request)
        logout_request3 = OneLogin_Saml2_Logout_Request(settings, b64encode(dom.toxml()))
        self.assertTrue(logout_request3.is_valid(request_data))

        settings.set_strict(True)
        logout_request4 = OneLogin_Saml2_Logout_Request(settings, b64encode(dom.toxml()))
        self.assertFalse(logout_request4.is_valid(request_data))

        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        request = request.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url)
        logout_request5 = OneLogin_Saml2_Logout_Request(settings, b64encode(request))
        self.assertTrue(logout_request5.is_valid(request_data))
Beispiel #30
0
    def logout(self, return_to=None):
        """
        Initiates the SLO process.

        :param return_to: Optional argument. The target URL the user should be redirected to after logout.
        :type return_to: string

        :returns: Redirection url
        """
        slo_url = self.get_slo_url()
        if slo_url is None:
            raise OneLogin_Saml2_Error(
                'The IdP does not support Single Log Out',
                OneLogin_Saml2_Error.SAML_SINGLE_LOGOUT_NOT_SUPPORTED
            )

        logout_request = OneLogin_Saml2_Logout_Request(self.__settings)

        saml_request = logout_request.get_request()

        parameters = {'SAMLRequest': logout_request.get_request()}
        if return_to is not None:
            parameters['RelayState'] = return_to
        else:
            parameters['RelayState'] = OneLogin_Saml2_Utils.get_self_url_no_query(self.__request_data)

        security = self.__settings.get_security_data()
        if security.get('logoutRequestSigned', False):
            parameters['SigAlg'] = OneLogin_Saml2_Constants.RSA_SHA1
            parameters['Signature'] = self.build_request_signature(saml_request, parameters['RelayState'])
        return self.redirect_to(slo_url, parameters)
Beispiel #31
0
    def testIsInValidRequestId(self):
        """
        Tests the is_valid method of the OneLogin_Saml2_LogoutResponse
        Case invalid request Id
        """
        request_data = {
            'http_host': 'example.com',
            'script_name': 'index.html',
            'get_data': {}
        }
        settings = OneLogin_Saml2_Settings(self.loadSettingsJSON())
        message = self.file_contents(
            join(self.data_path, 'logout_responses',
                 'logout_response_deflated.xml.base64'))

        plain_message = compat.to_string(
            OneLogin_Saml2_Utils.decode_base64_and_inflate(message))
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        plain_message = plain_message.replace(
            'http://stuff.com/endpoints/endpoints/sls.php', current_url)
        message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message)

        request_id = 'invalid_request_id'

        settings.set_strict(False)
        response = OneLogin_Saml2_Logout_Response(settings, message)
        self.assertTrue(response.is_valid(request_data, request_id))

        settings.set_strict(True)
        response_2 = OneLogin_Saml2_Logout_Response(settings, message)
        try:
            valid = response_2.is_valid(request_data, request_id)
            self.assertFalse(valid)
        except Exception as e:
            self.assertIn('The InResponseTo of the Logout Response:', str(e))
Beispiel #32
0
    def login(self, return_to=None, force_authn=False, is_passive=False, set_nameid_policy=True):
        """
        Initiates the SSO process.

        :param return_to: Optional argument. The target URL the user should be redirected to after login.
        :type return_to: string

        :param force_authn: Optional argument. When true the AuthNReuqest will set the ForceAuthn='true'.
        :type force_authn: bool

        :param is_passive: Optional argument. When true the AuthNReuqest will set the Ispassive='true'.
        :type is_passive: bool

        :param set_nameid_policy: Optional argument. When true the AuthNReuqest will set a nameIdPolicy element.
        :type set_nameid_policy: bool

        :returns: Redirection url
        :rtype: string
        """
        authn_request = OneLogin_Saml2_Authn_Request(self.__settings, force_authn, is_passive, set_nameid_policy)

        self.__last_request_id = authn_request.get_id()

        saml_request = authn_request.get_request()
        parameters = {'SAMLRequest': saml_request}

        if return_to is not None:
            parameters['RelayState'] = return_to
        else:
            parameters['RelayState'] = OneLogin_Saml2_Utils.get_self_url_no_query(self.__request_data)

        security = self.__settings.get_security_data()
        if security.get('authnRequestsSigned', False):
            parameters['SigAlg'] = security['signatureAlgorithm']
            parameters['Signature'] = self.build_request_signature(saml_request, parameters['RelayState'], security['signatureAlgorithm'])
            # HTTP-POST binding requires generation of a form
            if self.get_settings().get_idp_data()['singleSignOnService'].get('binding',
                                                                             None) == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST':
                log.debug("Generating AuthnRequest HTTP-POST binding form")

                # Return HTML form
                template_file = open(join(dirname(__file__), 'templates/authn_request.html'))
                template_text = template_file.read()
                template = Template(template_text)

                context = {
                    'sso_url': self.get_sso_url(),
                    'saml_request': saml_request,
                    'relay_state': parameters['RelayState']
                }

                html = template.render(context)
                log.debug("Generated HTML: %s" % html)

                return html

        return self.redirect_to(self.get_sso_url(), parameters)
Beispiel #33
0
    def process_slo(self, keep_local_session=False, request_id=None, delete_session_cb=None):
        """
        Process the SAML Logout Response / Logout Request sent by the IdP.

        :param keep_local_session: When false will destroy the local session, otherwise will destroy it
        :type keep_local_session: bool

        :param request_id: The ID of the LogoutRequest sent by this SP to the IdP
        :type request_id: string

        :returns: Redirection url
        """
        self.__errors = []

        if 'get_data' in self.__request_data and 'SAMLResponse' in self.__request_data['get_data']:
            logout_response = OneLogin_Saml2_Logout_Response(self.__settings, self.__request_data['get_data']['SAMLResponse'])
            if not logout_response.is_valid(self.__request_data, request_id):
                self.__errors.append('invalid_logout_response')
                self.__error_reason = logout_response.get_error()
            elif logout_response.get_status() != OneLogin_Saml2_Constants.STATUS_SUCCESS:
                self.__errors.append('logout_not_success')
            elif not keep_local_session:
                OneLogin_Saml2_Utils.delete_local_session(delete_session_cb)

        elif 'get_data' in self.__request_data and 'SAMLRequest' in self.__request_data['get_data']:
            logout_request = OneLogin_Saml2_Logout_Request(self.__settings, self.__request_data['get_data']['SAMLRequest'])
            if not logout_request.is_valid(self.__request_data):
                self.__errors.append('invalid_logout_request')
                self.__error_reason = logout_request.get_error()
            else:
                if not keep_local_session:
                    OneLogin_Saml2_Utils.delete_local_session(delete_session_cb)

                in_response_to = logout_request.id
                response_builder = OneLogin_Saml2_Logout_Response(self.__settings)
                response_builder.build(in_response_to)
                logout_response = response_builder.get_response()

                parameters = {'SAMLResponse': logout_response}
                if 'RelayState' in self.__request_data['get_data']:
                    parameters['RelayState'] = self.__request_data['get_data']['RelayState']
                else:
                    parameters['RelayState'] = OneLogin_Saml2_Utils.get_self_url_no_query(self.__request_data)

                security = self.__settings.get_security_data()
                if 'logoutResponseSigned' in security and security['logoutResponseSigned']:
                    parameters['SigAlg'] = security['signatureAlgorithm']
                    parameters['Signature'] = self.build_response_signature(logout_response, parameters.get('RelayState', None), security['signatureAlgorithm'])

                return self.redirect_to(self.get_slo_url(), parameters)
        else:
            self.__errors.append('invalid_binding')
            raise OneLogin_Saml2_Error(
                'SAML LogoutRequest/LogoutResponse not found. Only supported HTTP_REDIRECT Binding',
                OneLogin_Saml2_Error.SAML_LOGOUTMESSAGE_NOT_FOUND
            )
Beispiel #34
0
    def logout(self, return_to=None, name_id=None, session_index=None, nq=None, name_id_format=None):
        """
        Initiates the SLO process.

        :param return_to: Optional argument. The target URL the user should be redirected to after logout.
        :type return_to: string

        :param name_id: The NameID that will be set in the LogoutRequest.
        :type name_id: string

        :param session_index: SessionIndex that identifies the session of the user.
        :type session_index: string

        :param nq: IDP Name Qualifier
        :type: string

        :param name_id_format: The NameID Format that will be set in the LogoutRequest.
        :type: string

        :returns: Redirection url
        """
        slo_url = self.get_slo_url()
        if slo_url is None:
            raise OneLogin_Saml2_Error(
                'The IdP does not support Single Log Out',
                OneLogin_Saml2_Error.SAML_SINGLE_LOGOUT_NOT_SUPPORTED
            )

        if name_id is None and self.__nameid is not None:
            name_id = self.__nameid
        if name_id_format is None and self.__nameid_format is not None:
            name_id_format = self.__nameid_format

        logout_request = OneLogin_Saml2_Logout_Request(
            self.__settings,
            name_id=name_id,
            session_index=session_index,
            nq=nq,
            name_id_format=name_id_format
        )
        self.__last_request = logout_request.get_xml()
        self.__last_request_id = logout_request.id
        saml_request = logout_request.get_request()

        parameters = {'SAMLRequest': logout_request.get_request()}
        if return_to is not None:
            parameters['RelayState'] = return_to
        else:
            parameters['RelayState'] = OneLogin_Saml2_Utils.get_self_url_no_query(self.__request_data)

        security = self.__settings.get_security_data()
        if security.get('logoutRequestSigned', False):
            parameters['SigAlg'] = security['signatureAlgorithm']
            parameters['Signature'] = self.build_request_signature(saml_request, parameters['RelayState'], security['signatureAlgorithm'])
        return self.redirect_to(slo_url, parameters)
    def is_valid(self, request_data, request_id=None):
        """
        Determines if the SAML LogoutResponse is valid
        :param request_id: The ID of the LogoutRequest sent by this SP to the IdP
        :type request_id: string
        :return: Returns if the SAML LogoutResponse is or not valid
        :rtype: boolean
        """
        self.__error = None
        try:
            idp_data = self.__settings.get_idp_data()
            idp_entity_id = idp_data['entityId']
            get_data = request_data['get_data']

            if self.__settings.is_strict():
                res = OneLogin_Saml2_XML.validate_xml(self.document, 'saml-schema-protocol-2.0.xsd', self.__settings.is_debug_active())
                if isinstance(res, str):
                    raise Exception('Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd')

                security = self.__settings.get_security_data()

                # Check if the InResponseTo of the Logout Response matches the ID of the Logout Request (requestId) if provided
                in_response_to = self.document.get('InResponseTo', None)
                if request_id is not None and in_response_to and in_response_to != request_id:
                    raise Exception('The InResponseTo of the Logout Response: %s, does not match the ID of the Logout request sent by the SP: %s' % (in_response_to, request_id))

                # Check issuer
                issuer = self.get_issuer()
                if issuer is not None and issuer != idp_entity_id:
                    raise Exception('Invalid issuer in the Logout Request')

                current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)

                # Check destination
                destination = self.document.get('Destination', None)
                if destination and current_url not in destination:
                    raise Exception('The LogoutRequest was received at $currentURL instead of $destination')

                if security['wantMessagesSigned']:
                    if 'Signature' not in get_data:
                        raise Exception('The Message of the Logout Response is not signed and the SP require it')
            return True
        # pylint: disable=R0801
        except Exception as err:
            self.__error = str(err)
            debug = self.__settings.is_debug_active()
            if debug:
                print(err)
            return False
Beispiel #36
0
    def testProcessSLORequestDeletingSession(self):
        """
        Tests the process_slo method of the OneLogin_Saml2_Auth class
        Case Valid Logout Request, validating that the local session is deleted,
        a LogoutResponse is created and a redirection executed
        """
        settings_info = self.loadSettingsJSON()
        request_data = self.get_request()
        message = self.file_contents(
            join(self.data_path, 'logout_requests',
                 'logout_request_deflated.xml.base64'))
        # In order to avoid the destination problem
        plain_message = OneLogin_Saml2_Utils.decode_base64_and_inflate(message)
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        plain_message = plain_message.replace(
            'http://stuff.com/endpoints/endpoints/sls.php', current_url)
        message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message)
        request_data['get_data']['SAMLRequest'] = message
        # FIXME
        # if (!isset($_SESSION)) {
        #     $_SESSION = array();
        # }
        # $_SESSION['samltest'] = true;
        auth = OneLogin_Saml2_Auth(request_data, old_settings=settings_info)

        auth.set_strict(True)
        target_url = auth.process_slo(True)
        parsed_query = parse_qs(urlparse(target_url)[4])
        slo_url = settings_info['idp']['singleLogoutService']['url']
        self.assertIn(slo_url, target_url)
        self.assertIn('SAMLResponse', parsed_query)
        self.assertNotIn('RelayState', parsed_query)

        # FIXME // Session is not alive
        # $this->assertFalse(isset($_SESSION['samltest']));

        # $_SESSION['samltest'] = true;

        auth.set_strict(True)
        target_url_2 = auth.process_slo(True)
        target_url_2 = auth.process_slo(True)
        parsed_query_2 = parse_qs(urlparse(target_url_2)[4])
        slo_url = settings_info['idp']['singleLogoutService']['url']
        self.assertIn(slo_url, target_url_2)
        self.assertIn('SAMLResponse', parsed_query_2)
        self.assertNotIn('RelayState', parsed_query_2)
Beispiel #37
0
    def testProcessSLORequestNotOnOrAfterFailed(self):
        """
        Tests the process_slo method of the OneLogin_Saml2_Auth class
        Case Logout Request NotOnOrAfter failed
        """
        request_data = self.get_request()
        message = self.file_contents(join(self.data_path, 'logout_requests', 'invalids', 'not_after_failed.xml.base64'))
        # In order to avoid the destination problem
        plain_message = OneLogin_Saml2_Utils.decode_base64_and_inflate(message)
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url)
        message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message)
        request_data['get_data']['SAMLRequest'] = message
        auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON())

        auth.set_strict(True)
        auth.process_slo(True)
        self.assertEqual(auth.get_errors(), ['invalid_logout_request'])
Beispiel #38
0
    def testProcessSLORequestNotOnOrAfterFailed(self):
        """
        Tests the process_slo method of the OneLogin_Saml2_Auth class
        Case Logout Request NotOnOrAfter failed
        """
        request_data = self.get_request()
        message = self.file_contents(join(self.data_path, 'logout_requests', 'invalids', 'not_after_failed.xml.base64'))
        # In order to avoid the destination problem
        plain_message = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(message))
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url)
        message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message)
        request_data['get_data']['SAMLRequest'] = message
        auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON())

        auth.set_strict(True)
        auth.process_slo(True)
        self.assertEqual(auth.get_errors(), ['invalid_logout_request'])
Beispiel #39
0
    def testProcessSLORequestDeletingSession(self):
        """
        Tests the process_slo method of the OneLogin_Saml2_Auth class
        Case Valid Logout Request, validating that the local session is deleted,
        a LogoutResponse is created and a redirection executed
        """
        settings_info = self.loadSettingsJSON()
        request_data = self.get_request()
        message = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request_deflated.xml.base64'))
        # In order to avoid the destination problem
        plain_message = OneLogin_Saml2_Utils.decode_base64_and_inflate(message)
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url)
        message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message)
        request_data['get_data']['SAMLRequest'] = message
        # FIXME
        # if (!isset($_SESSION)) {
        #     $_SESSION = array();
        # }
        # $_SESSION['samltest'] = true;
        auth = OneLogin_Saml2_Auth(request_data, old_settings=settings_info)

        auth.set_strict(True)
        target_url = auth.process_slo(True)
        parsed_query = parse_qs(urlparse(target_url)[4])
        slo_url = settings_info['idp']['singleLogoutService']['url']
        self.assertIn(slo_url, target_url)
        self.assertIn('SAMLResponse', parsed_query)
        self.assertNotIn('RelayState', parsed_query)

        # FIXME // Session is not alive
        # $this->assertFalse(isset($_SESSION['samltest']));

        # $_SESSION['samltest'] = true;

        auth.set_strict(True)
        target_url_2 = auth.process_slo(True)
        target_url_2 = auth.process_slo(True)
        parsed_query_2 = parse_qs(urlparse(target_url_2)[4])
        slo_url = settings_info['idp']['singleLogoutService']['url']
        self.assertIn(slo_url, target_url_2)
        self.assertIn('SAMLResponse', parsed_query_2)
        self.assertNotIn('RelayState', parsed_query_2)
    def testIsInvalidIssuer(self):
        """
        Tests the is_valid method of the OneLogin_Saml2_LogoutRequest
        Case Invalid Issuer
        """
        request = self.file_contents(join(self.data_path, 'logout_requests', 'invalids', 'invalid_issuer.xml'))
        request_data = {
            'http_host': 'example.com',
            'script_name': 'index.html'
        }
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        request = request.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url)
        settings = OneLogin_Saml2_Settings(self.loadSettingsJSON())
        logout_request = OneLogin_Saml2_Logout_Request(settings, b64encode(request))
        self.assertTrue(logout_request.is_valid(request_data))

        settings.set_strict(True)
        logout_request2 = OneLogin_Saml2_Logout_Request(settings, b64encode(request))
        self.assertFalse(logout_request2.is_valid(request_data))
        self.assertIn('Invalid issuer in the Logout Request', logout_request2.get_error())
Beispiel #41
0
    def testProcessSLOResponseNoSucess(self):
        """
        Tests the process_slo method of the OneLogin_Saml2_Auth class
        Case Logout Response not sucess
        """
        request_data = self.get_request()
        message = self.file_contents(
            join(self.data_path, "logout_responses", "invalids", "status_code_responder.xml.base64")
        )
        # In order to avoid the destination problem
        plain_message = OneLogin_Saml2_Utils.decode_base64_and_inflate(message)
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        plain_message = plain_message.replace("http://stuff.com/endpoints/endpoints/sls.php", current_url)
        message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message)
        request_data["get_data"]["SAMLResponse"] = message
        auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON())

        auth.set_strict(True)
        auth.process_slo(True)
        self.assertEqual(auth.get_errors(), ["logout_not_success"])
    def testIsInvalidNotOnOrAfter(self):
        """
        Tests the is_valid method of the OneLogin_Saml2_LogoutRequest
        Case Invalid NotOnOrAfter
        """
        request_data = {
            'http_host': 'example.com',
            'script_name': 'index.html'
        }
        request = self.file_contents(join(self.data_path, 'logout_requests', 'invalids', 'not_after_failed.xml'))
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        request = request.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url)
        settings = OneLogin_Saml2_Settings(self.loadSettingsJSON())
        self.assertTrue(OneLogin_Saml2_Logout_Request.is_valid(settings, request, request_data))

        settings.set_strict(True)
        try:
            valid = OneLogin_Saml2_Logout_Request.is_valid(settings, request, request_data)
            self.assertFalse(valid)
        except Exception as e:
            self.assertIn('Timing issues (please check your clock settings)', e.message)
    def testIsInvalidNotOnOrAfter(self):
        """
        Tests the is_valid method of the OneLogin_Saml2_LogoutRequest
        Case Invalid NotOnOrAfter
        """
        request_data = {
            'http_host': 'example.com',
            'script_name': 'index.html'
        }
        request = self.file_contents(join(self.data_path, 'logout_requests', 'invalids', 'not_after_failed.xml'))
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        request = request.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url)
        settings = OneLogin_Saml2_Settings(self.loadSettingsJSON())

        logout_request = OneLogin_Saml2_Logout_Request(settings, b64encode(request))
        self.assertTrue(logout_request.is_valid(request_data))

        settings.set_strict(True)
        logout_request2 = OneLogin_Saml2_Logout_Request(settings, b64encode(request))
        self.assertFalse(logout_request2.is_valid(request_data))
        self.assertIn('Could not validate timestamp: expired. Check system clock.', logout_request2.get_error())
Beispiel #44
0
    def testProcessSLOResponseRequestId(self):
        """
        Tests the process_slo method of the OneLogin_Saml2_Auth class
        Case Logout Response with valid and invalid Request ID
        """
        request_data = self.get_request()
        message = self.file_contents(join(self.data_path, 'logout_responses', 'logout_response_deflated.xml.base64'))
        # In order to avoid the destination problem
        plain_message = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(message))
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url)
        message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message)
        request_data['get_data']['SAMLResponse'] = message
        auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON())

        request_id = 'wrongID'
        auth.set_strict(True)
        auth.process_slo(True, request_id)
        self.assertEqual(auth.get_errors(), ['invalid_logout_response'])

        request_id = 'ONELOGIN_21584ccdfaca36a145ae990442dcd96bfe60151e'
        auth.process_slo(True, request_id)
        self.assertEqual(len(auth.get_errors()), 0)
Beispiel #45
0
    def logout(self, return_to=None, name_id=None, session_index=None):
        """
        Initiates the SLO process.

        :param return_to: Optional argument. The target URL the user should be redirected to after logout.
        :type return_to: string

        :param name_id: The NameID that will be set in the LogoutRequest.
        :type name_id: string

        :param session_index: SessionIndex that identifies the session of the user.
        :type session_index: string

        :returns: Redirection url
        """
        slo_url = self.get_slo_url()
        if slo_url is None:
            raise OneLogin_Saml2_Error(
                "The IdP does not support Single Log Out", OneLogin_Saml2_Error.SAML_SINGLE_LOGOUT_NOT_SUPPORTED
            )

        if name_id is None and self.__nameid is not None:
            name_id = self.__nameid

        logout_request = OneLogin_Saml2_Logout_Request(self.__settings, name_id=name_id, session_index=session_index)

        parameters = {"SAMLRequest": logout_request.get_request()}
        if return_to is not None:
            parameters["RelayState"] = return_to
        else:
            parameters["RelayState"] = OneLogin_Saml2_Utils.get_self_url_no_query(self.__request_data)

        security = self.__settings.get_security_data()
        if security.get("logoutRequestSigned", False):
            self.add_request_signature(parameters, security["signatureAlgorithm"])
        return self.redirect_to(slo_url, parameters)
    def is_valid(self, request_data):
        """
        Checks if the Logout Request received is valid
        :param request_data: Request Data
        :type request_data: dict

        :return: If the Logout Request is or not valid
        :rtype: boolean
        """
        self.__error = None
        lowercase_urlencoding = False
        try:
            dom = fromstring(self.__logout_request)

            idp_data = self.__settings.get_idp_data()
            idp_entity_id = idp_data['entityId']

            if 'get_data' in request_data.keys():
                get_data = request_data['get_data']
            else:
                get_data = {}

            if 'lowercase_urlencoding' in request_data.keys():
                lowercase_urlencoding = request_data['lowercase_urlencoding']

            if self.__settings.is_strict():
                res = OneLogin_Saml2_Utils.validate_xml(dom, 'saml-schema-protocol-2.0.xsd', self.__settings.is_debug_active())
                if not isinstance(res, Document):
                    raise Exception('Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd')

                security = self.__settings.get_security_data()

                current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)

                # Check NotOnOrAfter
                if dom.get('NotOnOrAfter', None):
                    na = OneLogin_Saml2_Utils.parse_SAML_to_time(dom.get('NotOnOrAfter'))
                    if na <= OneLogin_Saml2_Utils.now():
                        raise Exception('Timing issues (please check your clock settings)')

                # Check destination
                if dom.get('Destination', None):
                    destination = dom.get('Destination')
                    if destination != '':
                        if current_url not in destination:
                            raise Exception(
                                'The LogoutRequest was received at '
                                '%(currentURL)s instead of %(destination)s' %
                                {
                                    'currentURL': current_url,
                                    'destination': destination,
                                }
                            )

                # Check issuer
                issuer = OneLogin_Saml2_Logout_Request.get_issuer(dom)
                if issuer is not None and issuer != idp_entity_id:
                    raise Exception('Invalid issuer in the Logout Request')

                if security['wantMessagesSigned']:
                    if 'Signature' not in get_data:
                        raise Exception('The Message of the Logout Request is not signed and the SP require it')

            if 'Signature' in get_data:
                if 'SigAlg' not in get_data:
                    sign_alg = OneLogin_Saml2_Constants.RSA_SHA1
                else:
                    sign_alg = get_data['SigAlg']

                signed_query = 'SAMLRequest=%s' % OneLogin_Saml2_Utils.get_encoded_parameter(get_data, 'SAMLRequest', lowercase_urlencoding=lowercase_urlencoding)
                if 'RelayState' in get_data:
                    signed_query = '%s&RelayState=%s' % (signed_query, OneLogin_Saml2_Utils.get_encoded_parameter(get_data, 'RelayState', lowercase_urlencoding=lowercase_urlencoding))
                signed_query = '%s&SigAlg=%s' % (signed_query, OneLogin_Saml2_Utils.get_encoded_parameter(get_data, 'SigAlg', OneLogin_Saml2_Constants.RSA_SHA1, lowercase_urlencoding=lowercase_urlencoding))

                if 'x509cert' not in idp_data or idp_data['x509cert'] is None:
                    raise Exception('In order to validate the sign on the Logout Request, the x509cert of the IdP is required')
                cert = idp_data['x509cert']

                if not OneLogin_Saml2_Utils.validate_binary_sign(signed_query, b64decode(get_data['Signature']), cert, sign_alg):
                    raise Exception('Signature validation failed. Logout Request rejected')

            return True
        except Exception as err:
            # pylint: disable=R0801sign_alg
            self.__error = err.__str__()
            debug = self.__settings.is_debug_active()
            if debug:
                print err.__str__()
            return False
Beispiel #47
0
    def is_valid(self, request_data, request_id=None, raise_exceptions=False):
        """
        Validates the response object.

        :param request_data: Request Data
        :type request_data: dict

        :param request_id: Optional argument. The ID of the AuthNRequest sent by this SP to the IdP
        :type request_id: string

        :param raise_exceptions: Whether to return false on failure or raise an exception
        :type raise_exceptions: Boolean

        :returns: True if the SAML Response is valid, False if not
        :rtype: bool
        """
        self.__error = None
        try:
            # Checks SAML version
            if self.document.get('Version', None) != '2.0':
                raise OneLogin_Saml2_ValidationError(
                    'Unsupported SAML version',
                    OneLogin_Saml2_ValidationError.UNSUPPORTED_SAML_VERSION
                )

            # Checks that ID exists
            if self.document.get('ID', None) is None:
                raise OneLogin_Saml2_ValidationError(
                    'Missing ID attribute on SAML Response',
                    OneLogin_Saml2_ValidationError.MISSING_ID
                )

            # Checks that the response only has one assertion
            if not self.validate_num_assertions():
                raise OneLogin_Saml2_ValidationError(
                    'SAML Response must contain 1 assertion',
                    OneLogin_Saml2_ValidationError.WRONG_NUMBER_OF_ASSERTIONS
                )

            # Checks that the response has the SUCCESS status
            self.check_status()

            idp_data = self.__settings.get_idp_data()
            idp_entity_id = idp_data['entityId']
            sp_data = self.__settings.get_sp_data()
            sp_entity_id = sp_data['entityId']

            signed_elements = self.process_signed_elements()

            has_signed_response = '{%s}Response' % OneLogin_Saml2_Constants.NS_SAMLP in signed_elements
            has_signed_assertion = '{%s}Assertion' % OneLogin_Saml2_Constants.NS_SAML in signed_elements

            if self.__settings.is_strict():
                no_valid_xml_msg = 'Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd'
                res = OneLogin_Saml2_XML.validate_xml(self.document, 'saml-schema-protocol-2.0.xsd', self.__settings.is_debug_active())
                if isinstance(res, str):
                    raise OneLogin_Saml2_ValidationError(
                        no_valid_xml_msg,
                        OneLogin_Saml2_ValidationError.INVALID_XML_FORMAT
                    )

                # If encrypted, check also the decrypted document
                if self.encrypted:
                    res = OneLogin_Saml2_XML.validate_xml(self.decrypted_document, 'saml-schema-protocol-2.0.xsd', self.__settings.is_debug_active())
                    if isinstance(res, str):
                        raise OneLogin_Saml2_ValidationError(
                            no_valid_xml_msg,
                            OneLogin_Saml2_ValidationError.INVALID_XML_FORMAT
                        )

                security = self.__settings.get_security_data()
                current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)

                # Check if the InResponseTo of the Response matchs the ID of the AuthNRequest (requestId) if provided
                in_response_to = self.document.get('InResponseTo', None)
                if in_response_to is not None and request_id is not None:
                    if in_response_to != request_id:
                        raise OneLogin_Saml2_ValidationError(
                            'The InResponseTo of the Response: %s, does not match the ID of the AuthNRequest sent by the SP: %s' % (in_response_to, request_id),
                            OneLogin_Saml2_ValidationError.WRONG_INRESPONSETO
                        )

                if not self.encrypted and security['wantAssertionsEncrypted']:
                    raise OneLogin_Saml2_ValidationError(
                        'The assertion of the Response is not encrypted and the SP require it',
                        OneLogin_Saml2_ValidationError.NO_ENCRYPTED_ASSERTION
                    )

                if security['wantNameIdEncrypted']:
                    encrypted_nameid_nodes = self.__query_assertion('/saml:Subject/saml:EncryptedID/xenc:EncryptedData')
                    if len(encrypted_nameid_nodes) != 1:
                        raise OneLogin_Saml2_ValidationError(
                            'The NameID of the Response is not encrypted and the SP require it',
                            OneLogin_Saml2_ValidationError.NO_ENCRYPTED_NAMEID
                        )

                # Checks that a Conditions element exists
                if not self.check_one_condition():
                    raise OneLogin_Saml2_ValidationError(
                        'The Assertion must include a Conditions element',
                        OneLogin_Saml2_ValidationError.MISSING_CONDITIONS
                    )

                # Validates Assertion timestamps
                self.validate_timestamps(raise_exceptions=True)

                # Checks that an AuthnStatement element exists and is unique
                if not self.check_one_authnstatement():
                    raise OneLogin_Saml2_ValidationError(
                        'The Assertion must include an AuthnStatement element',
                        OneLogin_Saml2_ValidationError.WRONG_NUMBER_OF_AUTHSTATEMENTS
                    )

                # Checks that there is at least one AttributeStatement if required
                attribute_statement_nodes = self.__query_assertion('/saml:AttributeStatement')
                if security.get('wantAttributeStatement', True) and not attribute_statement_nodes:
                    raise OneLogin_Saml2_ValidationError(
                        'There is no AttributeStatement on the Response',
                        OneLogin_Saml2_ValidationError.NO_ATTRIBUTESTATEMENT
                    )

                encrypted_attributes_nodes = self.__query_assertion('/saml:AttributeStatement/saml:EncryptedAttribute')
                if encrypted_attributes_nodes:
                    raise OneLogin_Saml2_ValidationError(
                        'There is an EncryptedAttribute in the Response and this SP not support them',
                        OneLogin_Saml2_ValidationError.ENCRYPTED_ATTRIBUTES
                    )

                # Checks destination
                destination = self.document.get('Destination', None)
                if destination:
                    if not destination.startswith(current_url):
                        # TODO: Review if following lines are required, since we can control the
                        # request_data
                        #  current_url_routed = OneLogin_Saml2_Utils.get_self_routed_url_no_query(request_data)
                        #  if not destination.startswith(current_url_routed):
                        raise OneLogin_Saml2_ValidationError(
                            'The response was received at %s instead of %s' % (current_url, destination),
                            OneLogin_Saml2_ValidationError.WRONG_DESTINATION
                        )
                elif destination == '':
                    raise OneLogin_Saml2_ValidationError(
                        'The response has an empty Destination value',
                        OneLogin_Saml2_ValidationError.EMPTY_DESTINATION
                    )
                # Checks audience
                valid_audiences = self.get_audiences()
                if valid_audiences and sp_entity_id not in valid_audiences:
                    raise OneLogin_Saml2_ValidationError(
                        '%s is not a valid audience for this Response' % sp_entity_id,
                        OneLogin_Saml2_ValidationError.WRONG_AUDIENCE
                    )

                # Checks the issuers
                issuers = self.get_issuers()
                for issuer in issuers:
                    if issuer is None or issuer != idp_entity_id:
                        raise OneLogin_Saml2_ValidationError(
                            'Invalid issuer in the Assertion/Response',
                            OneLogin_Saml2_ValidationError.WRONG_ISSUER
                        )

                # Checks the session Expiration
                session_expiration = self.get_session_not_on_or_after()
                if session_expiration and session_expiration <= OneLogin_Saml2_Utils.now():
                    raise OneLogin_Saml2_ValidationError(
                        'The attributes have expired, based on the SessionNotOnOrAfter of the AttributeStatement of this Response',
                        OneLogin_Saml2_ValidationError.SESSION_EXPIRED
                    )

                # Checks the SubjectConfirmation, at least one SubjectConfirmation must be valid
                any_subject_confirmation = False
                subject_confirmation_nodes = self.__query_assertion('/saml:Subject/saml:SubjectConfirmation')

                for scn in subject_confirmation_nodes:
                    method = scn.get('Method', None)
                    if method and method != OneLogin_Saml2_Constants.CM_BEARER:
                        continue
                    sc_data = scn.find('saml:SubjectConfirmationData', namespaces=OneLogin_Saml2_Constants.NSMAP)
                    if sc_data is None:
                        continue
                    else:
                        irt = sc_data.get('InResponseTo', None)
                        if in_response_to and irt and irt != in_response_to:
                            continue
                        recipient = sc_data.get('Recipient', None)
                        if recipient and current_url not in recipient:
                            continue
                        nooa = sc_data.get('NotOnOrAfter', None)
                        if nooa:
                            parsed_nooa = OneLogin_Saml2_Utils.parse_SAML_to_time(nooa)
                            if parsed_nooa <= OneLogin_Saml2_Utils.now():
                                continue
                        nb = sc_data.get('NotBefore', None)
                        if nb:
                            parsed_nb = OneLogin_Saml2_Utils.parse_SAML_to_time(nb)
                            if parsed_nb > OneLogin_Saml2_Utils.now():
                                continue
                        any_subject_confirmation = True
                        break

                if not any_subject_confirmation:
                    raise OneLogin_Saml2_ValidationError(
                        'A valid SubjectConfirmation was not found on this Response',
                        OneLogin_Saml2_ValidationError.WRONG_SUBJECTCONFIRMATION
                    )

                if security['wantAssertionsSigned'] and not has_signed_assertion:
                    raise OneLogin_Saml2_ValidationError(
                        'The Assertion of the Response is not signed and the SP require it',
                        OneLogin_Saml2_ValidationError.NO_SIGNED_ASSERTION
                    )

                if security['wantMessagesSigned'] and not has_signed_response:
                    raise OneLogin_Saml2_ValidationError(
                        'The Message of the Response is not signed and the SP require it',
                        OneLogin_Saml2_ValidationError.NO_SIGNED_MESSAGE
                    )

            if not signed_elements or (not has_signed_response and not has_signed_assertion):
                raise OneLogin_Saml2_ValidationError(
                    'No Signature found. SAML Response rejected',
                    OneLogin_Saml2_ValidationError.NO_SIGNATURE_FOUND
                )
            else:
                cert = idp_data.get('x509cert', None)
                fingerprint = idp_data.get('certFingerprint', None)
                if fingerprint:
                    fingerprint = OneLogin_Saml2_Utils.format_finger_print(fingerprint)
                fingerprintalg = idp_data.get('certFingerprintAlgorithm', None)

                # If find a Signature on the Response, validates it checking the original response
                if has_signed_response and not OneLogin_Saml2_Utils.validate_sign(self.document, cert, fingerprint, fingerprintalg, xpath=OneLogin_Saml2_Utils.RESPONSE_SIGNATURE_XPATH, raise_exceptions=False):
                    raise OneLogin_Saml2_ValidationError(
                        'Signature validation failed. SAML Response rejected',
                        OneLogin_Saml2_ValidationError.INVALID_SIGNATURE
                    )

                document_check_assertion = self.decrypted_document if self.encrypted else self.document
                if has_signed_assertion and not OneLogin_Saml2_Utils.validate_sign(document_check_assertion, cert, fingerprint, fingerprintalg, xpath=OneLogin_Saml2_Utils.ASSERTION_SIGNATURE_XPATH, raise_exceptions=False):
                    raise OneLogin_Saml2_ValidationError(
                        'Signature validation failed. SAML Response rejected',
                        OneLogin_Saml2_ValidationError.INVALID_SIGNATURE
                    )

            return True
        except Exception as err:
            self.__error = str(err)
            debug = self.__settings.is_debug_active()
            if debug:
                print(err)
            if raise_exceptions:
                raise
            return False
Beispiel #48
0
    def is_valid(self, request_data, request_id=None):
        """
        Validates the response object.

        :param request_data: Request Data
        :type request_data: dict

        :param request_id: Optional argument. The ID of the AuthNRequest sent by this SP to the IdP
        :type request_id: string

        :returns: True if the SAML Response is valid, False if not
        :rtype: bool
        """
        self.__error = None
        try:
            # Checks SAML version
            if self.document.get('Version', None) != '2.0':
                raise Exception('Unsupported SAML version')

            # Checks that ID exists
            if self.document.get('ID', None) is None:
                raise Exception('Missing ID attribute on SAML Response')

            # Checks that the response only has one assertion
            if not self.validate_num_assertions():
                raise Exception('SAML Response must contain 1 assertion')

            # Checks that the response has the SUCCESS status
            self.check_status()

            idp_data = self.__settings.get_idp_data()
            idp_entity_id = idp_data.get('entityId', '')
            sp_data = self.__settings.get_sp_data()
            sp_entity_id = sp_data.get('entityId', '')

            signed_elements = self.process_signed_elements()

            if self.__settings.is_strict():
                no_valid_xml_msg = 'Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd'
                res = OneLogin_Saml2_Utils.validate_xml(etree.tostring(self.document), 'saml-schema-protocol-2.0.xsd', self.__settings.is_debug_active())
                if not isinstance(res, Document):
                    raise Exception(no_valid_xml_msg)

                # If encrypted, check also the decrypted document
                if self.encrypted:
                    res = OneLogin_Saml2_Utils.validate_xml(etree.tostring(self.decrypted_document), 'saml-schema-protocol-2.0.xsd', self.__settings.is_debug_active())
                    if not isinstance(res, Document):
                        raise Exception(no_valid_xml_msg)

                security = self.__settings.get_security_data()
                current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)

                # Check if the InResponseTo of the Response matchs the ID of the AuthNRequest (requestId) if provided
                in_response_to = self.document.get('InResponseTo', None)
                if in_response_to and request_id:
                    if in_response_to != request_id:
                        raise Exception('The InResponseTo of the Response: %s, does not match the ID of the AuthNRequest sent by the SP: %s' % (in_response_to, request_id))

                if not self.encrypted and security.get('wantAssertionsEncrypted', False):
                    raise Exception('The assertion of the Response is not encrypted and the SP require it')

                if security.get('wantNameIdEncrypted', False):
                    encrypted_nameid_nodes = self.__query_assertion('/saml:Subject/saml:EncryptedID/xenc:EncryptedData')
                    if len(encrypted_nameid_nodes) == 0:
                        raise Exception('The NameID of the Response is not encrypted and the SP require it')

                # Checks that there is at least one AttributeStatement if required
                attribute_statement_nodes = self.__query_assertion('/saml:AttributeStatement')
                if security.get('wantAttributeStatement', True) and not attribute_statement_nodes:
                    raise Exception('There is no AttributeStatement on the Response')

                # Validates Assertion timestamps
                if not self.validate_timestamps():
                    raise Exception('Timing issues (please check your clock settings)')

                encrypted_attributes_nodes = self.__query_assertion('/saml:AttributeStatement/saml:EncryptedAttribute')
                if encrypted_attributes_nodes:
                    raise Exception('There is an EncryptedAttribute in the Response and this SP not support them')

                # Checks destination
                destination = self.document.get('Destination', '')
                if destination:
                    if not destination.startswith(current_url):
                        # TODO: Review if following lines are required, since we can control the
                        # request_data
                        #  current_url_routed = OneLogin_Saml2_Utils.get_self_routed_url_no_query(request_data)
                        #  if not destination.startswith(current_url_routed):
                        raise Exception('The response was received at %s instead of %s' % (current_url, destination))

                # Checks audience
                valid_audiences = self.get_audiences()
                if valid_audiences and sp_entity_id not in valid_audiences:
                    raise Exception('%s is not a valid audience for this Response' % sp_entity_id)

                # Checks the issuers
                issuers = self.get_issuers()
                for issuer in issuers:
                    if issuer is None or issuer != idp_entity_id:
                        raise Exception('Invalid issuer in the Assertion/Response')

                # Checks the session Expiration
                session_expiration = self.get_session_not_on_or_after()
                if session_expiration and session_expiration <= OneLogin_Saml2_Utils.now():
                    raise Exception('The attributes have expired, based on the SessionNotOnOrAfter of the AttributeStatement of this Response')

                # Checks the SubjectConfirmation, at least one SubjectConfirmation must be valid
                any_subject_confirmation = False
                subject_confirmation_nodes = self.__query_assertion('/saml:Subject/saml:SubjectConfirmation')

                for scn in subject_confirmation_nodes:
                    method = scn.get('Method', None)
                    if method and method != OneLogin_Saml2_Constants.CM_BEARER:
                        continue
                    sc_data = scn.find('saml:SubjectConfirmationData', namespaces=OneLogin_Saml2_Constants.NSMAP)
                    if sc_data is None:
                        continue
                    else:
                        irt = sc_data.get('InResponseTo', None)
                        if in_response_to and irt and irt != in_response_to:
                            continue
                        recipient = sc_data.get('Recipient', None)
                        if recipient and current_url not in recipient:
                            continue
                        nooa = sc_data.get('NotOnOrAfter', None)
                        if nooa:
                            parsed_nooa = OneLogin_Saml2_Utils.parse_SAML_to_time(nooa)
                            if parsed_nooa <= OneLogin_Saml2_Utils.now():
                                continue
                        nb = sc_data.get('NotBefore', None)
                        if nb:
                            parsed_nb = OneLogin_Saml2_Utils.parse_SAML_to_time(nb)
                            if parsed_nb > OneLogin_Saml2_Utils.now():
                                continue
                        any_subject_confirmation = True
                        break

                if not any_subject_confirmation:
                    raise Exception('A valid SubjectConfirmation was not found on this Response')

                if security.get('wantAssertionsSigned', False) and ('{%s}Assertion' % OneLogin_Saml2_Constants.NS_SAML) not in signed_elements:
                    raise Exception('The Assertion of the Response is not signed and the SP require it')

                if security.get('wantMessagesSigned', False) and ('{%s}Response' % OneLogin_Saml2_Constants.NS_SAMLP) not in signed_elements:
                    raise Exception('The Message of the Response is not signed and the SP require it')

            if len(signed_elements) > 0:
                if len(signed_elements) > 2:
                    raise Exception('Too many Signatures found. SAML Response rejected')
                certs = idp_data.get('x509certs', [])
                fingerprint = idp_data.get('certFingerprint', None)
                fingerprintalg = idp_data.get('certFingerprintAlgorithm', None)

                # If find a Signature on the Response, validates it checking the original response
                if '{%s}Response' % OneLogin_Saml2_Constants.NS_SAMLP in signed_elements:
                    document_to_validate = self.document
                # Otherwise validates the assertion (decrypted assertion if was encrypted)
                else:
                    if self.encrypted:
                        document_to_validate = self.decrypted_document
                    else:
                        document_to_validate = self.document
                if certs:
                    validated = any(OneLogin_Saml2_Utils.validate_sign(document_to_validate, cert, fingerprint, fingerprintalg)
                                    for cert in certs)
                else:
                    validated = OneLogin_Saml2_Utils.validate_sign(document_to_validate, None, fingerprint, fingerprintalg)
                if not validated:
                    raise Exception('Signature validation failed. SAML Response rejected')
            else:
                raise Exception('No Signature found. SAML Response rejected')

            return True
        except Exception as err:
            self.__error = err.__str__()
            debug = self.__settings.is_debug_active()
            if debug:
                print err.__str__()
            return False
    def is_valid(self, request_data):
        """
        Checks if the Logout Request recieved is valid
        :param request_data: Request Data
        :type request_data: dict

        :return: If the Logout Request is or not valid
        :rtype: boolean
        """
        self.__error = None
        try:
            root = OneLogin_Saml2_XML.to_etree(self.__logout_request)

            idp_data = self.__settings.get_idp_data()
            idp_entity_id = idp_data['entityId']

            get_data = ('get_data' in request_data and request_data['get_data']) or dict()

            if self.__settings.is_strict():
                res = OneLogin_Saml2_XML.validate_xml(root, 'saml-schema-protocol-2.0.xsd', self.__settings.is_debug_active())
                if isinstance(res, str):
                    raise Exception('Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd')

                security = self.__settings.get_security_data()

                current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)

                # Check NotOnOrAfter
                if root.get('NotOnOrAfter', None):
                    na = OneLogin_Saml2_Utils.parse_SAML_to_time(root.get('NotOnOrAfter'))
                    if na <= OneLogin_Saml2_Utils.now():
                        raise Exception('Timing issues (please check your clock settings)')

                # Check destination
                if root.get('Destination', None):
                    destination = root.get('Destination')
                    if destination != '':
                        if current_url not in destination:
                            raise Exception(
                                'The LogoutRequest was received at '
                                '%(currentURL)s instead of %(destination)s' %
                                {
                                    'currentURL': current_url,
                                    'destination': destination,
                                }
                            )

                # Check issuer
                issuer = OneLogin_Saml2_Logout_Request.get_issuer(root)
                if issuer is not None and issuer != idp_entity_id:
                    raise Exception('Invalid issuer in the Logout Request')

                if security['wantMessagesSigned']:
                    if 'Signature' not in get_data:
                        raise Exception('The Message of the Logout Request is not signed and the SP require it')
            return True
        except Exception as err:
            # pylint: disable=R0801
            self.__error = str(err)
            debug = self.__settings.is_debug_active()
            if debug:
                print(err)
            return False
    def testIsInValidSign(self):
        """
        Tests the is_valid method of the OneLogin_Saml2_LogoutResponse
        """
        request_data = {
            'http_host': 'example.com',
            'script_name': 'index.html',
            'get_data': {}
        }
        settings = OneLogin_Saml2_Settings(self.loadSettingsJSON())

        settings.set_strict(False)
        request_data['get_data'] = {
            'SAMLResponse': 'fZJva8IwEMa/Ssl7TZrW/gnqGHMMwSlM8cXeyLU9NaxNQi9lfvxVZczB5ptwSe733MPdjQma2qmFPdjOvyE5awiDU1MbUpevCetaoyyQJmWgQVK+VOvH14WSQ6Fca70tbc1ukPsEEGHrtTUsmM8mbDfKUhnFci8gliGINI/yXIAAiYnsw6JIRgWWAKlkwRZb6skJ64V6nKjDuSEPxvdPIowHIhpIsQkTFaYqSt9ZMEPy2oC/UEfvHSnOnfZFV38MjR1oN7TtgRv8tAZre9CGV9jYkGtT4Wnoju6Bauprme/ebOyErZbPi9XLfLnDoohwhHGc5WVSVhjCKM6rBMpYQpWJrIizfZ4IZNPxuTPqYrmd/m+EdONqPOfy8yG5rhxv0EMFHs52xvxWaHyd3tqD7+j37clWGGyh7vD+POiSrdZdWSIR49NrhR9R/teGTL8A',
            'RelayState': 'https://pitbulk.no-ip.org/newonelogin/demo1/index.php',
            'SigAlg': 'http://www.w3.org/2000/09/xmldsig#rsa-sha1',
            'Signature': 'vfWbbc47PkP3ejx4bjKsRX7lo9Ml1WRoE5J5owF/0mnyKHfSY6XbhO1wwjBV5vWdrUVX+xp6slHyAf4YoAsXFS0qhan6txDiZY4Oec6yE+l10iZbzvie06I4GPak4QrQ4gAyXOSzwCrRmJu4gnpeUxZ6IqKtdrKfAYRAcVfNKGA='
        }
        response = OneLogin_Saml2_Logout_Response(settings, request_data['get_data']['SAMLResponse'])
        self.assertTrue(response.is_valid(request_data))

        relayState = request_data['get_data']['RelayState']
        del request_data['get_data']['RelayState']
        inv_response = OneLogin_Saml2_Logout_Response(settings, request_data['get_data']['SAMLResponse'])
        self.assertFalse(inv_response.is_valid(request_data))
        request_data['get_data']['RelayState'] = relayState

        settings.set_strict(True)
        response_2 = OneLogin_Saml2_Logout_Response(settings, request_data['get_data']['SAMLResponse'])
        self.assertFalse(response_2.is_valid(request_data))
        self.assertIn('Invalid issuer in the Logout Response', response_2.get_error())

        settings.set_strict(False)
        old_signature = request_data['get_data']['Signature']
        request_data['get_data']['Signature'] = 'vfWbbc47PkP3ejx4bjKsRX7lo9Ml1WRoE5J5owF/0mnyKHfSY6XbhO1wwjBV5vWdrUVX+xp6slHyAf4YoAsXFS0qhan6txDiZY4Oec6yE+l10iZbzvie06I4GPak4QrQ4gAyXOSzwCrRmJu4gnpeUxZ6IqKtdrKfAYRAcVf3333='
        response_3 = OneLogin_Saml2_Logout_Response(settings, request_data['get_data']['SAMLResponse'])
        self.assertFalse(response_3.is_valid(request_data))
        self.assertIn('Signature validation failed. Logout Response rejected', response_3.get_error())

        request_data['get_data']['Signature'] = old_signature
        old_signature_algorithm = request_data['get_data']['SigAlg']
        del request_data['get_data']['SigAlg']
        response_4 = OneLogin_Saml2_Logout_Response(settings, request_data['get_data']['SAMLResponse'])
        self.assertTrue(response_4.is_valid(request_data))

        request_data['get_data']['RelayState'] = 'http://example.com/relaystate'
        response_5 = OneLogin_Saml2_Logout_Response(settings, request_data['get_data']['SAMLResponse'])
        self.assertFalse(response_5.is_valid(request_data))
        self.assertIn('Signature validation failed. Logout Response rejected', response_5.get_error())

        settings.set_strict(True)
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        plain_message_6 = OneLogin_Saml2_Utils.decode_base64_and_inflate(request_data['get_data']['SAMLResponse'])
        plain_message_6 = plain_message_6.replace('https://pitbulk.no-ip.org/newonelogin/demo1/index.php?sls', current_url)
        plain_message_6 = plain_message_6.replace('https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php', 'http://idp.example.com/')
        request_data['get_data']['SAMLResponse'] = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message_6)

        response_6 = OneLogin_Saml2_Logout_Response(settings, request_data['get_data']['SAMLResponse'])
        self.assertFalse(response_6.is_valid(request_data))
        self.assertIn('Signature validation failed. Logout Response rejected', response_6.get_error())

        settings.set_strict(False)
        response_7 = OneLogin_Saml2_Logout_Response(settings, request_data['get_data']['SAMLResponse'])
        self.assertFalse(response_7.is_valid(request_data))
        self.assertIn('Signature validation failed. Logout Response rejected', response_7.get_error())

        request_data['get_data']['SigAlg'] = 'http://www.w3.org/2000/09/xmldsig#dsa-sha1'
        response_8 = OneLogin_Saml2_Logout_Response(settings, request_data['get_data']['SAMLResponse'])
        self.assertFalse(response_8.is_valid(request_data))
        self.assertIn('Signature validation failed. Logout Response rejected', response_8.get_error())

        settings_info = self.loadSettingsJSON()
        settings_info['strict'] = True
        settings_info['security']['wantMessagesSigned'] = True
        settings = OneLogin_Saml2_Settings(settings_info)

        request_data['get_data']['SigAlg'] = old_signature_algorithm
        old_signature = request_data['get_data']['Signature']
        del request_data['get_data']['Signature']
        request_data['get_data']['SAMLResponse'] = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message_6)
        response_9 = OneLogin_Saml2_Logout_Response(settings, request_data['get_data']['SAMLResponse'])
        self.assertFalse(response_9.is_valid(request_data))
        self.assertIn('The Message of the Logout Response is not signed and the SP require it', response_9.get_error())

        request_data['get_data']['Signature'] = old_signature
        settings_info['idp']['certFingerprint'] = 'afe71c28ef740bc87425be13a2263d37971da1f9'
        del settings_info['idp']['x509cert']
        settings_2 = OneLogin_Saml2_Settings(settings_info)

        response_10 = OneLogin_Saml2_Logout_Response(settings_2, request_data['get_data']['SAMLResponse'])
        self.assertFalse(response_10.is_valid(request_data))
        self.assertIn('In order to validate the sign on the Logout Response, the x509cert of the IdP is required', response_10.get_error())
Beispiel #51
0
    def is_valid(self, request_data, raise_exceptions=False):
        """
        Checks if the Logout Request received is valid
        :param request_data: Request Data
        :type request_data: dict

        :param raise_exceptions: Whether to return false on failure or raise an exception
        :type raise_exceptions: Boolean

        :return: If the Logout Request is or not valid
        :rtype: boolean
        """
        self.__error = None
        try:
            root = OneLogin_Saml2_XML.to_etree(self.__logout_request)

            idp_data = self.__settings.get_idp_data()
            idp_entity_id = idp_data['entityId']

            get_data = ('get_data' in request_data and request_data['get_data']) or dict()

            if self.__settings.is_strict():
                res = OneLogin_Saml2_XML.validate_xml(root, 'saml-schema-protocol-2.0.xsd', self.__settings.is_debug_active())
                if isinstance(res, str):
                    raise OneLogin_Saml2_ValidationError(
                        'Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd',
                        OneLogin_Saml2_ValidationError.INVALID_XML_FORMAT
                    )

                security = self.__settings.get_security_data()

                current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)

                # Check NotOnOrAfter
                if root.get('NotOnOrAfter', None):
                    na = OneLogin_Saml2_Utils.parse_SAML_to_time(root.get('NotOnOrAfter'))
                    if na <= OneLogin_Saml2_Utils.now():
                        raise OneLogin_Saml2_ValidationError(
                            'Could not validate timestamp: expired. Check system clock.)',
                            OneLogin_Saml2_ValidationError.RESPONSE_EXPIRED
                        )

                # Check destination
                if root.get('Destination', None):
                    destination = root.get('Destination')
                    if destination != '':
                        if current_url not in destination:
                            raise OneLogin_Saml2_ValidationError(
                                'The LogoutRequest was received at '
                                '%(currentURL)s instead of %(destination)s' %
                                {
                                    'currentURL': current_url,
                                    'destination': destination,
                                },
                                OneLogin_Saml2_ValidationError.WRONG_DESTINATION
                            )

                # Check issuer
                issuer = OneLogin_Saml2_Logout_Request.get_issuer(root)
                if issuer is not None and issuer != idp_entity_id:
                    raise OneLogin_Saml2_ValidationError(
                        'Invalid issuer in the Logout Request (expected %(idpEntityId)s, got %(issuer)s)' %
                        {
                            'idpEntityId': idp_entity_id,
                            'issuer': issuer
                        },
                        OneLogin_Saml2_ValidationError.WRONG_ISSUER
                    )

                if security['wantMessagesSigned']:
                    if 'Signature' not in get_data:
                        raise OneLogin_Saml2_ValidationError(
                            'The Message of the Logout Request is not signed and the SP require it',
                            OneLogin_Saml2_ValidationError.NO_SIGNED_MESSAGE
                        )

            return True
        except Exception as err:
            # pylint: disable=R0801
            self.__error = str(err)
            debug = self.__settings.is_debug_active()
            if debug:
                print(err)
            if raise_exceptions:
                raise
            return False
Beispiel #52
0
    def is_valid(self, request_data):
        """
        Checks if the Logout Request recieved is valid
        :param request_data: Request Data
        :type request_data: dict

        :return: If the Logout Request is or not valid
        :rtype: boolean
        """
        self.__error = None
        try:
            dom = fromstring(self.__logout_request)

            idp_data = self.__settings.get_idp_data()
            idp_entity_id = idp_data['entityId']

            if 'get_data' in request_data.keys():
                get_data = request_data['get_data']
            else:
                get_data = {}

            if self.__settings.is_strict():
                res = OneLogin_Saml2_Utils.validate_xml(
                    dom, 'saml-schema-protocol-2.0.xsd',
                    self.__settings.is_debug_active())
                if not isinstance(res, Document):
                    raise Exception(
                        'Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd'
                    )

                security = self.__settings.get_security_data()

                current_url = OneLogin_Saml2_Utils.get_self_url_no_query(
                    request_data)

                # Check NotOnOrAfter
                if dom.get('NotOnOrAfter', None):
                    na = OneLogin_Saml2_Utils.parse_SAML_to_time(
                        dom.get('NotOnOrAfter'))
                    if na <= OneLogin_Saml2_Utils.now():
                        raise Exception(
                            'Timing issues (please check your clock settings)')

                # Check destination
                if dom.get('Destination', None):
                    destination = dom.get('Destination')
                    if destination != '':
                        if current_url not in destination:
                            raise Exception(
                                'The LogoutRequest was received at '
                                '%(currentURL)s instead of %(destination)s' % {
                                    'currentURL': current_url,
                                    'destination': destination,
                                })

                # Check issuer
                issuer = OneLogin_Saml2_Logout_Request.get_issuer(dom)
                if issuer is not None and issuer != idp_entity_id:
                    raise Exception('Invalid issuer in the Logout Request')

                if security['wantMessagesSigned']:
                    if 'Signature' not in get_data:
                        raise Exception(
                            'The Message of the Logout Request is not signed and the SP require it'
                        )

            if 'Signature' in get_data:
                if 'SigAlg' not in get_data:
                    sign_alg = OneLogin_Saml2_Constants.RSA_SHA1
                else:
                    sign_alg = get_data['SigAlg']

                signed_query = 'SAMLRequest=%s' % quote_plus(
                    get_data['SAMLRequest'])
                if 'RelayState' in get_data:
                    signed_query = '%s&RelayState=%s' % (
                        signed_query, quote_plus(get_data['RelayState']))
                signed_query = '%s&SigAlg=%s' % (signed_query,
                                                 quote_plus(sign_alg))

                if 'x509cert' not in idp_data or idp_data['x509cert'] is None:
                    raise Exception(
                        'In order to validate the sign on the Logout Request, the x509cert of the IdP is required'
                    )
                cert = idp_data['x509cert']

                if not OneLogin_Saml2_Utils.validate_binary_sign(
                        signed_query, b64decode(get_data['Signature']), cert,
                        sign_alg):
                    raise Exception(
                        'Signature validation failed. Logout Request rejected')

            return True
        except Exception as err:
            # pylint: disable=R0801sign_alg
            self.__error = err.__str__()
            debug = self.__settings.is_debug_active()
            if debug:
                print err.__str__()
            return False
    def is_valid(self, request_data, request_id=None, raise_exceptions=False):
        """
        Determines if the SAML LogoutResponse is valid
        :param request_id: The ID of the LogoutRequest sent by this SP to the IdP
        :type request_id: string

        :param raise_exceptions: Whether to return false on failure or raise an exception
        :type raise_exceptions: Boolean

        :return: Returns if the SAML LogoutResponse is or not valid
        :rtype: boolean
        """
        self.__error = None
        try:
            idp_data = self.__settings.get_idp_data()
            idp_entity_id = idp_data['entityId']
            get_data = request_data['get_data']

            if self.__settings.is_strict():
                res = OneLogin_Saml2_XML.validate_xml(
                    self.document, 'saml-schema-protocol-2.0.xsd',
                    self.__settings.is_debug_active())
                if isinstance(res, str):
                    raise OneLogin_Saml2_ValidationError(
                        'Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd',
                        OneLogin_Saml2_ValidationError.INVALID_XML_FORMAT)

                security = self.__settings.get_security_data()

                # Check if the InResponseTo of the Logout Response matches the ID of the Logout Request (requestId) if provided
                in_response_to = self.document.get('InResponseTo', None)
                if request_id is not None and in_response_to and in_response_to != request_id:
                    raise OneLogin_Saml2_ValidationError(
                        'The InResponseTo of the Logout Response: %s, does not match the ID of the Logout request sent by the SP: %s'
                        % (in_response_to, request_id),
                        OneLogin_Saml2_ValidationError.WRONG_INRESPONSETO)

                # Check issuer
                issuer = self.get_issuer()
                if issuer is not None and issuer != idp_entity_id:
                    raise OneLogin_Saml2_ValidationError(
                        'Invalid issuer in the Logout Request',
                        OneLogin_Saml2_ValidationError.WRONG_ISSUER)

                current_url = OneLogin_Saml2_Utils.get_self_url_no_query(
                    request_data)

                # Check destination
                destination = self.document.get('Destination', None)
                if destination and current_url not in destination:
                    raise OneLogin_Saml2_ValidationError(
                        'The LogoutResponse was received at %s instead of %s' %
                        (current_url, destination),
                        OneLogin_Saml2_ValidationError.WRONG_DESTINATION)

                if security['wantMessagesSigned']:
                    if 'Signature' not in get_data:
                        raise OneLogin_Saml2_ValidationError(
                            'The Message of the Logout Response is not signed and the SP require it',
                            OneLogin_Saml2_ValidationError.NO_SIGNED_MESSAGE)
            return True
        # pylint: disable=R0801
        except Exception as err:
            self.__error = str(err)
            debug = self.__settings.is_debug_active()
            if debug:
                print(err)
            if raise_exceptions:
                raise
            return False
    def is_valid(self, request_data):
        """
        Checks if the Logout Request received is valid
        :param request_data: Request Data
        :type request_data: dict

        :return: If the Logout Request is or not valid
        :rtype: boolean
        """
        self.__error = None
        try:
            root = OneLogin_Saml2_XML.to_etree(self.__logout_request)

            idp_data = self.__settings.get_idp_data()
            idp_entity_id = idp_data['entityId']

            get_data = ('get_data' in request_data
                        and request_data['get_data']) or dict()

            if self.__settings.is_strict():
                res = OneLogin_Saml2_XML.validate_xml(
                    root, 'saml-schema-protocol-2.0.xsd',
                    self.__settings.is_debug_active())
                if isinstance(res, str):
                    raise Exception(
                        'Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd'
                    )

                security = self.__settings.get_security_data()

                current_url = OneLogin_Saml2_Utils.get_self_url_no_query(
                    request_data)

                # Check NotOnOrAfter
                if root.get('NotOnOrAfter', None):
                    na = OneLogin_Saml2_Utils.parse_SAML_to_time(
                        root.get('NotOnOrAfter'))
                    if na <= OneLogin_Saml2_Utils.now():
                        raise Exception(
                            'Timing issues (please check your clock settings)')

                # Check destination
                if root.get('Destination', None):
                    destination = root.get('Destination')
                    if destination != '':
                        if current_url not in destination:
                            raise Exception(
                                'The LogoutRequest was received at '
                                '%(currentURL)s instead of %(destination)s' % {
                                    'currentURL': current_url,
                                    'destination': destination,
                                })

                # Check issuer
                issuer = OneLogin_Saml2_Logout_Request.get_issuer(root)
                if issuer is not None and issuer != idp_entity_id:
                    raise Exception('Invalid issuer in the Logout Request')

                if security['wantMessagesSigned']:
                    if 'Signature' not in get_data:
                        raise Exception(
                            'The Message of the Logout Request is not signed and the SP require it'
                        )
            return True
        except Exception as err:
            # pylint: disable=R0801
            self.__error = str(err)
            debug = self.__settings.is_debug_active()
            if debug:
                print(err)
            return False
Beispiel #55
0
    def is_valid(self, request_data, request_id=None):
        """
        Validates the response object.

        :param request_data: Request Data
        :type request_data: dict

        :param request_id: Optional argument. The ID of the AuthNRequest sent by this SP to the IdP
        :type request_id: string

        :returns: True if the SAML Response is valid, False if not
        :rtype: bool
        """
        self.__error = None
        try:
            # Checks SAML version
            if self.document.get('Version', None) != '2.0':
                raise Exception('Unsupported SAML version')

            # Checks that ID exists
            if self.document.get('ID', None) is None:
                raise Exception('Missing ID attribute on SAML Response')

            # Checks that the response only has one assertion
            if not self.validate_num_assertions():
                raise Exception('SAML Response must contain 1 assertion')

            # Checks that the response has the SUCCESS status
            self.check_status()

            idp_data = self.__settings.get_idp_data()
            idp_entity_id = idp_data.get('entityId', '')
            sp_data = self.__settings.get_sp_data()
            sp_entity_id = sp_data.get('entityId', '')

            sign_nodes = self.__query('//ds:Signature')

            signed_elements = []
            for sign_node in sign_nodes:
                signed_elements.append(sign_node.getparent().tag)

            if self.__settings.is_strict():
                res = OneLogin_Saml2_Utils.validate_xml(
                    etree.tostring(self.document),
                    'saml-schema-protocol-2.0.xsd',
                    self.__settings.is_debug_active())
                if not isinstance(res, Document):
                    raise Exception(
                        'Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd'
                    )

                security = self.__settings.get_security_data()
                current_url = OneLogin_Saml2_Utils.get_self_url_no_query(
                    request_data)

                # Check if the InResponseTo of the Response matchs the ID of the AuthNRequest (requestId) if provided
                in_response_to = self.document.get('InResponseTo', None)
                if in_response_to and request_id:
                    if in_response_to != request_id:
                        raise Exception(
                            'The InResponseTo of the Response: %s, does not match the ID of the AuthNRequest sent by the SP: %s'
                            % (in_response_to, request_id))

                if not self.encrypted and security.get(
                        'wantAssertionsEncrypted', False):
                    raise Exception(
                        'The assertion of the Response is not encrypted and the SP require it'
                    )

                if security.get('wantNameIdEncrypted', False):
                    encrypted_nameid_nodes = self.__query_assertion(
                        '/saml:Subject/saml:EncryptedID/xenc:EncryptedData')
                    if len(encrypted_nameid_nodes) == 0:
                        raise Exception(
                            'The NameID of the Response is not encrypted and the SP require it'
                        )

                # Checks that there is at least one AttributeStatement if required
                attribute_statement_nodes = self.__query_assertion(
                    '/saml:AttributeStatement')
                if security.get('wantAttributeStatement',
                                True) and not attribute_statement_nodes:
                    raise Exception(
                        'There is no AttributeStatement on the Response')

                # Validates Assertion timestamps
                if not self.validate_timestamps():
                    raise Exception(
                        'Timing issues (please check your clock settings)')

                encrypted_attributes_nodes = self.__query_assertion(
                    '/saml:AttributeStatement/saml:EncryptedAttribute')
                if encrypted_attributes_nodes:
                    raise Exception(
                        'There is an EncryptedAttribute in the Response and this SP not support them'
                    )

                # Checks destination
                destination = self.document.get('Destination', '')
                if destination:
                    if not destination.startswith(current_url):
                        # TODO: Review if following lines are required, since we can control the
                        # request_data
                        #  current_url_routed = OneLogin_Saml2_Utils.get_self_routed_url_no_query(request_data)
                        #  if not destination.startswith(current_url_routed):
                        raise Exception(
                            'The response was received at %s instead of %s' %
                            (current_url, destination))

                # Checks audience
                valid_audiences = self.get_audiences()
                if valid_audiences and sp_entity_id not in valid_audiences:
                    raise Exception(
                        '%s is not a valid audience for this Response' %
                        sp_entity_id)

                # Checks the issuers
                issuers = self.get_issuers()
                for issuer in issuers:
                    if issuer is None or issuer != idp_entity_id:
                        raise Exception(
                            'Invalid issuer in the Assertion/Response')

                # Checks the session Expiration
                session_expiration = self.get_session_not_on_or_after()
                if session_expiration and session_expiration <= OneLogin_Saml2_Utils.now(
                ):
                    raise Exception(
                        'The attributes have expired, based on the SessionNotOnOrAfter of the AttributeStatement of this Response'
                    )

                # Checks the SubjectConfirmation, at least one SubjectConfirmation must be valid
                any_subject_confirmation = False
                subject_confirmation_nodes = self.__query_assertion(
                    '/saml:Subject/saml:SubjectConfirmation')

                for scn in subject_confirmation_nodes:
                    method = scn.get('Method', None)
                    if method and method != OneLogin_Saml2_Constants.CM_BEARER:
                        continue
                    sc_data = scn.find(
                        'saml:SubjectConfirmationData',
                        namespaces=OneLogin_Saml2_Constants.NSMAP)
                    if sc_data is None:
                        continue
                    else:
                        irt = sc_data.get('InResponseTo', None)
                        if irt != in_response_to:
                            continue
                        recipient = sc_data.get('Recipient', None)
                        if recipient and current_url not in recipient:
                            continue
                        nooa = sc_data.get('NotOnOrAfter', None)
                        if nooa:
                            parsed_nooa = OneLogin_Saml2_Utils.parse_SAML_to_time(
                                nooa)
                            if parsed_nooa <= OneLogin_Saml2_Utils.now():
                                continue
                        nb = sc_data.get('NotBefore', None)
                        if nb:
                            parsed_nb = OneLogin_Saml2_Utils.parse_SAML_to_time(
                                nb)
                            if parsed_nb > OneLogin_Saml2_Utils.now():
                                continue
                        any_subject_confirmation = True
                        break

                if not any_subject_confirmation:
                    raise Exception(
                        'A valid SubjectConfirmation was not found on this Response'
                    )

                if security.get('wantAssertionsSigned', False) and (
                        '{%s}Assertion' % OneLogin_Saml2_Constants.NS_SAML
                ) not in signed_elements:
                    raise Exception(
                        'The Assertion of the Response is not signed and the SP require it'
                    )

                if security.get('wantMessagesSigned', False) and (
                        '{%s}Response' % OneLogin_Saml2_Constants.NS_SAMLP
                ) not in signed_elements:
                    raise Exception(
                        'The Message of the Response is not signed and the SP require it'
                    )

                if len(signed_elements) > 0:
                    if len(signed_elements) > 2:
                        raise Exception(
                            'Too many Signatures found. SAML Response rejected'
                        )
                    cert = idp_data.get('x509cert', None)
                    fingerprint = idp_data.get('certFingerprint', None)
                    fingerprintalg = idp_data.get('certFingerprintAlgorithm',
                                                  None)

                    # If find a Signature on the Response, validates it checking the original response
                    if '{%s}Response' % OneLogin_Saml2_Constants.NS_SAMLP in signed_elements:
                        document_to_validate = self.document
                    # Otherwise validates the assertion (decrypted assertion if was encrypted)
                    else:
                        if self.encrypted:
                            document_to_validate = self.decrypted_document
                        else:
                            document_to_validate = self.document
                    if not OneLogin_Saml2_Utils.validate_sign(
                            document_to_validate, cert, fingerprint,
                            fingerprintalg):
                        raise Exception(
                            'Signature validation failed. SAML Response rejected'
                        )
                else:
                    raise Exception(
                        'No Signature found. SAML Response rejected')

            return True
        except Exception as err:
            self.__error = err.__str__()
            debug = self.__settings.is_debug_active()
            if debug:
                print err.__str__()
            return False
Beispiel #56
0
    def login(self,
              return_to=None,
              force_authn=False,
              is_passive=False,
              set_nameid_policy=True):
        """
        Initiates the SSO process.

        :param return_to: Optional argument. The target URL the user should be redirected to after login.
        :type return_to: string

        :param force_authn: Optional argument. When true the AuthNReuqest will set the ForceAuthn='true'.
        :type force_authn: bool

        :param is_passive: Optional argument. When true the AuthNReuqest will set the Ispassive='true'.
        :type is_passive: bool

        :param set_nameid_policy: Optional argument. When true the AuthNReuqest will set a nameIdPolicy element.
        :type set_nameid_policy: bool

        :returns: Redirection url
        :rtype: string
        """
        authn_request = OneLogin_Saml2_Authn_Request(self.__settings,
                                                     force_authn, is_passive,
                                                     set_nameid_policy)

        self.__last_request_id = authn_request.get_id()

        saml_request = authn_request.get_request()
        parameters = {'SAMLRequest': saml_request}

        if return_to is not None:
            parameters['RelayState'] = return_to
        else:
            parameters[
                'RelayState'] = OneLogin_Saml2_Utils.get_self_url_no_query(
                    self.__request_data)

        security = self.__settings.get_security_data()
        if security.get('authnRequestsSigned', False):
            parameters['SigAlg'] = security['signatureAlgorithm']
            parameters['Signature'] = self.build_request_signature(
                saml_request, parameters['RelayState'],
                security['signatureAlgorithm'])
            # HTTP-POST binding requires generation of a form
            if self.get_settings().get_idp_data()['singleSignOnService'].get(
                    'binding',
                    None) == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST':
                log.debug("Generating AuthnRequest HTTP-POST binding form")

                # Return HTML form
                template_file = open(
                    join(dirname(__file__), 'templates/authn_request.html'))
                template_text = template_file.read()
                template = Template(template_text)

                context = {
                    'sso_url': self.get_sso_url(),
                    'saml_request': saml_request,
                    'relay_state': parameters['RelayState']
                }

                html = template.render(context)
                log.debug("Generated HTML: %s" % html)

                return html

        return self.redirect_to(self.get_sso_url(), parameters)
Beispiel #57
0
    def process_slo(self,
                    keep_local_session=False,
                    request_id=None,
                    delete_session_cb=None):
        """
        Process the SAML Logout Response / Logout Request sent by the IdP.

        :param keep_local_session: When false will destroy the local session, otherwise will destroy it
        :type keep_local_session: bool

        :param request_id: The ID of the LogoutRequest sent by this SP to the IdP
        :type request_id: string

        :returns: Redirection url
        """
        self.__errors = []

        if 'get_data' in self.__request_data and 'SAMLResponse' in self.__request_data[
                'get_data']:
            logout_response = OneLogin_Saml2_Logout_Response(
                self.__settings,
                self.__request_data['get_data']['SAMLResponse'])
            if not logout_response.is_valid(self.__request_data, request_id):
                self.__errors.append('invalid_logout_response')
                self.__error_reason = logout_response.get_error()
            elif logout_response.get_status(
            ) != OneLogin_Saml2_Constants.STATUS_SUCCESS:
                self.__errors.append('logout_not_success')
            elif not keep_local_session:
                OneLogin_Saml2_Utils.delete_local_session(delete_session_cb)

        elif 'get_data' in self.__request_data and 'SAMLRequest' in self.__request_data[
                'get_data']:
            logout_request = OneLogin_Saml2_Logout_Request(
                self.__settings,
                self.__request_data['get_data']['SAMLRequest'])
            if not logout_request.is_valid(self.__request_data):
                self.__errors.append('invalid_logout_request')
                self.__error_reason = logout_request.get_error()
            else:
                if not keep_local_session:
                    OneLogin_Saml2_Utils.delete_local_session(
                        delete_session_cb)

                in_response_to = logout_request.id
                response_builder = OneLogin_Saml2_Logout_Response(
                    self.__settings)
                response_builder.build(in_response_to)
                logout_response = response_builder.get_response()

                parameters = {'SAMLResponse': logout_response}
                if 'RelayState' in self.__request_data['get_data']:
                    parameters['RelayState'] = self.__request_data['get_data'][
                        'RelayState']
                else:
                    parameters[
                        'RelayState'] = OneLogin_Saml2_Utils.get_self_url_no_query(
                            self.__request_data)

                security = self.__settings.get_security_data()
                if 'logoutResponseSigned' in security and security[
                        'logoutResponseSigned']:
                    parameters['SigAlg'] = security['signatureAlgorithm']
                    parameters['Signature'] = self.build_response_signature(
                        logout_response, parameters.get('RelayState', None),
                        security['signatureAlgorithm'])

                return self.redirect_to(self.get_slo_url(), parameters)
        else:
            self.__errors.append('invalid_binding')
            raise OneLogin_Saml2_Error(
                'SAML LogoutRequest/LogoutResponse not found. Only supported HTTP_REDIRECT Binding',
                OneLogin_Saml2_Error.SAML_LOGOUTMESSAGE_NOT_FOUND)
    def testIsInValidSign(self):
        """
        Tests the is_valid method of the OneLogin_Saml2_LogoutResponse
        """
        request_data = {
            'http_host': 'example.com',
            'script_name': 'index.html',
            'get_data': {}
        }
        settings = OneLogin_Saml2_Settings(self.loadSettingsJSON())

        settings.set_strict(False)
        request_data['get_data'] = {
            'SAMLResponse': 'fZJva8IwEMa/Ssl7TZrW/gnqGHMMwSlM8cXeyLU9NaxNQi9lfvxVZczB5ptwSe733MPdjQma2qmFPdjOvyE5awiDU1MbUpevCetaoyyQJmWgQVK+VOvH14WSQ6Fca70tbc1ukPsEEGHrtTUsmM8mbDfKUhnFci8gliGINI/yXIAAiYnsw6JIRgWWAKlkwRZb6skJ64V6nKjDuSEPxvdPIowHIhpIsQkTFaYqSt9ZMEPy2oC/UEfvHSnOnfZFV38MjR1oN7TtgRv8tAZre9CGV9jYkGtT4Wnoju6Bauprme/ebOyErZbPi9XLfLnDoohwhHGc5WVSVhjCKM6rBMpYQpWJrIizfZ4IZNPxuTPqYrmd/m+EdONqPOfy8yG5rhxv0EMFHs52xvxWaHyd3tqD7+j37clWGGyh7vD+POiSrdZdWSIR49NrhR9R/teGTL8A',
            'RelayState': 'https://pitbulk.no-ip.org/newonelogin/demo1/index.php',
            'SigAlg': 'http://www.w3.org/2000/09/xmldsig#rsa-sha1',
            'Signature': 'vfWbbc47PkP3ejx4bjKsRX7lo9Ml1WRoE5J5owF/0mnyKHfSY6XbhO1wwjBV5vWdrUVX+xp6slHyAf4YoAsXFS0qhan6txDiZY4Oec6yE+l10iZbzvie06I4GPak4QrQ4gAyXOSzwCrRmJu4gnpeUxZ6IqKtdrKfAYRAcVfNKGA='
        }
        response = OneLogin_Saml2_Logout_Response(settings, request_data['get_data']['SAMLResponse'])
        self.assertTrue(response.is_valid(request_data))

        relayState = request_data['get_data']['RelayState']
        del request_data['get_data']['RelayState']
        inv_response = OneLogin_Saml2_Logout_Response(settings, request_data['get_data']['SAMLResponse'])
        self.assertFalse(inv_response.is_valid(request_data))
        request_data['get_data']['RelayState'] = relayState

        settings.set_strict(True)
        response_2 = OneLogin_Saml2_Logout_Response(settings, request_data['get_data']['SAMLResponse'])
        try:
            valid = response_2.is_valid(request_data)
            self.assertFalse(valid)
        except Exception as e:
            self.assertIn('Invalid issuer in the Logout Request', e.message)

        settings.set_strict(False)
        old_signature = request_data['get_data']['Signature']
        request_data['get_data']['Signature'] = 'vfWbbc47PkP3ejx4bjKsRX7lo9Ml1WRoE5J5owF/0mnyKHfSY6XbhO1wwjBV5vWdrUVX+xp6slHyAf4YoAsXFS0qhan6txDiZY4Oec6yE+l10iZbzvie06I4GPak4QrQ4gAyXOSzwCrRmJu4gnpeUxZ6IqKtdrKfAYRAcVf3333='
        response_3 = OneLogin_Saml2_Logout_Response(settings, request_data['get_data']['SAMLResponse'])
        try:
            valid = response_3.is_valid(request_data)
            self.assertFalse(valid)
        except Exception as e:
            self.assertIn('Signature validation failed. Logout Response rejected', e.message)

        request_data['get_data']['Signature'] = old_signature
        old_signature_algorithm = request_data['get_data']['SigAlg']
        del request_data['get_data']['SigAlg']
        response_4 = OneLogin_Saml2_Logout_Response(settings, request_data['get_data']['SAMLResponse'])
        self.assertTrue(response_4.is_valid(request_data))

        request_data['get_data']['RelayState'] = 'http://example.com/relaystate'
        response_5 = OneLogin_Saml2_Logout_Response(settings, request_data['get_data']['SAMLResponse'])
        try:
            valid = response_5.is_valid(request_data)
            self.assertFalse(valid)
        except Exception as e:
            self.assertIn('Signature validation failed. Logout Response rejected', e.message)

        settings.set_strict(True)
        current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
        plain_message_6 = OneLogin_Saml2_Utils.decode_base64_and_inflate(request_data['get_data']['SAMLResponse'])
        plain_message_6 = plain_message_6.replace('https://pitbulk.no-ip.org/newonelogin/demo1/index.php?sls', current_url)
        plain_message_6 = plain_message_6.replace('https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php', 'http://idp.example.com/')
        request_data['get_data']['SAMLResponse'] = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message_6)

        response_6 = OneLogin_Saml2_Logout_Response(settings, request_data['get_data']['SAMLResponse'])
        try:
            valid = response_6.is_valid(request_data)
            self.assertFalse(valid)
        except Exception as e:
            self.assertIn('Signature validation failed. Logout Response rejected', e.message)

        settings.set_strict(False)
        response_7 = OneLogin_Saml2_Logout_Response(settings, request_data['get_data']['SAMLResponse'])
        try:
            valid = response_7.is_valid(request_data)
            self.assertFalse(valid)
        except Exception as e:
            self.assertIn('Signature validation failed. Logout Response rejected', e.message)

        request_data['get_data']['SigAlg'] = 'http://www.w3.org/2000/09/xmldsig#dsa-sha1'
        response_8 = OneLogin_Saml2_Logout_Response(settings, request_data['get_data']['SAMLResponse'])
        try:
            valid = response_8.is_valid(request_data)
            self.assertFalse(valid)
        except Exception as e:
            self.assertIn('Invalid signAlg in the recieved Logout Response', e.message)

        settings_info = self.loadSettingsJSON()
        settings_info['strict'] = True
        settings_info['security']['wantMessagesSigned'] = True
        settings = OneLogin_Saml2_Settings(settings_info)

        request_data['get_data']['SigAlg'] = old_signature_algorithm
        old_signature = request_data['get_data']['Signature']
        del request_data['get_data']['Signature']
        request_data['get_data']['SAMLResponse'] = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message_6)
        response_9 = OneLogin_Saml2_Logout_Response(settings, request_data['get_data']['SAMLResponse'])
        try:
            valid = response_9.is_valid(request_data)
            self.assertFalse(valid)
        except Exception as e:
            self.assertIn('The Message of the Logout Response is not signed and the SP require it', e.message)

        request_data['get_data']['Signature'] = old_signature
        settings_info['idp']['certFingerprint'] = 'afe71c28ef740bc87425be13a2263d37971da1f9'
        del settings_info['idp']['x509cert']
        settings_2 = OneLogin_Saml2_Settings(settings_info)

        response_10 = OneLogin_Saml2_Logout_Response(settings_2, request_data['get_data']['SAMLResponse'])
        try:
            valid = response_10.is_valid(request_data)
            self.assertFalse(valid)
        except Exception as e:
            self.assertIn('In order to validate the sign on the Logout Response, the x509cert of the IdP is required', e.message)