def test_valid_saml_auth(self, mock_datetime):
        mock_datetime.now.return_value = datetime(2019,
                                                  4,
                                                  9,
                                                  21,
                                                  35,
                                                  0,
                                                  tzinfo=timezone.utc)
        mock_datetime.strptime = datetime.strptime

        a = SAMLAuthenticator()

        signed_xml = a._verify_saml_signature(self.metadata_etree,
                                              self.response_etree)

        assert etree.tostring(signed_xml) == etree.tostring(
            self.verified_signed_xml)

        response_is_valid, signed_xml = a._test_valid_saml_response(
            self.metadata_etree, self.response_etree)

        assert response_is_valid
        # Check the signed xml is the subset of the xml that is returned by signxml
        assert etree.tostring(signed_xml) == etree.tostring(
            self.verified_signed_xml)
    def test_not_valid_xml(self, mock_b64decode):
        a = SAMLAuthenticator()
        fake_data = {a.login_post_field: 'this string isn\'t important'}

        mock_b64decode.return_value = 'bad xml string'

        assert a._get_saml_doc_etree(fake_data) is None
 def test_tampered_response(self):
     a = SAMLAuthenticator()
     a.metadata_content = test_constants.sample_metadata_xml
     assert a._authenticate(None, {
         a.login_post_field:
         test_constants.tampered_sample_response_encoded
     }) is None
    def test_check_username_invalid_username(self):
        a = SAMLAuthenticator()
        a._optional_user_add = MagicMock()

        assert not a._check_username_and_add_user('bluedata/')

        a._optional_user_add.assert_not_called()
    def test_create_system_users_option(self):
        a = SAMLAuthenticator()
        a.create_system_users = False
        a._optional_user_add = MagicMock()

        assert a._check_username_and_add_user('bluedata')

        a._optional_user_add.assert_not_called()
    def test_metadata_field(self):
        a = SAMLAuthenticator()
        a.metadata_url = 'bad_data'
        a.metadata_content = test_constants.sample_metadata_xml

        assert a._get_metadata_from_config(
        ) == test_constants.sample_metadata_xml
        self._test_high_level_metadata_retrieval_functions(a)
    def test_get_handlers(self):
        a = SAMLAuthenticator()

        handler_list = a.get_handlers(None)

        for url_path, handler_class in handler_list:
            assert url_path in self.expected_handler_paths
            assert isinstance(handler_class, type)
            assert issubclass(handler_class, BaseHandler)
    def test_no_xpath_no_roles_run_default(self):
        a = SAMLAuthenticator()
        a._valid_roles_in_assertion = unittest.mock.create_autospec(
            MagicMock(name='_valid_roles_in_assertion'))
        a.log.warning = MagicMock(name='warning')

        assert a._valid_config_and_roles(None, None)
        a._valid_roles_in_assertion.assert_not_called()
        a.log.warning.assert_not_called()
    def test_no_not_before(self):
        a = SAMLAuthenticator()

        tampered_etree = etree.fromstring(
            test_constants.tampered_assertion_no_not_before)

        assert not a._verify_physical_constraints(tampered_etree)
        assert not a._verify_saml_response_fields(self.metadata_etree,
                                                  tampered_etree)
    def test_create_existing_user(self, mock_pwd, mock_subprocess):
        mock_pwd.getpwnam.return_value = True

        a = SAMLAuthenticator()

        assert a._optional_user_add('Bluedata')

        mock_pwd.getpwnam.assert_called_once_with('Bluedata')
        mock_subprocess.call.assert_not_called()
    def test_get_username_from_saml_doc(self):
        a = SAMLAuthenticator()

        assert 'Bluedata' == a._get_username_from_saml_etree(
            self.verified_signed_xml)
        assert 'Bluedata' == a._get_username_from_saml_etree(
            self.response_etree)
        assert 'Bluedata' == a._get_username_from_saml_doc(
            self.verified_signed_xml, self.response_etree)
    def test_get_invalid_xml_element(self):
        a = SAMLAuthenticator()
        a.metadata_content = test_constants.sample_metadata_xml

        mock_handler_self = MagicMock()

        with self.assertRaises(IndexError):
            a._get_redirect_from_metadata_and_redirect('md:BadElement',
                                                       mock_handler_self)
    def test_make_full_metadata_default(self):
        a = SAMLAuthenticator()

        mock_handler_self = MagicMock()
        mock_handler_self.request.protocol = 'https'
        mock_handler_self.request.host = 'localhost:8000'

        assert a._make_sp_metadata(
            mock_handler_self) == self.full_sp_meta_default
    def test_create_user_fails(self, mock_pwd, mock_subprocess):
        mock_pwd.getpwnam.side_effect = KeyError('Bad username')
        mock_subprocess.call.return_value = 1

        a = SAMLAuthenticator()

        assert not a._optional_user_add('Bluedata')

        mock_pwd.getpwnam.assert_called_once_with('Bluedata')
        mock_subprocess.call.assert_called_once_with(['useradd', 'Bluedata'])
    def test_make_full_metadata_nameid_format(self):
        a = SAMLAuthenticator()
        a.nameid_format = 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent'

        mock_handler_self = MagicMock()
        mock_handler_self.request.protocol = 'https'
        mock_handler_self.request.host = 'localhost:8000'

        assert a._make_sp_metadata(
            mock_handler_self) == self.full_sp_meta_nameid_format
    def test_assertion_no_issuer(self):
        a = SAMLAuthenticator()

        tampered_etree = etree.fromstring(
            test_constants.tampered_assertion_no_issuer)

        assert not a._verify_saml_response_against_metadata(
            self.metadata_etree, tampered_etree)
        assert not a._verify_saml_response_fields(self.metadata_etree,
                                                  tampered_etree)
    def test_make_full_metadata_org_name(self):
        a = SAMLAuthenticator()
        a.organization_name = 'org_name'

        mock_handler_self = MagicMock()
        mock_handler_self.request.protocol = 'https'
        mock_handler_self.request.host = 'localhost:8000'

        assert a._make_sp_metadata(
            mock_handler_self) == self.full_sp_meta_org_name
    def test_signed_xml_no_audience(self):
        a = SAMLAuthenticator()
        a.audience = '''audience_should_exist'''

        tampered_etree = etree.fromstring(
            test_constants.tampered_assertion_no_audience)

        assert not a._verify_saml_response_against_configured_fields(
            tampered_etree)
        assert not a._verify_saml_response_fields(self.metadata_etree,
                                                  tampered_etree)
    def test_get_saml_doc_etree(self):
        # We expect the SAML Response to be coming in base 64 encoded
        a = SAMLAuthenticator()
        fake_data = {
            a.login_post_field: test_constants.b64encoded_response_xml
        }

        faked_etree = a._get_saml_doc_etree(fake_data)
        real_etree = etree.fromstring(test_constants.sample_response_xml)

        assert etree.tostring(faked_etree) == etree.tostring(real_etree)
    def test_get_saml_doc_different_location(self):
        a = SAMLAuthenticator()
        a.login_post_field = 'test'
        fake_data = {
            a.login_post_field: test_constants.b64encoded_response_xml
        }

        faked_etree = a._get_saml_doc_etree(fake_data)
        real_etree = etree.fromstring(test_constants.sample_response_xml)

        assert etree.tostring(faked_etree) == etree.tostring(real_etree)
    def test_signed_xml_no_recipient(self):
        a = SAMLAuthenticator()
        a.recipient = 'unimportant_recipient'

        tampered_etree = etree.fromstring(
            test_constants.tampered_assertion_no_recipient)

        assert not a._verify_saml_response_against_configured_fields(
            tampered_etree)
        assert not a._verify_saml_response_fields(self.metadata_etree,
                                                  tampered_etree)
    def test_create_user_alternate_binary_existing_user(
            self, mock_pwd, mock_subprocess):
        mock_pwd.getpwnam.return_value = True
        binary_value = 'test_binary'

        a = SAMLAuthenticator()
        a.create_system_user_binary = binary_value

        assert a._optional_user_add('Bluedata')

        mock_pwd.getpwnam.assert_called_once_with('Bluedata')
        mock_subprocess.call.assert_not_called()
    def test_get_valid_logout_redirect(self):
        a = SAMLAuthenticator()
        a.metadata_content = test_constants.sample_metadata_xml

        mock_handler_self = MagicMock()

        a._get_redirect_from_metadata_and_redirect('md:SingleLogoutService',
                                                   mock_handler_self)

        mock_handler_self.redirect.assert_called_once_with(
            'https://bluedata-test-before-deploy.onelogin.com/trust/saml2/http-redirect/slo/719630',
            permanent=False)
    def _confirm_tom(self, saml_data, mock_datetime, mock_pwd):
        mock_datetime.now.return_value = saml_data.datetime_stamp
        mock_datetime.strptime = datetime.strptime
        mock_pwd.getpwnam.return_value = True

        a = SAMLAuthenticator()
        a.metadata_content = saml_data.metadata_xml

        assert 'tom' == a._authenticate(
            None, {a.login_post_field: saml_data.b64encoded_response})
        mock_datetime.now.assert_called_once_with(timezone.utc)
        mock_pwd.getpwnam.assert_called_once_with('tom')
    def test_create_user_alternate_binary(self, mock_pwd, mock_subprocess):
        mock_pwd.getpwnam.side_effect = KeyError('Bad username')
        mock_subprocess.call.return_value = 0
        binary_value = 'test_binary'

        a = SAMLAuthenticator()
        a.create_system_user_binary = binary_value

        assert a._optional_user_add('Bluedata')

        mock_pwd.getpwnam.assert_called_once_with('Bluedata')
        mock_subprocess.call.assert_called_once_with(
            [binary_value, 'Bluedata'])
    def test_no_xpath_roles(self):
        a = SAMLAuthenticator()
        a.allowed_roles = 'value'
        a._valid_roles_in_assertion = unittest.mock.create_autospec(
            MagicMock(name='_valid_roles_in_assertion'))
        a.log.warning = MagicMock(name='warning')

        assert a._valid_config_and_roles(None, None)
        a._valid_roles_in_assertion.assert_not_called()
        print(a.log.warning.call_args_list)
        a.log.warning.assert_called()
        a.log.warning.assert_any_call(a._const_warn_explain)
        a.log.warning.assert_any_call(a._const_warn_no_role_xpath)
    def test_no_metadata_cert(self):
        a = SAMLAuthenticator()
        no_cert_metadata_etree = etree.fromstring(
            test_constants.sample_metadata_no_cert_xml)

        bad_signed_xml = a._verify_saml_signature(no_cert_metadata_etree,
                                                  self.response_etree)

        assert bad_signed_xml is None

        response_is_valid, signed_xml = a._test_valid_saml_response(
            no_cert_metadata_etree, self.response_etree)

        assert not response_is_valid
        assert signed_xml is None
    def test_tampered_saml_response(self):
        a = SAMLAuthenticator()
        tampered_etree = etree.fromstring(
            test_constants.tampered_sample_response_xml)

        bad_signed_xml = a._verify_saml_signature(self.metadata_etree,
                                                  tampered_etree)

        assert bad_signed_xml is None

        response_is_valid, signed_xml = a._test_valid_saml_response(
            self.metadata_etree, tampered_etree)

        assert not response_is_valid
        assert signed_xml is None
    def test_metadata_no_entity(self):
        a = SAMLAuthenticator()
        no_metadata_entity_etree = etree.fromstring(
            test_constants.sample_metadata_no_entity)

        assert a._verify_saml_response_against_metadata(
            no_metadata_entity_etree, self.verified_signed_xml) is False

        assert a._verify_saml_response_fields(
            no_metadata_entity_etree, self.verified_signed_xml) is False

        response_is_valid, signed_xml = a._test_valid_saml_response(
            no_metadata_entity_etree, self.response_etree)

        assert not response_is_valid
        assert etree.tostring(signed_xml) == etree.tostring(
            self.verified_signed_xml)
    def test_metadata_url(self, mock_urlopen):
        entered_obj = MagicMock()
        entered_obj.read.return_value = test_constants.sample_metadata_xml
        mock_urlopen().__enter__.return_value = entered_obj

        a = SAMLAuthenticator()
        a.metadata_url = 'http://foo'

        # Check that we're getting the right value
        assert a._get_metadata_from_url() == test_constants.sample_metadata_xml
        # Check that we have, at least once, called open with the provided url
        # TODO: Figure out how to do this so we can use 'assert_called_once_with'
        mock_urlopen.assert_any_call(a.metadata_url)
        # Check that we're reading the file
        entered_obj.read.assert_called_once()

        self._test_readable_mock(a, mock_urlopen)