From 26b77f8482ebefcb07537cd537b08c796254149e Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Tue, 22 Oct 2024 17:24:17 -0500 Subject: [PATCH 1/4] Switch test_backend from unittest-style to plain asserts Parametrization is incompatible with unittest-style tests. So, this migrates test_backend to use plain asserts instead. --- tests/unit/test_backend.py | 1956 ++++++++++++++++++------------------ 1 file changed, 974 insertions(+), 982 deletions(-) diff --git a/tests/unit/test_backend.py b/tests/unit/test_backend.py index e0e997d..4e77aee 100644 --- a/tests/unit/test_backend.py +++ b/tests/unit/test_backend.py @@ -16,10 +16,10 @@ import os import time import uuid +from unittest import mock import ldap -import mock -import unittest +import pytest from st2auth_ldap import ldap_backend @@ -77,994 +77,986 @@ LDAP_GROUP_SEARCH_RESULT = [('cn=testers,dc=stackstorm,dc=net', ()), ('cn=stormers,dc=stackstorm,dc=net', ())] -__all__ = [ - 'LDAPBackendTest' -] - -class LDAPBackendTest(unittest.TestCase): - - def test_instantaite_no_group_dns_provided(self): - # User is member of two of the groups, but none of them are required - required_group_dns = [] - expected_msg = 'One or more user groups must be specified' - self.assertRaisesRegexp(ValueError, expected_msg, ldap_backend.LDAPAuthenticationBackend, - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - required_group_dns, - LDAP_HOST, - id_attr=LDAP_ID_ATTR) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) - def test_authenticate(self): - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - LDAP_GROUP_DNS, - LDAP_HOST, - id_attr=LDAP_ID_ATTR - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_PASSWD) - self.assertTrue(authenticated) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) - def test_authenticate_with_multiple_ldap_hosts(self): - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - LDAP_GROUP_DNS, - LDAP_MULTIPLE_HOSTS, - id_attr=LDAP_ID_ATTR - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_PASSWD) - self.assertTrue(authenticated) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) - def test_authenticate_without_password(self): - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - LDAP_GROUP_DNS, - LDAP_HOST, - id_attr=LDAP_ID_ATTR - ) - - with self.assertRaises(ValueError): - backend.authenticate(LDAP_USER_UID, '') - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(side_effect=Exception())) - def test_authenticate_failure_bad_bind_cred(self): - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - LDAP_GROUP_DNS, - LDAP_HOST, - id_attr=LDAP_ID_ATTR - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) - self.assertFalse(authenticated) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(side_effect=[None, Exception()])) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) - def test_authenticate_failure_bad_user_password(self): - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - LDAP_GROUP_DNS, - LDAP_HOST, - id_attr=LDAP_ID_ATTR - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) - self.assertFalse(authenticated) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, []])) - def test_authenticate_failure_non_group_member_no_groups(self): - # User is not member of any of the required group - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - LDAP_GROUP_DNS, - LDAP_HOST, - id_attr=LDAP_ID_ATTR, - group_dns_check='and' - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) - self.assertFalse(authenticated) - - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - LDAP_GROUP_DNS, - LDAP_HOST, - id_attr=LDAP_ID_ATTR, - group_dns_check='or' - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) - self.assertFalse(authenticated) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ())]])) - def test_authenticatefailure_non_group_member_non_required_group(self): - # User is member of a group which is not required - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - LDAP_GROUP_DNS, - LDAP_HOST, - id_attr=LDAP_ID_ATTR, - group_dns_check='and' - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) - self.assertFalse(authenticated) - - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - LDAP_GROUP_DNS, - LDAP_HOST, - id_attr=LDAP_ID_ATTR, - group_dns_check='or' - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) - self.assertFalse(authenticated) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ()), - ('cn=group3,dc=stackstorm,dc=net', ())]])) - def test_authenticate_and_behavior_failure_non_group_member_of_all_required_groups_1(self): - # User is member of two of the required groups (1 and 3) but not all three of them - # (1, 2, 3) - required_group_dns = [ - 'cn=group1,dc=stackstorm,dc=net', - 'cn=group2,dc=stackstorm,dc=net', - 'cn=group3,dc=stackstorm,dc=net', - ] - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - required_group_dns, - LDAP_HOST, - id_attr=LDAP_ID_ATTR, - group_dns_check='and' - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) - self.assertFalse(authenticated) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ()), - ('cn=group3,dc=stackstorm,dc=net', ())]])) - def test_authenticate_and_behavior_failure_non_group_member_of_all_required_groups_2(self): - # User is member of two of the groups, but none of them are required - required_group_dns = [ - 'cn=group7,dc=stackstorm,dc=net', - 'cn=group8,dc=stackstorm,dc=net' - ] - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - required_group_dns, - LDAP_HOST, - id_attr=LDAP_ID_ATTR, - group_dns_check='and' - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) - self.assertFalse(authenticated) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ()), - ('cn=group2,dc=stackstorm,dc=net', ()), - ('cn=group3,dc=stackstorm,dc=net', ()), - ('cn=group4,dc=stackstorm,dc=net', ())]])) - def test_authenticate_and_behavior_failure_non_group_member_of_all_required_groups_3(self): - # User is member of two of the required groups and two non-required, but not - # all of the required groups - required_group_dns = [ - 'cn=group1,dc=stackstorm,dc=net', - 'cn=group2,dc=stackstorm,dc=net', - 'cn=group5,dc=stackstorm,dc=net', - 'cn=group6,dc=stackstorm,dc=net', - ] - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - required_group_dns, - LDAP_HOST, - id_attr=LDAP_ID_ATTR, - group_dns_check='and' - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) - self.assertFalse(authenticated) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ()), - ('cn=group2,dc=stackstorm,dc=net', ()), - ('cn=group3,dc=stackstorm,dc=net', ()), - ('cn=group4,dc=stackstorm,dc=net', ())]])) - def test_authenticate_and_is_default_behavior_non_group_member_of_all_required_groups(self): - # User is member of two of the required groups and two non-required, but not - # all of the required groups - # Verify "and" is a default group_dns_check_behavior - required_group_dns = [ - 'cn=group1,dc=stackstorm,dc=net', - 'cn=group2,dc=stackstorm,dc=net', - 'cn=group5,dc=stackstorm,dc=net', - 'cn=group6,dc=stackstorm,dc=net', - ] - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - required_group_dns, - LDAP_HOST, - id_attr=LDAP_ID_ATTR - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) - self.assertFalse(authenticated) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ())]])) - def test_authenticate_or_behavior_success_member_of_single_group_1(self): - # User is a memeber of single of possible required groups - required_group_dns = [ - 'cn=group1,dc=stackstorm,dc=net' - ] - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - required_group_dns, - LDAP_HOST, - id_attr=LDAP_ID_ATTR, - group_dns_check='or' - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) - self.assertTrue(authenticated) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ())]])) - def test_authenticate_or_behavior_success_member_of_single_group_2(self): - # User is a memeber of single of possible required groups - required_group_dns = [ - 'cn=group1,dc=stackstorm,dc=net', - 'cn=group2,dc=stackstorm,dc=net', - 'cn=group3,dc=stackstorm,dc=net', - 'cn=group4,dc=stackstorm,dc=net' - ] - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - required_group_dns, - LDAP_HOST, - id_attr=LDAP_ID_ATTR, - group_dns_check='or' - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) - self.assertTrue(authenticated) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group3,dc=stackstorm,dc=net', ())]])) - def test_authenticate_or_behavior_success_member_of_single_group_2b(self): - # User is a memeber of single of possible required groups - required_group_dns = [ - 'cn=group1,dc=stackstorm,dc=net', - 'cn=group2,dc=stackstorm,dc=net', - 'cn=group3,dc=stackstorm,dc=net', - 'cn=group4,dc=stackstorm,dc=net' - ] - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - required_group_dns, - LDAP_HOST, - id_attr=LDAP_ID_ATTR, - group_dns_check='or' - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) - self.assertTrue(authenticated) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ()), - ('cn=group4,dc=stackstorm,dc=net', ())]])) - def test_authenticate_or_behavior_success_member_of_multiple_groups_1(self): - # User is a memeber of multiple of required groups - required_group_dns = [ - 'cn=group1,dc=stackstorm,dc=net', - 'cn=group2,dc=stackstorm,dc=net', - 'cn=group3,dc=stackstorm,dc=net', - 'cn=group4,dc=stackstorm,dc=net', - 'cn=group5,dc=stackstorm,dc=net' - ] - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - required_group_dns, - LDAP_HOST, - id_attr=LDAP_ID_ATTR, - group_dns_check='or' - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) - self.assertTrue(authenticated) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ()), - ('cn=group4,dc=stackstorm,dc=net', ())]])) - def test_authenticate_or_behavior_success_member_of_multiple_groups_2(self): - # User is a memeber of multiple of required groups - required_group_dns = [ - 'cn=group1,dc=stackstorm,dc=net', - 'cn=group4,dc=stackstorm,dc=net' - ] - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - required_group_dns, - LDAP_HOST, - id_attr=LDAP_ID_ATTR, - group_dns_check='or' - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) - self.assertTrue(authenticated) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ()), - ('cn=group3,dc=stackstorm,dc=net', ()), - ('cn=group6,dc=stackstorm,dc=net', ())]])) - def test_authenticate_or_behavior_success_member_of_multiple_groups_3(self): - # User is a memeber of multiple of required groups - required_group_dns = [ - 'cn=group3,dc=stackstorm,dc=net' - ] - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - required_group_dns, - LDAP_HOST, - id_attr=LDAP_ID_ATTR, - group_dns_check='or' - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) - self.assertTrue(authenticated) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ()), - ('cn=group3,dc=stackstorm,dc=net', ()), - ('cn=group6,dc=stackstorm,dc=net', ())]])) - def test_authenticate_or_behavior_success_member_of_multiple_groups_3b(self): - # User is a memeber of multiple of required groups - required_group_dns = [ - 'cn=group3,dc=stackstorm,dc=net', - 'cn=group1,dc=stackstorm,dc=net' - ] - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - required_group_dns, - LDAP_HOST, - id_attr=LDAP_ID_ATTR, - group_dns_check='or' - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) - self.assertTrue(authenticated) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) - def test_ssl_authenticate(self): - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - LDAP_GROUP_DNS, - LDAP_HOST, - port=LDAPS_PORT, - use_ssl=True, - id_attr=LDAP_ID_ATTR - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_PASSWD) - self.assertTrue(authenticated) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(side_effect=[None, Exception()])) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) - def test_ssl_authenticate_failure(self): - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - LDAP_GROUP_DNS, - LDAP_HOST, - port=LDAPS_PORT, - use_ssl=True, - id_attr=LDAP_ID_ATTR - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) - self.assertFalse(authenticated) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) - def test_ssl_authenticate_validate_cert(self): - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - LDAP_GROUP_DNS, - LDAP_HOST, - port=LDAPS_PORT, - use_ssl=True, - cacert=LDAP_CACERT_REAL_PATH, - id_attr=LDAP_ID_ATTR - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_PASSWD) - self.assertTrue(authenticated) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'start_tls_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) - def test_tls_authenticate(self): - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - LDAP_GROUP_DNS, - LDAP_HOST, - use_tls=True, - id_attr=LDAP_ID_ATTR - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_PASSWD) - self.assertTrue(authenticated) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'start_tls_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(side_effect=[None, Exception()])) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) - def test_tls_authenticate_failure(self): - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - LDAP_GROUP_DNS, - LDAP_HOST, - use_tls=True, - id_attr=LDAP_ID_ATTR - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) - self.assertFalse(authenticated) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'start_tls_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) - def test_tls_authenticate_validate_cert(self): - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - LDAP_GROUP_DNS, - LDAP_HOST, - use_tls=True, - cacert=LDAP_CACERT_REAL_PATH, - id_attr=LDAP_ID_ATTR - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_PASSWD) - self.assertTrue(authenticated) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, []])) - def test_special_characters_in_username_are_escaped(self): - # User is not member of any of the required group - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - LDAP_GROUP_DNS, - LDAP_HOST, - id_attr=LDAP_ID_ATTR - ) - - values = [ - ('stanleyA', 'stanleyA'), - ('stanley!@?.,&', 'stanley!@?.,&'), - # Special characters () should be escaped - ('(stanley)', '\\28stanley\\29'), - # Special characters () should be escaped - ('(stanley=)', '\\28stanley=\\29'), - ] - - for actual_username, expected_username in values: - backend.authenticate(actual_username, LDAP_USER_BAD_PASSWD) - - call_args_1 = ldap.ldapobject.SimpleLDAPObject.search_s.call_args_list[0][0] - call_args_2 = ldap.ldapobject.SimpleLDAPObject.search_s.call_args_list[1][0] - - # First search_s call (find user by uid) - filter_call_value = call_args_1[2] - self.assertEqual(filter_call_value, 'uid=%s' % (expected_username)) - - # Second search_s call (group membership test) - filter_call_value = call_args_2[2] - self.assertTrue('(memberUid=%s)' % (expected_username) in filter_call_value) - - ldap.ldapobject.SimpleLDAPObject.search_s = mock.MagicMock( - side_effect=[LDAP_USER_SEARCH_RESULT, []]) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) - def test_get_user(self): - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - LDAP_GROUP_DNS, - LDAP_HOST, - id_attr=LDAP_ID_ATTR - ) - - user_info = backend.get_user(username=LDAP_USER_UID) - self.assertEqual(user_info['cn'], ['Tomaz Muraus']) - self.assertEqual(user_info['displayName'], ['Tomaz Muraus']) - self.assertEqual(user_info['givenName'], ['Tomaz']) - self.assertEqual(user_info['primaryGroupID'], ['513']) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[2 * LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) - def test_get_user_multiple_results(self): - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - LDAP_GROUP_DNS, - LDAP_HOST, - id_attr=LDAP_ID_ATTR - ) - - user_info = backend.get_user(username=LDAP_USER_UID) - self.assertIsNone(user_info) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) - def test_get_user_groups(self): - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - LDAP_GROUP_DNS, - LDAP_HOST, - id_attr=LDAP_ID_ATTR - ) - - expected = [ - 'cn=testers,dc=stackstorm,dc=net', - 'cn=stormers,dc=stackstorm,dc=net' - ] - - groups = backend.get_user_groups(username=LDAP_USER_UID) - self.assertEqual(groups, expected) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ())], - LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ())] - ])) - def test_authenticate_and_get_user_groups_caching_disabled(self): - required_group_dns = [ - 'cn=group1,dc=stackstorm,dc=net' - ] - - backend = ldap_backend.LDAPAuthenticationBackend( +def test_instantaite_no_group_dns_provided(): + # User is member of two of the groups, but none of them are required + required_group_dns = [] + expected_msg = "One or more user groups must be specified" + with pytest.raises(ValueError, match=expected_msg): + ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, LDAP_BIND_PASSWORD, LDAP_BASE_OU, required_group_dns, LDAP_HOST, id_attr=LDAP_ID_ATTR, - group_dns_check='or', - cache_user_groups_response=False ) - self.assertEqual(ldap.ldapobject.SimpleLDAPObject.search_s.call_count, 0) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) - self.assertTrue(authenticated) - - # 1 for user dn search, 1 for groups search - self.assertEqual(ldap.ldapobject.SimpleLDAPObject.search_s.call_count, 2) - - user_groups = backend.get_user_groups(username=LDAP_USER_UID) - self.assertEqual(user_groups, ['cn=group1,dc=stackstorm,dc=net']) - self.assertEqual(ldap.ldapobject.SimpleLDAPObject.search_s.call_count, 4) - self.assertTrue(backend._user_groups_cache is None) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ())], - LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ())] - ])) - def test_authenticate_and_get_user_groups_caching_enabled(self): - required_group_dns = [ - 'cn=group1,dc=stackstorm,dc=net' - ] - - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - required_group_dns, - LDAP_HOST, - id_attr=LDAP_ID_ATTR, - group_dns_check='or', - cache_user_groups_response=True - ) - - self.assertEqual(ldap.ldapobject.SimpleLDAPObject.search_s.call_count, 0) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) - self.assertTrue(authenticated) - self.assertEqual(ldap.ldapobject.SimpleLDAPObject.search_s.call_count, 2) - - user_groups = backend.get_user_groups(username=LDAP_USER_UID) - self.assertEqual(user_groups, ['cn=group1,dc=stackstorm,dc=net']) - self.assertEqual(ldap.ldapobject.SimpleLDAPObject.search_s.call_count, 2) - self.assertTrue(LDAP_USER_UID in backend._user_groups_cache) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT])) - def test_get_user_specifying_account_pattern(self): - expected_username = 'unique_username_1' - required_group_dns = [ - 'cn=group3,dc=stackstorm,dc=net', - 'cn=group4,dc=stackstorm,dc=net' - ] - scope = 'subtree' - scope_number = ldap_backend.SEARCH_SCOPES[scope] - - account_pattern = ''' - (& - (objectClass=person) - (| - (accountName={username}) - (mail={username}) - ) - ) - '''.replace('\n', '').replace(' ', '') - expected_account_pattern = account_pattern.format(username=expected_username) - - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - required_group_dns, - LDAP_HOST, - scope=scope, - account_pattern=account_pattern, - ) - connection = mock.MagicMock() - backend._init_connection = mock.MagicMock(return_value=connection) - backend.get_user(expected_username) - - connection.search_s.assert_called_once_with(LDAP_BASE_OU, scope_number, - expected_account_pattern, []) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group3,dc=stackstorm,dc=net', ())], - LDAP_USER_SEARCH_RESULT, - [('cn=group4,dc=stackstorm,dc=net', ())] - ])) - def test_get_user_groups_specifying_group_pattern(self): - expected_user_dn = 'unique_userdn_1' - expected_username = 'unique_username_2' - required_group_dns = [ - 'cn=group3,dc=stackstorm,dc=net', - 'cn=group4,dc=stackstorm,dc=net' - ] - scope = 'subtree' - scope_number = ldap_backend.SEARCH_SCOPES[scope] - - group_pattern = ''' +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) +def test_authenticate(): + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + LDAP_GROUP_DNS, + LDAP_HOST, + id_attr=LDAP_ID_ATTR + ) + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_PASSWD) + assert authenticated + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) +def test_authenticate_with_multiple_ldap_hosts(): + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + LDAP_GROUP_DNS, + LDAP_MULTIPLE_HOSTS, + id_attr=LDAP_ID_ATTR + ) + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_PASSWD) + assert authenticated + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) +def test_authenticate_without_password(): + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + LDAP_GROUP_DNS, + LDAP_HOST, + id_attr=LDAP_ID_ATTR + ) + + with pytest.raises(ValueError): + backend.authenticate(LDAP_USER_UID, '') + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(side_effect=Exception())) +def test_authenticate_failure_bad_bind_cred(): + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + LDAP_GROUP_DNS, + LDAP_HOST, + id_attr=LDAP_ID_ATTR + ) + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) + assert not authenticated + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(side_effect=[None, Exception()])) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) +def test_authenticate_failure_bad_user_password(): + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + LDAP_GROUP_DNS, + LDAP_HOST, + id_attr=LDAP_ID_ATTR + ) + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) + assert not authenticated + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, []])) +def test_authenticate_failure_non_group_member_no_groups(): + # User is not member of any of the required group + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + LDAP_GROUP_DNS, + LDAP_HOST, + id_attr=LDAP_ID_ATTR, + group_dns_check='and' + ) + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) + assert not authenticated + + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + LDAP_GROUP_DNS, + LDAP_HOST, + id_attr=LDAP_ID_ATTR, + group_dns_check='or' + ) + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) + assert not authenticated + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, + [('cn=group1,dc=stackstorm,dc=net', ())]])) +def test_authenticatefailure_non_group_member_non_required_group(): + # User is member of a group which is not required + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + LDAP_GROUP_DNS, + LDAP_HOST, + id_attr=LDAP_ID_ATTR, + group_dns_check='and' + ) + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) + assert not authenticated + + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + LDAP_GROUP_DNS, + LDAP_HOST, + id_attr=LDAP_ID_ATTR, + group_dns_check='or' + ) + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) + assert not authenticated + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, + [('cn=group1,dc=stackstorm,dc=net', ()), + ('cn=group3,dc=stackstorm,dc=net', ())]])) +def test_authenticate_and_behavior_failure_non_group_member_of_all_required_groups_1(): + # User is member of two of the required groups (1 and 3) but not all three of them + # (1, 2, 3) + required_group_dns = [ + 'cn=group1,dc=stackstorm,dc=net', + 'cn=group2,dc=stackstorm,dc=net', + 'cn=group3,dc=stackstorm,dc=net', + ] + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + required_group_dns, + LDAP_HOST, + id_attr=LDAP_ID_ATTR, + group_dns_check='and' + ) + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) + assert not authenticated + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, + [('cn=group1,dc=stackstorm,dc=net', ()), + ('cn=group3,dc=stackstorm,dc=net', ())]])) +def test_authenticate_and_behavior_failure_non_group_member_of_all_required_groups_2(): + # User is member of two of the groups, but none of them are required + required_group_dns = [ + 'cn=group7,dc=stackstorm,dc=net', + 'cn=group8,dc=stackstorm,dc=net' + ] + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + required_group_dns, + LDAP_HOST, + id_attr=LDAP_ID_ATTR, + group_dns_check='and' + ) + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) + assert not authenticated + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, + [('cn=group1,dc=stackstorm,dc=net', ()), + ('cn=group2,dc=stackstorm,dc=net', ()), + ('cn=group3,dc=stackstorm,dc=net', ()), + ('cn=group4,dc=stackstorm,dc=net', ())]])) +def test_authenticate_and_behavior_failure_non_group_member_of_all_required_groups_3(): + # User is member of two of the required groups and two non-required, but not + # all of the required groups + required_group_dns = [ + 'cn=group1,dc=stackstorm,dc=net', + 'cn=group2,dc=stackstorm,dc=net', + 'cn=group5,dc=stackstorm,dc=net', + 'cn=group6,dc=stackstorm,dc=net', + ] + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + required_group_dns, + LDAP_HOST, + id_attr=LDAP_ID_ATTR, + group_dns_check='and' + ) + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) + assert not authenticated + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, + [('cn=group1,dc=stackstorm,dc=net', ()), + ('cn=group2,dc=stackstorm,dc=net', ()), + ('cn=group3,dc=stackstorm,dc=net', ()), + ('cn=group4,dc=stackstorm,dc=net', ())]])) +def test_authenticate_and_is_default_behavior_non_group_member_of_all_required_groups(): + # User is member of two of the required groups and two non-required, but not + # all of the required groups + # Verify "and" is a default group_dns_check_behavior + required_group_dns = [ + 'cn=group1,dc=stackstorm,dc=net', + 'cn=group2,dc=stackstorm,dc=net', + 'cn=group5,dc=stackstorm,dc=net', + 'cn=group6,dc=stackstorm,dc=net', + ] + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + required_group_dns, + LDAP_HOST, + id_attr=LDAP_ID_ATTR + ) + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) + assert not authenticated + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, + [('cn=group1,dc=stackstorm,dc=net', ())]])) +def test_authenticate_or_behavior_success_member_of_single_group_1(): + # User is a memeber of single of possible required groups + required_group_dns = [ + 'cn=group1,dc=stackstorm,dc=net' + ] + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + required_group_dns, + LDAP_HOST, + id_attr=LDAP_ID_ATTR, + group_dns_check='or' + ) + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) + assert authenticated + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, + [('cn=group1,dc=stackstorm,dc=net', ())]])) +def test_authenticate_or_behavior_success_member_of_single_group_2(): + # User is a memeber of single of possible required groups + required_group_dns = [ + 'cn=group1,dc=stackstorm,dc=net', + 'cn=group2,dc=stackstorm,dc=net', + 'cn=group3,dc=stackstorm,dc=net', + 'cn=group4,dc=stackstorm,dc=net' + ] + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + required_group_dns, + LDAP_HOST, + id_attr=LDAP_ID_ATTR, + group_dns_check='or' + ) + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) + assert authenticated + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, + [('cn=group3,dc=stackstorm,dc=net', ())]])) +def test_authenticate_or_behavior_success_member_of_single_group_2b(): + # User is a memeber of single of possible required groups + required_group_dns = [ + 'cn=group1,dc=stackstorm,dc=net', + 'cn=group2,dc=stackstorm,dc=net', + 'cn=group3,dc=stackstorm,dc=net', + 'cn=group4,dc=stackstorm,dc=net' + ] + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + required_group_dns, + LDAP_HOST, + id_attr=LDAP_ID_ATTR, + group_dns_check='or' + ) + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) + assert authenticated + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, + [('cn=group1,dc=stackstorm,dc=net', ()), + ('cn=group4,dc=stackstorm,dc=net', ())]])) +def test_authenticate_or_behavior_success_member_of_multiple_groups_1(): + # User is a memeber of multiple of required groups + required_group_dns = [ + 'cn=group1,dc=stackstorm,dc=net', + 'cn=group2,dc=stackstorm,dc=net', + 'cn=group3,dc=stackstorm,dc=net', + 'cn=group4,dc=stackstorm,dc=net', + 'cn=group5,dc=stackstorm,dc=net' + ] + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + required_group_dns, + LDAP_HOST, + id_attr=LDAP_ID_ATTR, + group_dns_check='or' + ) + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) + assert authenticated + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, + [('cn=group1,dc=stackstorm,dc=net', ()), + ('cn=group4,dc=stackstorm,dc=net', ())]])) +def test_authenticate_or_behavior_success_member_of_multiple_groups_2(): + # User is a memeber of multiple of required groups + required_group_dns = [ + 'cn=group1,dc=stackstorm,dc=net', + 'cn=group4,dc=stackstorm,dc=net' + ] + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + required_group_dns, + LDAP_HOST, + id_attr=LDAP_ID_ATTR, + group_dns_check='or' + ) + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) + assert authenticated + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, + [('cn=group1,dc=stackstorm,dc=net', ()), + ('cn=group3,dc=stackstorm,dc=net', ()), + ('cn=group6,dc=stackstorm,dc=net', ())]])) +def test_authenticate_or_behavior_success_member_of_multiple_groups_3(): + # User is a memeber of multiple of required groups + required_group_dns = [ + 'cn=group3,dc=stackstorm,dc=net' + ] + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + required_group_dns, + LDAP_HOST, + id_attr=LDAP_ID_ATTR, + group_dns_check='or' + ) + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) + assert authenticated + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, + [('cn=group1,dc=stackstorm,dc=net', ()), + ('cn=group3,dc=stackstorm,dc=net', ()), + ('cn=group6,dc=stackstorm,dc=net', ())]])) +def test_authenticate_or_behavior_success_member_of_multiple_groups_3b(): + # User is a memeber of multiple of required groups + required_group_dns = [ + 'cn=group3,dc=stackstorm,dc=net', + 'cn=group1,dc=stackstorm,dc=net' + ] + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + required_group_dns, + LDAP_HOST, + id_attr=LDAP_ID_ATTR, + group_dns_check='or' + ) + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) + assert authenticated + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) +def test_ssl_authenticate(): + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + LDAP_GROUP_DNS, + LDAP_HOST, + port=LDAPS_PORT, + use_ssl=True, + id_attr=LDAP_ID_ATTR + ) + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_PASSWD) + assert authenticated + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(side_effect=[None, Exception()])) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) +def test_ssl_authenticate_failure(): + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + LDAP_GROUP_DNS, + LDAP_HOST, + port=LDAPS_PORT, + use_ssl=True, + id_attr=LDAP_ID_ATTR + ) + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) + assert not authenticated + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) +def test_ssl_authenticate_validate_cert(): + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + LDAP_GROUP_DNS, + LDAP_HOST, + port=LDAPS_PORT, + use_ssl=True, + cacert=LDAP_CACERT_REAL_PATH, + id_attr=LDAP_ID_ATTR + ) + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_PASSWD) + assert authenticated + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'start_tls_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) +def test_tls_authenticate(): + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + LDAP_GROUP_DNS, + LDAP_HOST, + use_tls=True, + id_attr=LDAP_ID_ATTR + ) + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_PASSWD) + assert authenticated + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'start_tls_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(side_effect=[None, Exception()])) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) +def test_tls_authenticate_failure(): + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + LDAP_GROUP_DNS, + LDAP_HOST, + use_tls=True, + id_attr=LDAP_ID_ATTR + ) + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) + assert not authenticated + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'start_tls_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) +def test_tls_authenticate_validate_cert(): + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + LDAP_GROUP_DNS, + LDAP_HOST, + use_tls=True, + cacert=LDAP_CACERT_REAL_PATH, + id_attr=LDAP_ID_ATTR + ) + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_PASSWD) + assert authenticated + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, []])) +def test_special_characters_in_username_are_escaped(): + # User is not member of any of the required group + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + LDAP_GROUP_DNS, + LDAP_HOST, + id_attr=LDAP_ID_ATTR + ) + + values = [ + ('stanleyA', 'stanleyA'), + ('stanley!@?.,&', 'stanley!@?.,&'), + # Special characters () should be escaped + ('(stanley)', '\\28stanley\\29'), + # Special characters () should be escaped + ('(stanley=)', '\\28stanley=\\29'), + ] + + for actual_username, expected_username in values: + backend.authenticate(actual_username, LDAP_USER_BAD_PASSWD) + + call_args_1 = ldap.ldapobject.SimpleLDAPObject.search_s.call_args_list[0][0] + call_args_2 = ldap.ldapobject.SimpleLDAPObject.search_s.call_args_list[1][0] + + # First search_s call (find user by uid) + filter_call_value = call_args_1[2] + assert filter_call_value == f"uid={expected_username}" + + # Second search_s call (group membership test) + filter_call_value = call_args_2[2] + assert f"(memberUid={expected_username})" in filter_call_value + + ldap.ldapobject.SimpleLDAPObject.search_s = mock.MagicMock( + side_effect=[LDAP_USER_SEARCH_RESULT, []]) + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) +def test_get_user(): + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + LDAP_GROUP_DNS, + LDAP_HOST, + id_attr=LDAP_ID_ATTR + ) + + user_info = backend.get_user(username=LDAP_USER_UID) + assert user_info['cn'] == ['Tomaz Muraus'] + assert user_info['displayName'] == ['Tomaz Muraus'] + assert user_info['givenName'] == ['Tomaz'] + assert user_info['primaryGroupID'] == ['513'] + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[2 * LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) +def test_get_user_multiple_results(): + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + LDAP_GROUP_DNS, + LDAP_HOST, + id_attr=LDAP_ID_ATTR + ) + + user_info = backend.get_user(username=LDAP_USER_UID) + assert user_info is None + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) +def test_get_user_groups(): + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + LDAP_GROUP_DNS, + LDAP_HOST, + id_attr=LDAP_ID_ATTR + ) + + expected = [ + 'cn=testers,dc=stackstorm,dc=net', + 'cn=stormers,dc=stackstorm,dc=net' + ] + + groups = backend.get_user_groups(username=LDAP_USER_UID) + assert groups == expected + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, + [('cn=group1,dc=stackstorm,dc=net', ())], + LDAP_USER_SEARCH_RESULT, + [('cn=group1,dc=stackstorm,dc=net', ())] + ])) +def test_authenticate_and_get_user_groups_caching_disabled(): + required_group_dns = [ + 'cn=group1,dc=stackstorm,dc=net' + ] + + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + required_group_dns, + LDAP_HOST, + id_attr=LDAP_ID_ATTR, + group_dns_check='or', + cache_user_groups_response=False + ) + + assert ldap.ldapobject.SimpleLDAPObject.search_s.call_count == 0 + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) + assert authenticated + + # 1 for user dn search, 1 for groups search + assert ldap.ldapobject.SimpleLDAPObject.search_s.call_count == 2 + + user_groups = backend.get_user_groups(username=LDAP_USER_UID) + assert user_groups == ['cn=group1,dc=stackstorm,dc=net'] + assert ldap.ldapobject.SimpleLDAPObject.search_s.call_count == 4 + assert backend._user_groups_cache is None + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, + [('cn=group1,dc=stackstorm,dc=net', ())], + LDAP_USER_SEARCH_RESULT, + [('cn=group1,dc=stackstorm,dc=net', ())] + ])) +def test_authenticate_and_get_user_groups_caching_enabled(): + required_group_dns = [ + 'cn=group1,dc=stackstorm,dc=net' + ] + + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + required_group_dns, + LDAP_HOST, + id_attr=LDAP_ID_ATTR, + group_dns_check='or', + cache_user_groups_response=True + ) + + assert ldap.ldapobject.SimpleLDAPObject.search_s.call_count == 0 + + authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) + assert authenticated + assert ldap.ldapobject.SimpleLDAPObject.search_s.call_count == 2 + + user_groups = backend.get_user_groups(username=LDAP_USER_UID) + assert user_groups == ['cn=group1,dc=stackstorm,dc=net'] + assert ldap.ldapobject.SimpleLDAPObject.search_s.call_count == 2 + assert LDAP_USER_UID in backend._user_groups_cache + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT])) +def test_get_user_specifying_account_pattern(): + expected_username = 'unique_username_1' + required_group_dns = [ + 'cn=group3,dc=stackstorm,dc=net', + 'cn=group4,dc=stackstorm,dc=net' + ] + scope = 'subtree' + scope_number = ldap_backend.SEARCH_SCOPES[scope] + + account_pattern = ''' + (& + (objectClass=person) + (| + (accountName={username}) + (mail={username}) + ) + ) + '''.replace('\n', '').replace(' ', '') + expected_account_pattern = account_pattern.format(username=expected_username) + + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + required_group_dns, + LDAP_HOST, + scope=scope, + account_pattern=account_pattern, + ) + connection = mock.MagicMock() + backend._init_connection = mock.MagicMock(return_value=connection) + backend.get_user(expected_username) + + connection.search_s.assert_called_once_with(LDAP_BASE_OU, scope_number, + expected_account_pattern, []) + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, + [('cn=group3,dc=stackstorm,dc=net', ())], + LDAP_USER_SEARCH_RESULT, + [('cn=group4,dc=stackstorm,dc=net', ())] + ])) +def test_get_user_groups_specifying_group_pattern(): + expected_user_dn = 'unique_userdn_1' + expected_username = 'unique_username_2' + required_group_dns = [ + 'cn=group3,dc=stackstorm,dc=net', + 'cn=group4,dc=stackstorm,dc=net' + ] + scope = 'subtree' + scope_number = ldap_backend.SEARCH_SCOPES[scope] + + group_pattern = ''' + (| + (& + (objectClass=group) (| - (& - (objectClass=group) - (| - (memberUserdn={user_dn}) - (uniqueMemberUserdn={user_dn}) - (memberUid={username}) - (uniqueMemberUid={username}) - ) - ) - ) - '''.replace('\n', '').replace(' ', '') - expected_group_pattern = group_pattern.format( - user_dn=expected_user_dn, - username=expected_username, - ) - - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - required_group_dns, - LDAP_HOST, - scope=scope, - group_pattern=group_pattern, - cache_user_groups_response=False, - ) - connection = mock.MagicMock() - backend._init_connection = mock.MagicMock(return_value=connection) - backend._get_user_dn = mock.MagicMock(return_value=expected_user_dn) - - backend.get_user_groups(expected_username) - connection.search_s.assert_called_with(LDAP_BASE_OU, scope_number, - expected_group_pattern, []) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group3,dc=stackstorm,dc=net', ())], - LDAP_USER_SEARCH_RESULT, - [('cn=group4,dc=stackstorm,dc=net', ())] - ])) - def test_get_groups_caching_no_cross_username_cache_polution(self): - required_group_dns = [ - 'cn=group3,dc=stackstorm,dc=net', - 'cn=group4,dc=stackstorm,dc=net' - ] - # Test which verifies that cache items are correctly scoped per username - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - required_group_dns, - LDAP_HOST, - id_attr=LDAP_ID_ATTR, - group_dns_check='or', - cache_user_groups_response=True - ) - user_groups = backend.get_user_groups(username=LDAP_USER_UID) - self.assertEqual(user_groups, ['cn=group3,dc=stackstorm,dc=net']) - self.assertEqual(backend._user_groups_cache[LDAP_USER_UID], - ['cn=group3,dc=stackstorm,dc=net']) - - user_groups = backend.get_user_groups(username=LDAP_USER_UID_2) - self.assertEqual(user_groups, ['cn=group4,dc=stackstorm,dc=net']) - self.assertEqual(backend._user_groups_cache[LDAP_USER_UID_2], - ['cn=group4,dc=stackstorm,dc=net']) - - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) - @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group3,dc=stackstorm,dc=net', ())], - LDAP_USER_SEARCH_RESULT, - [('cn=group4,dc=stackstorm,dc=net', ())] - ])) - def test_get_groups_caching_cache_ttl(self): - required_group_dns = [ - 'cn=group3,dc=stackstorm,dc=net', - 'cn=group4,dc=stackstorm,dc=net' - ] - - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - required_group_dns, - LDAP_HOST, - id_attr=LDAP_ID_ATTR, - group_dns_check='or', - cache_user_groups_response=True, - cache_user_groups_cache_ttl=1 + (memberUserdn={user_dn}) + (uniqueMemberUserdn={user_dn}) + (memberUid={username}) + (uniqueMemberUid={username}) ) - user_groups = backend.get_user_groups(username=LDAP_USER_UID) - self.assertEqual(user_groups, ['cn=group3,dc=stackstorm,dc=net']) - self.assertTrue(LDAP_USER_UID in backend._user_groups_cache) - self.assertEqual(backend._user_groups_cache[LDAP_USER_UID], - ['cn=group3,dc=stackstorm,dc=net']) - - # After 1 second, cache entry should expire and it should result in another search_s call - # which returns group4 - time.sleep(1.5) - - user_groups = backend.get_user_groups(username=LDAP_USER_UID) - self.assertEqual(user_groups, ['cn=group4,dc=stackstorm,dc=net']) - self.assertTrue(LDAP_USER_UID in backend._user_groups_cache) - self.assertEqual(backend._user_groups_cache[LDAP_USER_UID], - ['cn=group4,dc=stackstorm,dc=net']) - - # Cache should now be empty - time.sleep(1.5) - self.assertFalse(LDAP_USER_UID in backend._user_groups_cache) + ) + ) + '''.replace('\n', '').replace(' ', '') + expected_group_pattern = group_pattern.format( + user_dn=expected_user_dn, + username=expected_username, + ) + + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + required_group_dns, + LDAP_HOST, + scope=scope, + group_pattern=group_pattern, + cache_user_groups_response=False, + ) + connection = mock.MagicMock() + backend._init_connection = mock.MagicMock(return_value=connection) + backend._get_user_dn = mock.MagicMock(return_value=expected_user_dn) + + backend.get_user_groups(expected_username) + connection.search_s.assert_called_with(LDAP_BASE_OU, scope_number, + expected_group_pattern, []) + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, + [('cn=group3,dc=stackstorm,dc=net', ())], + LDAP_USER_SEARCH_RESULT, + [('cn=group4,dc=stackstorm,dc=net', ())] + ])) +def test_get_groups_caching_no_cross_username_cache_polution(): + required_group_dns = [ + 'cn=group3,dc=stackstorm,dc=net', + 'cn=group4,dc=stackstorm,dc=net' + ] + # Test which verifies that cache items are correctly scoped per username + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + required_group_dns, + LDAP_HOST, + id_attr=LDAP_ID_ATTR, + group_dns_check='or', + cache_user_groups_response=True + ) + user_groups = backend.get_user_groups(username=LDAP_USER_UID) + assert user_groups == ['cn=group3,dc=stackstorm,dc=net'] + assert backend._user_groups_cache[LDAP_USER_UID] == ['cn=group3,dc=stackstorm,dc=net'] + + user_groups = backend.get_user_groups(username=LDAP_USER_UID_2) + assert user_groups == ['cn=group4,dc=stackstorm,dc=net'] + assert backend._user_groups_cache[LDAP_USER_UID_2] == ['cn=group4,dc=stackstorm,dc=net'] + +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', + mock.MagicMock(return_value=None)) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, 'search_s', + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, + [('cn=group3,dc=stackstorm,dc=net', ())], + LDAP_USER_SEARCH_RESULT, + [('cn=group4,dc=stackstorm,dc=net', ())] + ])) +def test_get_groups_caching_cache_ttl(): + required_group_dns = [ + 'cn=group3,dc=stackstorm,dc=net', + 'cn=group4,dc=stackstorm,dc=net' + ] + + backend = ldap_backend.LDAPAuthenticationBackend( + LDAP_BIND_DN, + LDAP_BIND_PASSWORD, + LDAP_BASE_OU, + required_group_dns, + LDAP_HOST, + id_attr=LDAP_ID_ATTR, + group_dns_check='or', + cache_user_groups_response=True, + cache_user_groups_cache_ttl=1 + ) + user_groups = backend.get_user_groups(username=LDAP_USER_UID) + assert user_groups == ['cn=group3,dc=stackstorm,dc=net'] + assert LDAP_USER_UID in backend._user_groups_cache + assert backend._user_groups_cache[LDAP_USER_UID] == ['cn=group3,dc=stackstorm,dc=net'] + + # After 1 second, cache entry should expire and it should result in another search_s call + # which returns group4 + time.sleep(1.5) + + user_groups = backend.get_user_groups(username=LDAP_USER_UID) + assert user_groups == ['cn=group4,dc=stackstorm,dc=net'] + assert LDAP_USER_UID in backend._user_groups_cache + assert backend._user_groups_cache[LDAP_USER_UID] == ['cn=group4,dc=stackstorm,dc=net'] + + # Cache should now be empty + time.sleep(1.5) + assert LDAP_USER_UID not in backend._user_groups_cache From 97aa3aef362074ff6db867c81729d6cfec4d1f15 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Tue, 22 Oct 2024 17:26:03 -0500 Subject: [PATCH 2/4] fmt test_backend with black --- tests/unit/test_backend.py | 910 ++++++++++++++++++++++--------------- 1 file changed, 550 insertions(+), 360 deletions(-) diff --git a/tests/unit/test_backend.py b/tests/unit/test_backend.py index 4e77aee..00de9da 100644 --- a/tests/unit/test_backend.py +++ b/tests/unit/test_backend.py @@ -24,58 +24,65 @@ from st2auth_ldap import ldap_backend -LDAP_HOST = '127.0.0.1' -LDAP_MULTIPLE_HOSTS = '127.0.0.1,localhost' +LDAP_HOST = "127.0.0.1" +LDAP_MULTIPLE_HOSTS = "127.0.0.1,localhost" LDAPS_PORT = 636 -LDAP_BIND_DN = 'cn=Administrator,cn=users,dc=stackstorm,dc=net' +LDAP_BIND_DN = "cn=Administrator,cn=users,dc=stackstorm,dc=net" LDAP_BIND_PASSWORD = uuid.uuid4().hex -LDAP_GROUP_DNS = ['cn=testers,dc=stackstorm,dc=net'] -LDAP_CACERT = '../fixtures/certs/cacert.pem' -LDAP_CACERT_REAL_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), LDAP_CACERT) -LDAP_BASE_OU = 'dc=stackstorm,dc=net' -LDAP_ID_ATTR = 'uid' -LDAP_USER_UID = 'stanley' -LDAP_USER_UID_2 = 'stanley_2' -LDAP_USER_PASSWD = 'st@nl3y' -LDAP_USER_BAD_PASSWD = 'badbot' +LDAP_GROUP_DNS = ["cn=testers,dc=stackstorm,dc=net"] +LDAP_CACERT = "../fixtures/certs/cacert.pem" +LDAP_CACERT_REAL_PATH = os.path.join( + os.path.dirname(os.path.abspath(__file__)), LDAP_CACERT +) +LDAP_BASE_OU = "dc=stackstorm,dc=net" +LDAP_ID_ATTR = "uid" +LDAP_USER_UID = "stanley" +LDAP_USER_UID_2 = "stanley_2" +LDAP_USER_PASSWD = "st@nl3y" +LDAP_USER_BAD_PASSWD = "badbot" LDAP_USER_INFO_DICT = { - 'accountExpires': ['9223372036854775807'], - 'badPasswordTime': ['0'], - 'badPwdCount': ['0'], - 'cn': ['Tomaz Muraus'], - 'codePage': ['0'], - 'countryCode': ['0'], - 'displayName': ['Tomaz Muraus'], - 'distinguishedName': ['CN=Tomaz Muraus,OU=stormers,DC=stackstorm,DC=net'], - 'givenName': ['Tomaz'], - 'instanceType': ['4'], - 'lastLogoff': ['0'], - 'lastLogon': ['131144315509626450'], - 'lastLogonTimestamp': ['131326807618683640'], - 'logonCount': ['0'], - 'memberOf': ['CN=stormers,OU=groups,DC=stackstorm,DC=net', - 'CN=testers,OU=groups,DC=stackstorm,DC=net'], - 'name': ['Tomaz Muraus'], - 'objectCategory': ['CN=Person,CN=Schema,CN=Configuration,DC=stackstorm,DC=net'], - 'objectClass': ['top', 'person', 'organizationalPerson', 'user'], - 'objectGUID': ['\x1cR\xca\x12\x8a\xda\x8eL\xabe\xcfp\xda\x17H\xf7'], - 'primaryGroupID': ['513'], - 'pwdLastSet': ['131144314220000000'], - 'sAMAccountName': ['tomaz'], - 'sAMAccountType': ['805306368'], - 'sn': ['Muraus'], - 'uSNChanged': ['9835'], - 'uSNCreated': ['3550'], - 'userAccountControl': ['512'], - 'userPrincipalName': ['tomaz@stackstorm.net'], - 'whenChanged': ['20170227145241.0Z'], - 'whenCreated': ['20160731093701.0Z'] + "accountExpires": ["9223372036854775807"], + "badPasswordTime": ["0"], + "badPwdCount": ["0"], + "cn": ["Tomaz Muraus"], + "codePage": ["0"], + "countryCode": ["0"], + "displayName": ["Tomaz Muraus"], + "distinguishedName": ["CN=Tomaz Muraus,OU=stormers,DC=stackstorm,DC=net"], + "givenName": ["Tomaz"], + "instanceType": ["4"], + "lastLogoff": ["0"], + "lastLogon": ["131144315509626450"], + "lastLogonTimestamp": ["131326807618683640"], + "logonCount": ["0"], + "memberOf": [ + "CN=stormers,OU=groups,DC=stackstorm,DC=net", + "CN=testers,OU=groups,DC=stackstorm,DC=net", + ], + "name": ["Tomaz Muraus"], + "objectCategory": ["CN=Person,CN=Schema,CN=Configuration,DC=stackstorm,DC=net"], + "objectClass": ["top", "person", "organizationalPerson", "user"], + "objectGUID": ["\x1cR\xca\x12\x8a\xda\x8eL\xabe\xcfp\xda\x17H\xf7"], + "primaryGroupID": ["513"], + "pwdLastSet": ["131144314220000000"], + "sAMAccountName": ["tomaz"], + "sAMAccountType": ["805306368"], + "sn": ["Muraus"], + "uSNChanged": ["9835"], + "uSNCreated": ["3550"], + "userAccountControl": ["512"], + "userPrincipalName": ["tomaz@stackstorm.net"], + "whenChanged": ["20170227145241.0Z"], + "whenCreated": ["20160731093701.0Z"], } -LDAP_USER_SEARCH_RESULT = [('cn=Stormin Stanley,cn=users,dc=stackstorm,dc=net', - LDAP_USER_INFO_DICT)] -LDAP_GROUP_SEARCH_RESULT = [('cn=testers,dc=stackstorm,dc=net', ()), - ('cn=stormers,dc=stackstorm,dc=net', ())] +LDAP_USER_SEARCH_RESULT = [ + ("cn=Stormin Stanley,cn=users,dc=stackstorm,dc=net", LDAP_USER_INFO_DICT) +] +LDAP_GROUP_SEARCH_RESULT = [ + ("cn=testers,dc=stackstorm,dc=net", ()), + ("cn=stormers,dc=stackstorm,dc=net", ()), +] def test_instantaite_no_group_dns_provided(): @@ -92,12 +99,15 @@ def test_instantaite_no_group_dns_provided(): id_attr=LDAP_ID_ATTR, ) + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT]), +) def test_authenticate(): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -105,18 +115,21 @@ def test_authenticate(): LDAP_BASE_OU, LDAP_GROUP_DNS, LDAP_HOST, - id_attr=LDAP_ID_ATTR + id_attr=LDAP_ID_ATTR, ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_PASSWD) assert authenticated + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT]), +) def test_authenticate_with_multiple_ldap_hosts(): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -124,18 +137,21 @@ def test_authenticate_with_multiple_ldap_hosts(): LDAP_BASE_OU, LDAP_GROUP_DNS, LDAP_MULTIPLE_HOSTS, - id_attr=LDAP_ID_ATTR + id_attr=LDAP_ID_ATTR, ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_PASSWD) assert authenticated + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT]), +) def test_authenticate_without_password(): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -143,15 +159,18 @@ def test_authenticate_without_password(): LDAP_BASE_OU, LDAP_GROUP_DNS, LDAP_HOST, - id_attr=LDAP_ID_ATTR + id_attr=LDAP_ID_ATTR, ) with pytest.raises(ValueError): - backend.authenticate(LDAP_USER_UID, '') + backend.authenticate(LDAP_USER_UID, "") + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(side_effect=Exception())) + ldap.ldapobject.SimpleLDAPObject, + "simple_bind_s", + mock.MagicMock(side_effect=Exception()), +) def test_authenticate_failure_bad_bind_cred(): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -159,18 +178,23 @@ def test_authenticate_failure_bad_bind_cred(): LDAP_BASE_OU, LDAP_GROUP_DNS, LDAP_HOST, - id_attr=LDAP_ID_ATTR + id_attr=LDAP_ID_ATTR, ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) assert not authenticated + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(side_effect=[None, Exception()])) + ldap.ldapobject.SimpleLDAPObject, + "simple_bind_s", + mock.MagicMock(side_effect=[None, Exception()]), +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT]), +) def test_authenticate_failure_bad_user_password(): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -178,18 +202,21 @@ def test_authenticate_failure_bad_user_password(): LDAP_BASE_OU, LDAP_GROUP_DNS, LDAP_HOST, - id_attr=LDAP_ID_ATTR + id_attr=LDAP_ID_ATTR, ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) assert not authenticated + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, []])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, []]), +) def test_authenticate_failure_non_group_member_no_groups(): # User is not member of any of the required group backend = ldap_backend.LDAPAuthenticationBackend( @@ -199,7 +226,7 @@ def test_authenticate_failure_non_group_member_no_groups(): LDAP_GROUP_DNS, LDAP_HOST, id_attr=LDAP_ID_ATTR, - group_dns_check='and' + group_dns_check="and", ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) @@ -212,19 +239,23 @@ def test_authenticate_failure_non_group_member_no_groups(): LDAP_GROUP_DNS, LDAP_HOST, id_attr=LDAP_ID_ATTR, - group_dns_check='or' + group_dns_check="or", ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) assert not authenticated + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ())]])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock( + side_effect=[LDAP_USER_SEARCH_RESULT, [("cn=group1,dc=stackstorm,dc=net", ())]] + ), +) def test_authenticatefailure_non_group_member_non_required_group(): # User is member of a group which is not required backend = ldap_backend.LDAPAuthenticationBackend( @@ -234,7 +265,7 @@ def test_authenticatefailure_non_group_member_non_required_group(): LDAP_GROUP_DNS, LDAP_HOST, id_attr=LDAP_ID_ATTR, - group_dns_check='and' + group_dns_check="and", ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) @@ -247,27 +278,36 @@ def test_authenticatefailure_non_group_member_non_required_group(): LDAP_GROUP_DNS, LDAP_HOST, id_attr=LDAP_ID_ATTR, - group_dns_check='or' + group_dns_check="or", ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) assert not authenticated + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ()), - ('cn=group3,dc=stackstorm,dc=net', ())]])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock( + side_effect=[ + LDAP_USER_SEARCH_RESULT, + [ + ("cn=group1,dc=stackstorm,dc=net", ()), + ("cn=group3,dc=stackstorm,dc=net", ()), + ], + ] + ), +) def test_authenticate_and_behavior_failure_non_group_member_of_all_required_groups_1(): # User is member of two of the required groups (1 and 3) but not all three of them # (1, 2, 3) required_group_dns = [ - 'cn=group1,dc=stackstorm,dc=net', - 'cn=group2,dc=stackstorm,dc=net', - 'cn=group3,dc=stackstorm,dc=net', + "cn=group1,dc=stackstorm,dc=net", + "cn=group2,dc=stackstorm,dc=net", + "cn=group3,dc=stackstorm,dc=net", ] backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -276,25 +316,34 @@ def test_authenticate_and_behavior_failure_non_group_member_of_all_required_grou required_group_dns, LDAP_HOST, id_attr=LDAP_ID_ATTR, - group_dns_check='and' + group_dns_check="and", ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) assert not authenticated + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ()), - ('cn=group3,dc=stackstorm,dc=net', ())]])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock( + side_effect=[ + LDAP_USER_SEARCH_RESULT, + [ + ("cn=group1,dc=stackstorm,dc=net", ()), + ("cn=group3,dc=stackstorm,dc=net", ()), + ], + ] + ), +) def test_authenticate_and_behavior_failure_non_group_member_of_all_required_groups_2(): # User is member of two of the groups, but none of them are required required_group_dns = [ - 'cn=group7,dc=stackstorm,dc=net', - 'cn=group8,dc=stackstorm,dc=net' + "cn=group7,dc=stackstorm,dc=net", + "cn=group8,dc=stackstorm,dc=net", ] backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -303,30 +352,39 @@ def test_authenticate_and_behavior_failure_non_group_member_of_all_required_grou required_group_dns, LDAP_HOST, id_attr=LDAP_ID_ATTR, - group_dns_check='and' + group_dns_check="and", ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) assert not authenticated + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ()), - ('cn=group2,dc=stackstorm,dc=net', ()), - ('cn=group3,dc=stackstorm,dc=net', ()), - ('cn=group4,dc=stackstorm,dc=net', ())]])) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock( + side_effect=[ + LDAP_USER_SEARCH_RESULT, + [ + ("cn=group1,dc=stackstorm,dc=net", ()), + ("cn=group2,dc=stackstorm,dc=net", ()), + ("cn=group3,dc=stackstorm,dc=net", ()), + ("cn=group4,dc=stackstorm,dc=net", ()), + ], + ] + ), +) def test_authenticate_and_behavior_failure_non_group_member_of_all_required_groups_3(): # User is member of two of the required groups and two non-required, but not # all of the required groups required_group_dns = [ - 'cn=group1,dc=stackstorm,dc=net', - 'cn=group2,dc=stackstorm,dc=net', - 'cn=group5,dc=stackstorm,dc=net', - 'cn=group6,dc=stackstorm,dc=net', + "cn=group1,dc=stackstorm,dc=net", + "cn=group2,dc=stackstorm,dc=net", + "cn=group5,dc=stackstorm,dc=net", + "cn=group6,dc=stackstorm,dc=net", ] backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -335,31 +393,40 @@ def test_authenticate_and_behavior_failure_non_group_member_of_all_required_grou required_group_dns, LDAP_HOST, id_attr=LDAP_ID_ATTR, - group_dns_check='and' + group_dns_check="and", ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) assert not authenticated + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ()), - ('cn=group2,dc=stackstorm,dc=net', ()), - ('cn=group3,dc=stackstorm,dc=net', ()), - ('cn=group4,dc=stackstorm,dc=net', ())]])) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) +@mock.patch.object( + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock( + side_effect=[ + LDAP_USER_SEARCH_RESULT, + [ + ("cn=group1,dc=stackstorm,dc=net", ()), + ("cn=group2,dc=stackstorm,dc=net", ()), + ("cn=group3,dc=stackstorm,dc=net", ()), + ("cn=group4,dc=stackstorm,dc=net", ()), + ], + ] + ), +) def test_authenticate_and_is_default_behavior_non_group_member_of_all_required_groups(): # User is member of two of the required groups and two non-required, but not # all of the required groups # Verify "and" is a default group_dns_check_behavior required_group_dns = [ - 'cn=group1,dc=stackstorm,dc=net', - 'cn=group2,dc=stackstorm,dc=net', - 'cn=group5,dc=stackstorm,dc=net', - 'cn=group6,dc=stackstorm,dc=net', + "cn=group1,dc=stackstorm,dc=net", + "cn=group2,dc=stackstorm,dc=net", + "cn=group5,dc=stackstorm,dc=net", + "cn=group6,dc=stackstorm,dc=net", ] backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -367,24 +434,26 @@ def test_authenticate_and_is_default_behavior_non_group_member_of_all_required_g LDAP_BASE_OU, required_group_dns, LDAP_HOST, - id_attr=LDAP_ID_ATTR + id_attr=LDAP_ID_ATTR, ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) assert not authenticated + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ())]])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock( + side_effect=[LDAP_USER_SEARCH_RESULT, [("cn=group1,dc=stackstorm,dc=net", ())]] + ), +) def test_authenticate_or_behavior_success_member_of_single_group_1(): # User is a memeber of single of possible required groups - required_group_dns = [ - 'cn=group1,dc=stackstorm,dc=net' - ] + required_group_dns = ["cn=group1,dc=stackstorm,dc=net"] backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, LDAP_BIND_PASSWORD, @@ -392,26 +461,30 @@ def test_authenticate_or_behavior_success_member_of_single_group_1(): required_group_dns, LDAP_HOST, id_attr=LDAP_ID_ATTR, - group_dns_check='or' + group_dns_check="or", ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) assert authenticated + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ())]])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock( + side_effect=[LDAP_USER_SEARCH_RESULT, [("cn=group1,dc=stackstorm,dc=net", ())]] + ), +) def test_authenticate_or_behavior_success_member_of_single_group_2(): # User is a memeber of single of possible required groups required_group_dns = [ - 'cn=group1,dc=stackstorm,dc=net', - 'cn=group2,dc=stackstorm,dc=net', - 'cn=group3,dc=stackstorm,dc=net', - 'cn=group4,dc=stackstorm,dc=net' + "cn=group1,dc=stackstorm,dc=net", + "cn=group2,dc=stackstorm,dc=net", + "cn=group3,dc=stackstorm,dc=net", + "cn=group4,dc=stackstorm,dc=net", ] backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -420,26 +493,30 @@ def test_authenticate_or_behavior_success_member_of_single_group_2(): required_group_dns, LDAP_HOST, id_attr=LDAP_ID_ATTR, - group_dns_check='or' + group_dns_check="or", ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) assert authenticated + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group3,dc=stackstorm,dc=net', ())]])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock( + side_effect=[LDAP_USER_SEARCH_RESULT, [("cn=group3,dc=stackstorm,dc=net", ())]] + ), +) def test_authenticate_or_behavior_success_member_of_single_group_2b(): # User is a memeber of single of possible required groups required_group_dns = [ - 'cn=group1,dc=stackstorm,dc=net', - 'cn=group2,dc=stackstorm,dc=net', - 'cn=group3,dc=stackstorm,dc=net', - 'cn=group4,dc=stackstorm,dc=net' + "cn=group1,dc=stackstorm,dc=net", + "cn=group2,dc=stackstorm,dc=net", + "cn=group3,dc=stackstorm,dc=net", + "cn=group4,dc=stackstorm,dc=net", ] backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -448,28 +525,37 @@ def test_authenticate_or_behavior_success_member_of_single_group_2b(): required_group_dns, LDAP_HOST, id_attr=LDAP_ID_ATTR, - group_dns_check='or' + group_dns_check="or", ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) assert authenticated + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ()), - ('cn=group4,dc=stackstorm,dc=net', ())]])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock( + side_effect=[ + LDAP_USER_SEARCH_RESULT, + [ + ("cn=group1,dc=stackstorm,dc=net", ()), + ("cn=group4,dc=stackstorm,dc=net", ()), + ], + ] + ), +) def test_authenticate_or_behavior_success_member_of_multiple_groups_1(): # User is a memeber of multiple of required groups required_group_dns = [ - 'cn=group1,dc=stackstorm,dc=net', - 'cn=group2,dc=stackstorm,dc=net', - 'cn=group3,dc=stackstorm,dc=net', - 'cn=group4,dc=stackstorm,dc=net', - 'cn=group5,dc=stackstorm,dc=net' + "cn=group1,dc=stackstorm,dc=net", + "cn=group2,dc=stackstorm,dc=net", + "cn=group3,dc=stackstorm,dc=net", + "cn=group4,dc=stackstorm,dc=net", + "cn=group5,dc=stackstorm,dc=net", ] backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -478,25 +564,34 @@ def test_authenticate_or_behavior_success_member_of_multiple_groups_1(): required_group_dns, LDAP_HOST, id_attr=LDAP_ID_ATTR, - group_dns_check='or' + group_dns_check="or", ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) assert authenticated + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ()), - ('cn=group4,dc=stackstorm,dc=net', ())]])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock( + side_effect=[ + LDAP_USER_SEARCH_RESULT, + [ + ("cn=group1,dc=stackstorm,dc=net", ()), + ("cn=group4,dc=stackstorm,dc=net", ()), + ], + ] + ), +) def test_authenticate_or_behavior_success_member_of_multiple_groups_2(): # User is a memeber of multiple of required groups required_group_dns = [ - 'cn=group1,dc=stackstorm,dc=net', - 'cn=group4,dc=stackstorm,dc=net' + "cn=group1,dc=stackstorm,dc=net", + "cn=group4,dc=stackstorm,dc=net", ] backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -505,26 +600,33 @@ def test_authenticate_or_behavior_success_member_of_multiple_groups_2(): required_group_dns, LDAP_HOST, id_attr=LDAP_ID_ATTR, - group_dns_check='or' + group_dns_check="or", ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) assert authenticated + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ()), - ('cn=group3,dc=stackstorm,dc=net', ()), - ('cn=group6,dc=stackstorm,dc=net', ())]])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock( + side_effect=[ + LDAP_USER_SEARCH_RESULT, + [ + ("cn=group1,dc=stackstorm,dc=net", ()), + ("cn=group3,dc=stackstorm,dc=net", ()), + ("cn=group6,dc=stackstorm,dc=net", ()), + ], + ] + ), +) def test_authenticate_or_behavior_success_member_of_multiple_groups_3(): # User is a memeber of multiple of required groups - required_group_dns = [ - 'cn=group3,dc=stackstorm,dc=net' - ] + required_group_dns = ["cn=group3,dc=stackstorm,dc=net"] backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, LDAP_BIND_PASSWORD, @@ -532,26 +634,35 @@ def test_authenticate_or_behavior_success_member_of_multiple_groups_3(): required_group_dns, LDAP_HOST, id_attr=LDAP_ID_ATTR, - group_dns_check='or' + group_dns_check="or", ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) assert authenticated + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ()), - ('cn=group3,dc=stackstorm,dc=net', ()), - ('cn=group6,dc=stackstorm,dc=net', ())]])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock( + side_effect=[ + LDAP_USER_SEARCH_RESULT, + [ + ("cn=group1,dc=stackstorm,dc=net", ()), + ("cn=group3,dc=stackstorm,dc=net", ()), + ("cn=group6,dc=stackstorm,dc=net", ()), + ], + ] + ), +) def test_authenticate_or_behavior_success_member_of_multiple_groups_3b(): # User is a memeber of multiple of required groups required_group_dns = [ - 'cn=group3,dc=stackstorm,dc=net', - 'cn=group1,dc=stackstorm,dc=net' + "cn=group3,dc=stackstorm,dc=net", + "cn=group1,dc=stackstorm,dc=net", ] backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -560,18 +671,21 @@ def test_authenticate_or_behavior_success_member_of_multiple_groups_3b(): required_group_dns, LDAP_HOST, id_attr=LDAP_ID_ATTR, - group_dns_check='or' + group_dns_check="or", ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) assert authenticated + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT]), +) def test_ssl_authenticate(): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -581,18 +695,23 @@ def test_ssl_authenticate(): LDAP_HOST, port=LDAPS_PORT, use_ssl=True, - id_attr=LDAP_ID_ATTR + id_attr=LDAP_ID_ATTR, ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_PASSWD) assert authenticated + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(side_effect=[None, Exception()])) + ldap.ldapobject.SimpleLDAPObject, + "simple_bind_s", + mock.MagicMock(side_effect=[None, Exception()]), +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT]), +) def test_ssl_authenticate_failure(): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -602,18 +721,21 @@ def test_ssl_authenticate_failure(): LDAP_HOST, port=LDAPS_PORT, use_ssl=True, - id_attr=LDAP_ID_ATTR + id_attr=LDAP_ID_ATTR, ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) assert not authenticated + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT]), +) def test_ssl_authenticate_validate_cert(): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -624,21 +746,24 @@ def test_ssl_authenticate_validate_cert(): port=LDAPS_PORT, use_ssl=True, cacert=LDAP_CACERT_REAL_PATH, - id_attr=LDAP_ID_ATTR + id_attr=LDAP_ID_ATTR, ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_PASSWD) assert authenticated + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'start_tls_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "start_tls_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT]), +) def test_tls_authenticate(): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -647,21 +772,26 @@ def test_tls_authenticate(): LDAP_GROUP_DNS, LDAP_HOST, use_tls=True, - id_attr=LDAP_ID_ATTR + id_attr=LDAP_ID_ATTR, ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_PASSWD) assert authenticated + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'start_tls_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "start_tls_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(side_effect=[None, Exception()])) + ldap.ldapobject.SimpleLDAPObject, + "simple_bind_s", + mock.MagicMock(side_effect=[None, Exception()]), +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT]), +) def test_tls_authenticate_failure(): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -670,21 +800,24 @@ def test_tls_authenticate_failure(): LDAP_GROUP_DNS, LDAP_HOST, use_tls=True, - id_attr=LDAP_ID_ATTR + id_attr=LDAP_ID_ATTR, ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) assert not authenticated + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'start_tls_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "start_tls_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT]), +) def test_tls_authenticate_validate_cert(): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -694,18 +827,21 @@ def test_tls_authenticate_validate_cert(): LDAP_HOST, use_tls=True, cacert=LDAP_CACERT_REAL_PATH, - id_attr=LDAP_ID_ATTR + id_attr=LDAP_ID_ATTR, ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_PASSWD) assert authenticated + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, []])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, []]), +) def test_special_characters_in_username_are_escaped(): # User is not member of any of the required group backend = ldap_backend.LDAPAuthenticationBackend( @@ -714,16 +850,16 @@ def test_special_characters_in_username_are_escaped(): LDAP_BASE_OU, LDAP_GROUP_DNS, LDAP_HOST, - id_attr=LDAP_ID_ATTR + id_attr=LDAP_ID_ATTR, ) values = [ - ('stanleyA', 'stanleyA'), - ('stanley!@?.,&', 'stanley!@?.,&'), + ("stanleyA", "stanleyA"), + ("stanley!@?.,&", "stanley!@?.,&"), # Special characters () should be escaped - ('(stanley)', '\\28stanley\\29'), + ("(stanley)", "\\28stanley\\29"), # Special characters () should be escaped - ('(stanley=)', '\\28stanley=\\29'), + ("(stanley=)", "\\28stanley=\\29"), ] for actual_username, expected_username in values: @@ -741,14 +877,18 @@ def test_special_characters_in_username_are_escaped(): assert f"(memberUid={expected_username})" in filter_call_value ldap.ldapobject.SimpleLDAPObject.search_s = mock.MagicMock( - side_effect=[LDAP_USER_SEARCH_RESULT, []]) + side_effect=[LDAP_USER_SEARCH_RESULT, []] + ) + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT]), +) def test_get_user(): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -756,21 +896,24 @@ def test_get_user(): LDAP_BASE_OU, LDAP_GROUP_DNS, LDAP_HOST, - id_attr=LDAP_ID_ATTR + id_attr=LDAP_ID_ATTR, ) user_info = backend.get_user(username=LDAP_USER_UID) - assert user_info['cn'] == ['Tomaz Muraus'] - assert user_info['displayName'] == ['Tomaz Muraus'] - assert user_info['givenName'] == ['Tomaz'] - assert user_info['primaryGroupID'] == ['513'] + assert user_info["cn"] == ["Tomaz Muraus"] + assert user_info["displayName"] == ["Tomaz Muraus"] + assert user_info["givenName"] == ["Tomaz"] + assert user_info["primaryGroupID"] == ["513"] + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[2 * LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock(side_effect=[2 * LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT]), +) def test_get_user_multiple_results(): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -778,18 +921,21 @@ def test_get_user_multiple_results(): LDAP_BASE_OU, LDAP_GROUP_DNS, LDAP_HOST, - id_attr=LDAP_ID_ATTR + id_attr=LDAP_ID_ATTR, ) user_info = backend.get_user(username=LDAP_USER_UID) assert user_info is None + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT]), +) def test_get_user_groups(): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -797,31 +943,32 @@ def test_get_user_groups(): LDAP_BASE_OU, LDAP_GROUP_DNS, LDAP_HOST, - id_attr=LDAP_ID_ATTR + id_attr=LDAP_ID_ATTR, ) - expected = [ - 'cn=testers,dc=stackstorm,dc=net', - 'cn=stormers,dc=stackstorm,dc=net' - ] + expected = ["cn=testers,dc=stackstorm,dc=net", "cn=stormers,dc=stackstorm,dc=net"] groups = backend.get_user_groups(username=LDAP_USER_UID) assert groups == expected + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ())], - LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ())] - ])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock( + side_effect=[ + LDAP_USER_SEARCH_RESULT, + [("cn=group1,dc=stackstorm,dc=net", ())], + LDAP_USER_SEARCH_RESULT, + [("cn=group1,dc=stackstorm,dc=net", ())], + ] + ), +) def test_authenticate_and_get_user_groups_caching_disabled(): - required_group_dns = [ - 'cn=group1,dc=stackstorm,dc=net' - ] + required_group_dns = ["cn=group1,dc=stackstorm,dc=net"] backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -830,8 +977,8 @@ def test_authenticate_and_get_user_groups_caching_disabled(): required_group_dns, LDAP_HOST, id_attr=LDAP_ID_ATTR, - group_dns_check='or', - cache_user_groups_response=False + group_dns_check="or", + cache_user_groups_response=False, ) assert ldap.ldapobject.SimpleLDAPObject.search_s.call_count == 0 @@ -843,24 +990,28 @@ def test_authenticate_and_get_user_groups_caching_disabled(): assert ldap.ldapobject.SimpleLDAPObject.search_s.call_count == 2 user_groups = backend.get_user_groups(username=LDAP_USER_UID) - assert user_groups == ['cn=group1,dc=stackstorm,dc=net'] + assert user_groups == ["cn=group1,dc=stackstorm,dc=net"] assert ldap.ldapobject.SimpleLDAPObject.search_s.call_count == 4 assert backend._user_groups_cache is None + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ())], - LDAP_USER_SEARCH_RESULT, - [('cn=group1,dc=stackstorm,dc=net', ())] - ])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock( + side_effect=[ + LDAP_USER_SEARCH_RESULT, + [("cn=group1,dc=stackstorm,dc=net", ())], + LDAP_USER_SEARCH_RESULT, + [("cn=group1,dc=stackstorm,dc=net", ())], + ] + ), +) def test_authenticate_and_get_user_groups_caching_enabled(): - required_group_dns = [ - 'cn=group1,dc=stackstorm,dc=net' - ] + required_group_dns = ["cn=group1,dc=stackstorm,dc=net"] backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -869,8 +1020,8 @@ def test_authenticate_and_get_user_groups_caching_enabled(): required_group_dns, LDAP_HOST, id_attr=LDAP_ID_ATTR, - group_dns_check='or', - cache_user_groups_response=True + group_dns_check="or", + cache_user_groups_response=True, ) assert ldap.ldapobject.SimpleLDAPObject.search_s.call_count == 0 @@ -880,26 +1031,29 @@ def test_authenticate_and_get_user_groups_caching_enabled(): assert ldap.ldapobject.SimpleLDAPObject.search_s.call_count == 2 user_groups = backend.get_user_groups(username=LDAP_USER_UID) - assert user_groups == ['cn=group1,dc=stackstorm,dc=net'] + assert user_groups == ["cn=group1,dc=stackstorm,dc=net"] assert ldap.ldapobject.SimpleLDAPObject.search_s.call_count == 2 assert LDAP_USER_UID in backend._user_groups_cache + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT]), +) def test_get_user_specifying_account_pattern(): - expected_username = 'unique_username_1' + expected_username = "unique_username_1" required_group_dns = [ - 'cn=group3,dc=stackstorm,dc=net', - 'cn=group4,dc=stackstorm,dc=net' + "cn=group3,dc=stackstorm,dc=net", + "cn=group4,dc=stackstorm,dc=net", ] - scope = 'subtree' + scope = "subtree" scope_number = ldap_backend.SEARCH_SCOPES[scope] - account_pattern = ''' + account_pattern = """ (& (objectClass=person) (| @@ -907,7 +1061,11 @@ def test_get_user_specifying_account_pattern(): (mail={username}) ) ) - '''.replace('\n', '').replace(' ', '') + """.replace( + "\n", "" + ).replace( + " ", "" + ) expected_account_pattern = account_pattern.format(username=expected_username) backend = ldap_backend.LDAPAuthenticationBackend( @@ -923,30 +1081,37 @@ def test_get_user_specifying_account_pattern(): backend._init_connection = mock.MagicMock(return_value=connection) backend.get_user(expected_username) - connection.search_s.assert_called_once_with(LDAP_BASE_OU, scope_number, - expected_account_pattern, []) + connection.search_s.assert_called_once_with( + LDAP_BASE_OU, scope_number, expected_account_pattern, [] + ) + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group3,dc=stackstorm,dc=net', ())], - LDAP_USER_SEARCH_RESULT, - [('cn=group4,dc=stackstorm,dc=net', ())] - ])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock( + side_effect=[ + LDAP_USER_SEARCH_RESULT, + [("cn=group3,dc=stackstorm,dc=net", ())], + LDAP_USER_SEARCH_RESULT, + [("cn=group4,dc=stackstorm,dc=net", ())], + ] + ), +) def test_get_user_groups_specifying_group_pattern(): - expected_user_dn = 'unique_userdn_1' - expected_username = 'unique_username_2' + expected_user_dn = "unique_userdn_1" + expected_username = "unique_username_2" required_group_dns = [ - 'cn=group3,dc=stackstorm,dc=net', - 'cn=group4,dc=stackstorm,dc=net' + "cn=group3,dc=stackstorm,dc=net", + "cn=group4,dc=stackstorm,dc=net", ] - scope = 'subtree' + scope = "subtree" scope_number = ldap_backend.SEARCH_SCOPES[scope] - group_pattern = ''' + group_pattern = """ (| (& (objectClass=group) @@ -958,7 +1123,11 @@ def test_get_user_groups_specifying_group_pattern(): ) ) ) - '''.replace('\n', '').replace(' ', '') + """.replace( + "\n", "" + ).replace( + " ", "" + ) expected_group_pattern = group_pattern.format( user_dn=expected_user_dn, username=expected_username, @@ -979,23 +1148,30 @@ def test_get_user_groups_specifying_group_pattern(): backend._get_user_dn = mock.MagicMock(return_value=expected_user_dn) backend.get_user_groups(expected_username) - connection.search_s.assert_called_with(LDAP_BASE_OU, scope_number, - expected_group_pattern, []) + connection.search_s.assert_called_with( + LDAP_BASE_OU, scope_number, expected_group_pattern, [] + ) + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group3,dc=stackstorm,dc=net', ())], - LDAP_USER_SEARCH_RESULT, - [('cn=group4,dc=stackstorm,dc=net', ())] - ])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock( + side_effect=[ + LDAP_USER_SEARCH_RESULT, + [("cn=group3,dc=stackstorm,dc=net", ())], + LDAP_USER_SEARCH_RESULT, + [("cn=group4,dc=stackstorm,dc=net", ())], + ] + ), +) def test_get_groups_caching_no_cross_username_cache_polution(): required_group_dns = [ - 'cn=group3,dc=stackstorm,dc=net', - 'cn=group4,dc=stackstorm,dc=net' + "cn=group3,dc=stackstorm,dc=net", + "cn=group4,dc=stackstorm,dc=net", ] # Test which verifies that cache items are correctly scoped per username backend = ldap_backend.LDAPAuthenticationBackend( @@ -1005,31 +1181,41 @@ def test_get_groups_caching_no_cross_username_cache_polution(): required_group_dns, LDAP_HOST, id_attr=LDAP_ID_ATTR, - group_dns_check='or', - cache_user_groups_response=True + group_dns_check="or", + cache_user_groups_response=True, ) user_groups = backend.get_user_groups(username=LDAP_USER_UID) - assert user_groups == ['cn=group3,dc=stackstorm,dc=net'] - assert backend._user_groups_cache[LDAP_USER_UID] == ['cn=group3,dc=stackstorm,dc=net'] + assert user_groups == ["cn=group3,dc=stackstorm,dc=net"] + assert backend._user_groups_cache[LDAP_USER_UID] == [ + "cn=group3,dc=stackstorm,dc=net" + ] user_groups = backend.get_user_groups(username=LDAP_USER_UID_2) - assert user_groups == ['cn=group4,dc=stackstorm,dc=net'] - assert backend._user_groups_cache[LDAP_USER_UID_2] == ['cn=group4,dc=stackstorm,dc=net'] + assert user_groups == ["cn=group4,dc=stackstorm,dc=net"] + assert backend._user_groups_cache[LDAP_USER_UID_2] == [ + "cn=group4,dc=stackstorm,dc=net" + ] + @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'simple_bind_s', - mock.MagicMock(return_value=None)) + ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +) @mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, 'search_s', - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, - [('cn=group3,dc=stackstorm,dc=net', ())], - LDAP_USER_SEARCH_RESULT, - [('cn=group4,dc=stackstorm,dc=net', ())] - ])) + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mock.MagicMock( + side_effect=[ + LDAP_USER_SEARCH_RESULT, + [("cn=group3,dc=stackstorm,dc=net", ())], + LDAP_USER_SEARCH_RESULT, + [("cn=group4,dc=stackstorm,dc=net", ())], + ] + ), +) def test_get_groups_caching_cache_ttl(): required_group_dns = [ - 'cn=group3,dc=stackstorm,dc=net', - 'cn=group4,dc=stackstorm,dc=net' + "cn=group3,dc=stackstorm,dc=net", + "cn=group4,dc=stackstorm,dc=net", ] backend = ldap_backend.LDAPAuthenticationBackend( @@ -1039,23 +1225,27 @@ def test_get_groups_caching_cache_ttl(): required_group_dns, LDAP_HOST, id_attr=LDAP_ID_ATTR, - group_dns_check='or', + group_dns_check="or", cache_user_groups_response=True, - cache_user_groups_cache_ttl=1 + cache_user_groups_cache_ttl=1, ) user_groups = backend.get_user_groups(username=LDAP_USER_UID) - assert user_groups == ['cn=group3,dc=stackstorm,dc=net'] + assert user_groups == ["cn=group3,dc=stackstorm,dc=net"] assert LDAP_USER_UID in backend._user_groups_cache - assert backend._user_groups_cache[LDAP_USER_UID] == ['cn=group3,dc=stackstorm,dc=net'] + assert backend._user_groups_cache[LDAP_USER_UID] == [ + "cn=group3,dc=stackstorm,dc=net" + ] # After 1 second, cache entry should expire and it should result in another search_s call # which returns group4 time.sleep(1.5) user_groups = backend.get_user_groups(username=LDAP_USER_UID) - assert user_groups == ['cn=group4,dc=stackstorm,dc=net'] + assert user_groups == ["cn=group4,dc=stackstorm,dc=net"] assert LDAP_USER_UID in backend._user_groups_cache - assert backend._user_groups_cache[LDAP_USER_UID] == ['cn=group4,dc=stackstorm,dc=net'] + assert backend._user_groups_cache[LDAP_USER_UID] == [ + "cn=group4,dc=stackstorm,dc=net" + ] # Cache should now be empty time.sleep(1.5) From 6180b6ba9d29a131997db52349696114031a6f17 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 23 Oct 2024 11:55:21 -0500 Subject: [PATCH 3/4] Refactor test_backend to use pytest-mock Parametrization also doesn't play nicely with @mock.patch.object decorators, so this refactor paves the way for additional parametrization in future PRs. --- test-requirements.txt | 1 + tests/unit/test_backend.py | 726 ++++++++++++++++++------------------- 2 files changed, 345 insertions(+), 382 deletions(-) diff --git a/test-requirements.txt b/test-requirements.txt index a37080f..3f072e6 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -4,6 +4,7 @@ mock==5.1.0 pylint~=3.1.0 pylint-plugin-utils>=0.4 pytest +pytest-mock st2-auth-backend-flat-file st2flake8>0.1 tox diff --git a/tests/unit/test_backend.py b/tests/unit/test_backend.py index 00de9da..58cb6ee 100644 --- a/tests/unit/test_backend.py +++ b/tests/unit/test_backend.py @@ -16,10 +16,10 @@ import os import time import uuid -from unittest import mock import ldap import pytest +from pytest_mock import MockerFixture, MockType from st2auth_ldap import ldap_backend @@ -85,6 +85,47 @@ ] +@pytest.fixture +def mock_ldap_bind(mocker: MockerFixture) -> MockType: + return mocker.patch.object( + ldap.ldapobject.SimpleLDAPObject, + "simple_bind_s", + mocker.MagicMock(return_value=None), # bind/auth always succeeds + ) + + +@pytest.fixture +def mock_ldap_auth_failure(mocker: MockerFixture) -> MockType: + return mocker.patch.object( + ldap.ldapobject.SimpleLDAPObject, + "simple_bind_s", + mocker.MagicMock( + side_effect=[ + None, # bind succeeds + Exception(), # auth fails + ] + ), + ) + + +@pytest.fixture +def mock_ldap_start_tls(mocker: MockerFixture) -> MockType: + return mocker.patch.object( + ldap.ldapobject.SimpleLDAPObject, + "start_tls_s", + mocker.MagicMock(return_value=None), + ) + + +@pytest.fixture +def mock_ldap_search(mocker: MockerFixture, request: pytest.FixtureRequest) -> MockType: + return mocker.patch.object( + ldap.ldapobject.SimpleLDAPObject, + "search_s", + mocker.MagicMock(side_effect=request.param), + ) + + def test_instantaite_no_group_dns_provided(): # User is member of two of the groups, but none of them are required required_group_dns = [] @@ -100,15 +141,12 @@ def test_instantaite_no_group_dns_provided(): ) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +@pytest.mark.parametrize( + "mock_ldap_search", + ([LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT],), + indirect=True, ) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT]), -) -def test_authenticate(): +def test_authenticate(mock_ldap_bind: MockType, mock_ldap_search: MockType): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, LDAP_BIND_PASSWORD, @@ -122,15 +160,14 @@ def test_authenticate(): assert authenticated -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT]), +@pytest.mark.parametrize( + "mock_ldap_search", + ([LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT],), + indirect=True, ) -def test_authenticate_with_multiple_ldap_hosts(): +def test_authenticate_with_multiple_ldap_hosts( + mock_ldap_bind: MockType, mock_ldap_search: MockType +): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, LDAP_BIND_PASSWORD, @@ -144,15 +181,14 @@ def test_authenticate_with_multiple_ldap_hosts(): assert authenticated -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT]), +@pytest.mark.parametrize( + "mock_ldap_search", + ([LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT],), + indirect=True, ) -def test_authenticate_without_password(): +def test_authenticate_without_password( + mock_ldap_bind: MockType, mock_ldap_search: MockType +): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, LDAP_BIND_PASSWORD, @@ -166,12 +202,12 @@ def test_authenticate_without_password(): backend.authenticate(LDAP_USER_UID, "") -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "simple_bind_s", - mock.MagicMock(side_effect=Exception()), -) -def test_authenticate_failure_bad_bind_cred(): +def test_authenticate_failure_bad_bind_cred(mocker: MockerFixture): + mocker.patch.object( + ldap.ldapobject.SimpleLDAPObject, + "simple_bind_s", + mocker.MagicMock(side_effect=Exception()), # bind fails + ) backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, LDAP_BIND_PASSWORD, @@ -185,17 +221,14 @@ def test_authenticate_failure_bad_bind_cred(): assert not authenticated -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "simple_bind_s", - mock.MagicMock(side_effect=[None, Exception()]), -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT]), +@pytest.mark.parametrize( + "mock_ldap_search", + ([LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT],), + indirect=True, ) -def test_authenticate_failure_bad_user_password(): +def test_authenticate_failure_bad_user_password( + mock_ldap_auth_failure: MockType, mock_ldap_search: MockType +): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, LDAP_BIND_PASSWORD, @@ -209,15 +242,17 @@ def test_authenticate_failure_bad_user_password(): assert not authenticated -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, []]), +@pytest.mark.parametrize( + "group_dns_check,mock_ldap_search", + ( + pytest.param(group_dns_check, [LDAP_USER_SEARCH_RESULT, []], id=group_dns_check) + for group_dns_check in ("and", "or") + ), + indirect=["mock_ldap_search"], ) -def test_authenticate_failure_non_group_member_no_groups(): +def test_authenticate_failure_non_group_member_no_groups( + group_dns_check: str, mock_ldap_bind: MockType, mock_ldap_search: MockType +): # User is not member of any of the required group backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -226,37 +261,28 @@ def test_authenticate_failure_non_group_member_no_groups(): LDAP_GROUP_DNS, LDAP_HOST, id_attr=LDAP_ID_ATTR, - group_dns_check="and", - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) - assert not authenticated - - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - LDAP_GROUP_DNS, - LDAP_HOST, - id_attr=LDAP_ID_ATTR, - group_dns_check="or", + group_dns_check=group_dns_check, ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) assert not authenticated -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock( - side_effect=[LDAP_USER_SEARCH_RESULT, [("cn=group1,dc=stackstorm,dc=net", ())]] +@pytest.mark.parametrize( + "group_dns_check,mock_ldap_search", + ( + pytest.param( + group_dns_check, + [LDAP_USER_SEARCH_RESULT, [("cn=group1,dc=stackstorm,dc=net", ())]], + id=group_dns_check, + ) + for group_dns_check in ("and", "or") ), + indirect=["mock_ldap_search"], ) -def test_authenticatefailure_non_group_member_non_required_group(): +def test_authenticatefailure_non_group_member_non_required_group( + group_dns_check: str, mock_ldap_bind: MockType, mock_ldap_search: MockType +): # User is member of a group which is not required backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -265,43 +291,29 @@ def test_authenticatefailure_non_group_member_non_required_group(): LDAP_GROUP_DNS, LDAP_HOST, id_attr=LDAP_ID_ATTR, - group_dns_check="and", - ) - - authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) - assert not authenticated - - backend = ldap_backend.LDAPAuthenticationBackend( - LDAP_BIND_DN, - LDAP_BIND_PASSWORD, - LDAP_BASE_OU, - LDAP_GROUP_DNS, - LDAP_HOST, - id_attr=LDAP_ID_ATTR, - group_dns_check="or", + group_dns_check=group_dns_check, ) authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) assert not authenticated -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock( - side_effect=[ +@pytest.mark.parametrize( + "mock_ldap_search", + ( + [ LDAP_USER_SEARCH_RESULT, [ ("cn=group1,dc=stackstorm,dc=net", ()), ("cn=group3,dc=stackstorm,dc=net", ()), ], - ] + ], ), + indirect=True, ) -def test_authenticate_and_behavior_failure_non_group_member_of_all_required_groups_1(): +def test_authenticate_and_behavior_failure_non_group_member_of_all_required_groups_1( + mock_ldap_bind: MockType, mock_ldap_search: MockType +): # User is member of two of the required groups (1 and 3) but not all three of them # (1, 2, 3) required_group_dns = [ @@ -323,23 +335,22 @@ def test_authenticate_and_behavior_failure_non_group_member_of_all_required_grou assert not authenticated -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock( - side_effect=[ +@pytest.mark.parametrize( + "mock_ldap_search", + ( + [ LDAP_USER_SEARCH_RESULT, [ ("cn=group1,dc=stackstorm,dc=net", ()), ("cn=group3,dc=stackstorm,dc=net", ()), ], - ] + ], ), + indirect=True, ) -def test_authenticate_and_behavior_failure_non_group_member_of_all_required_groups_2(): +def test_authenticate_and_behavior_failure_non_group_member_of_all_required_groups_2( + mock_ldap_bind: MockType, mock_ldap_search: MockType +): # User is member of two of the groups, but none of them are required required_group_dns = [ "cn=group7,dc=stackstorm,dc=net", @@ -359,14 +370,10 @@ def test_authenticate_and_behavior_failure_non_group_member_of_all_required_grou assert not authenticated -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock( - side_effect=[ +@pytest.mark.parametrize( + "mock_ldap_search", + ( + [ LDAP_USER_SEARCH_RESULT, [ ("cn=group1,dc=stackstorm,dc=net", ()), @@ -374,10 +381,13 @@ def test_authenticate_and_behavior_failure_non_group_member_of_all_required_grou ("cn=group3,dc=stackstorm,dc=net", ()), ("cn=group4,dc=stackstorm,dc=net", ()), ], - ] + ], ), + indirect=True, ) -def test_authenticate_and_behavior_failure_non_group_member_of_all_required_groups_3(): +def test_authenticate_and_behavior_failure_non_group_member_of_all_required_groups_3( + mock_ldap_bind: MockType, mock_ldap_search: MockType +): # User is member of two of the required groups and two non-required, but not # all of the required groups required_group_dns = [ @@ -400,14 +410,10 @@ def test_authenticate_and_behavior_failure_non_group_member_of_all_required_grou assert not authenticated -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock( - side_effect=[ +@pytest.mark.parametrize( + "mock_ldap_search", + ( + [ LDAP_USER_SEARCH_RESULT, [ ("cn=group1,dc=stackstorm,dc=net", ()), @@ -415,10 +421,13 @@ def test_authenticate_and_behavior_failure_non_group_member_of_all_required_grou ("cn=group3,dc=stackstorm,dc=net", ()), ("cn=group4,dc=stackstorm,dc=net", ()), ], - ] + ], ), + indirect=True, ) -def test_authenticate_and_is_default_behavior_non_group_member_of_all_required_groups(): +def test_authenticate_and_is_default_behavior_non_group_member_of_all_required_groups( + mock_ldap_bind: MockType, mock_ldap_search: MockType +): # User is member of two of the required groups and two non-required, but not # all of the required groups # Verify "and" is a default group_dns_check_behavior @@ -441,17 +450,14 @@ def test_authenticate_and_is_default_behavior_non_group_member_of_all_required_g assert not authenticated -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +@pytest.mark.parametrize( + "mock_ldap_search", + ([LDAP_USER_SEARCH_RESULT, [("cn=group1,dc=stackstorm,dc=net", ())]],), + indirect=True, ) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock( - side_effect=[LDAP_USER_SEARCH_RESULT, [("cn=group1,dc=stackstorm,dc=net", ())]] - ), -) -def test_authenticate_or_behavior_success_member_of_single_group_1(): +def test_authenticate_or_behavior_success_member_of_single_group_1( + mock_ldap_bind: MockType, mock_ldap_search: MockType +): # User is a memeber of single of possible required groups required_group_dns = ["cn=group1,dc=stackstorm,dc=net"] backend = ldap_backend.LDAPAuthenticationBackend( @@ -468,17 +474,14 @@ def test_authenticate_or_behavior_success_member_of_single_group_1(): assert authenticated -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +@pytest.mark.parametrize( + "mock_ldap_search", + ([LDAP_USER_SEARCH_RESULT, [("cn=group1,dc=stackstorm,dc=net", ())]],), + indirect=True, ) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock( - side_effect=[LDAP_USER_SEARCH_RESULT, [("cn=group1,dc=stackstorm,dc=net", ())]] - ), -) -def test_authenticate_or_behavior_success_member_of_single_group_2(): +def test_authenticate_or_behavior_success_member_of_single_group_2( + mock_ldap_bind: MockType, mock_ldap_search: MockType +): # User is a memeber of single of possible required groups required_group_dns = [ "cn=group1,dc=stackstorm,dc=net", @@ -500,17 +503,14 @@ def test_authenticate_or_behavior_success_member_of_single_group_2(): assert authenticated -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock( - side_effect=[LDAP_USER_SEARCH_RESULT, [("cn=group3,dc=stackstorm,dc=net", ())]] - ), +@pytest.mark.parametrize( + "mock_ldap_search", + ([LDAP_USER_SEARCH_RESULT, [("cn=group3,dc=stackstorm,dc=net", ())]],), + indirect=True, ) -def test_authenticate_or_behavior_success_member_of_single_group_2b(): +def test_authenticate_or_behavior_success_member_of_single_group_2b( + mock_ldap_bind: MockType, mock_ldap_search: MockType +): # User is a memeber of single of possible required groups required_group_dns = [ "cn=group1,dc=stackstorm,dc=net", @@ -532,23 +532,22 @@ def test_authenticate_or_behavior_success_member_of_single_group_2b(): assert authenticated -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock( - side_effect=[ +@pytest.mark.parametrize( + "mock_ldap_search", + ( + [ LDAP_USER_SEARCH_RESULT, [ ("cn=group1,dc=stackstorm,dc=net", ()), ("cn=group4,dc=stackstorm,dc=net", ()), ], - ] + ], ), + indirect=True, ) -def test_authenticate_or_behavior_success_member_of_multiple_groups_1(): +def test_authenticate_or_behavior_success_member_of_multiple_groups_1( + mock_ldap_bind: MockType, mock_ldap_search: MockType +): # User is a memeber of multiple of required groups required_group_dns = [ "cn=group1,dc=stackstorm,dc=net", @@ -571,23 +570,22 @@ def test_authenticate_or_behavior_success_member_of_multiple_groups_1(): assert authenticated -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock( - side_effect=[ +@pytest.mark.parametrize( + "mock_ldap_search", + ( + [ LDAP_USER_SEARCH_RESULT, [ ("cn=group1,dc=stackstorm,dc=net", ()), ("cn=group4,dc=stackstorm,dc=net", ()), ], - ] + ], ), + indirect=True, ) -def test_authenticate_or_behavior_success_member_of_multiple_groups_2(): +def test_authenticate_or_behavior_success_member_of_multiple_groups_2( + mock_ldap_bind: MockType, mock_ldap_search: MockType +): # User is a memeber of multiple of required groups required_group_dns = [ "cn=group1,dc=stackstorm,dc=net", @@ -607,24 +605,23 @@ def test_authenticate_or_behavior_success_member_of_multiple_groups_2(): assert authenticated -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock( - side_effect=[ +@pytest.mark.parametrize( + "mock_ldap_search", + ( + [ LDAP_USER_SEARCH_RESULT, [ ("cn=group1,dc=stackstorm,dc=net", ()), ("cn=group3,dc=stackstorm,dc=net", ()), ("cn=group6,dc=stackstorm,dc=net", ()), ], - ] + ], ), + indirect=True, ) -def test_authenticate_or_behavior_success_member_of_multiple_groups_3(): +def test_authenticate_or_behavior_success_member_of_multiple_groups_3( + mock_ldap_bind: MockType, mock_ldap_search: MockType +): # User is a memeber of multiple of required groups required_group_dns = ["cn=group3,dc=stackstorm,dc=net"] backend = ldap_backend.LDAPAuthenticationBackend( @@ -641,24 +638,23 @@ def test_authenticate_or_behavior_success_member_of_multiple_groups_3(): assert authenticated -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock( - side_effect=[ +@pytest.mark.parametrize( + "mock_ldap_search", + ( + [ LDAP_USER_SEARCH_RESULT, [ ("cn=group1,dc=stackstorm,dc=net", ()), ("cn=group3,dc=stackstorm,dc=net", ()), ("cn=group6,dc=stackstorm,dc=net", ()), ], - ] + ], ), + indirect=True, ) -def test_authenticate_or_behavior_success_member_of_multiple_groups_3b(): +def test_authenticate_or_behavior_success_member_of_multiple_groups_3b( + mock_ldap_bind: MockType, mock_ldap_search: MockType +): # User is a memeber of multiple of required groups required_group_dns = [ "cn=group3,dc=stackstorm,dc=net", @@ -678,15 +674,12 @@ def test_authenticate_or_behavior_success_member_of_multiple_groups_3b(): assert authenticated -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT]), +@pytest.mark.parametrize( + "mock_ldap_search", + ([LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT],), + indirect=True, ) -def test_ssl_authenticate(): +def test_ssl_authenticate(mock_ldap_bind: MockType, mock_ldap_search: MockType): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, LDAP_BIND_PASSWORD, @@ -702,17 +695,14 @@ def test_ssl_authenticate(): assert authenticated -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "simple_bind_s", - mock.MagicMock(side_effect=[None, Exception()]), -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT]), +@pytest.mark.parametrize( + "mock_ldap_search", + ([LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT],), + indirect=True, ) -def test_ssl_authenticate_failure(): +def test_ssl_authenticate_failure( + mock_ldap_auth_failure: MockType, mock_ldap_search: MockType +): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, LDAP_BIND_PASSWORD, @@ -728,15 +718,14 @@ def test_ssl_authenticate_failure(): assert not authenticated -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +@pytest.mark.parametrize( + "mock_ldap_search", + ([LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT],), + indirect=True, ) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT]), -) -def test_ssl_authenticate_validate_cert(): +def test_ssl_authenticate_validate_cert( + mock_ldap_bind: MockType, mock_ldap_search: MockType +): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, LDAP_BIND_PASSWORD, @@ -753,18 +742,14 @@ def test_ssl_authenticate_validate_cert(): assert authenticated -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "start_tls_s", mock.MagicMock(return_value=None) -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +@pytest.mark.parametrize( + "mock_ldap_search", + ([LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT],), + indirect=True, ) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT]), -) -def test_tls_authenticate(): +def test_tls_authenticate( + mock_ldap_start_tls: MockType, mock_ldap_bind: MockType, mock_ldap_search: MockType +): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, LDAP_BIND_PASSWORD, @@ -779,20 +764,16 @@ def test_tls_authenticate(): assert authenticated -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "start_tls_s", mock.MagicMock(return_value=None) -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "simple_bind_s", - mock.MagicMock(side_effect=[None, Exception()]), -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT]), +@pytest.mark.parametrize( + "mock_ldap_search", + ([LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT],), + indirect=True, ) -def test_tls_authenticate_failure(): +def test_tls_authenticate_failure( + mock_ldap_start_tls: MockType, + mock_ldap_auth_failure: MockType, + mock_ldap_search: MockType, +): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, LDAP_BIND_PASSWORD, @@ -807,18 +788,14 @@ def test_tls_authenticate_failure(): assert not authenticated -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "start_tls_s", mock.MagicMock(return_value=None) +@pytest.mark.parametrize( + "mock_ldap_search", + ([LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT],), + indirect=True, ) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT]), -) -def test_tls_authenticate_validate_cert(): +def test_tls_authenticate_validate_cert( + mock_ldap_start_tls: MockType, mock_ldap_bind: MockType, mock_ldap_search: MockType +): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, LDAP_BIND_PASSWORD, @@ -834,15 +811,27 @@ def test_tls_authenticate_validate_cert(): assert authenticated -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, []]), -) -def test_special_characters_in_username_are_escaped(): +@pytest.mark.parametrize( + "actual_username,expected_username,mock_ldap_search", + ( + pytest.param(actual, expected, [LDAP_USER_SEARCH_RESULT, []], id=test_name) + for test_name, actual, expected in ( + ("only-alpha", "stanleyA", "stanleyA"), + ("special-chars-unescaped", "stanley!@?.,&", "stanley!@?.,&"), + # Special characters () should be escaped + ("parens-escaped-1", "(stanley)", "\\28stanley\\29"), + # Special characters () should be escaped + ("parens-escaped-2", "(stanley=)", "\\28stanley=\\29"), + ) + ), + indirect=["mock_ldap_search"], +) +def test_special_characters_in_username_are_escaped( + actual_username: str, + expected_username: str, + mock_ldap_bind: MockType, + mock_ldap_search: MockType, +): # User is not member of any of the required group backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -853,43 +842,26 @@ def test_special_characters_in_username_are_escaped(): id_attr=LDAP_ID_ATTR, ) - values = [ - ("stanleyA", "stanleyA"), - ("stanley!@?.,&", "stanley!@?.,&"), - # Special characters () should be escaped - ("(stanley)", "\\28stanley\\29"), - # Special characters () should be escaped - ("(stanley=)", "\\28stanley=\\29"), - ] - - for actual_username, expected_username in values: - backend.authenticate(actual_username, LDAP_USER_BAD_PASSWD) + backend.authenticate(actual_username, LDAP_USER_BAD_PASSWD) - call_args_1 = ldap.ldapobject.SimpleLDAPObject.search_s.call_args_list[0][0] - call_args_2 = ldap.ldapobject.SimpleLDAPObject.search_s.call_args_list[1][0] + call_args_1 = mock_ldap_search.call_args_list[0][0] + call_args_2 = mock_ldap_search.call_args_list[1][0] - # First search_s call (find user by uid) - filter_call_value = call_args_1[2] - assert filter_call_value == f"uid={expected_username}" + # First search_s call (find user by uid) + filter_call_value = call_args_1[2] + assert filter_call_value == f"uid={expected_username}" - # Second search_s call (group membership test) - filter_call_value = call_args_2[2] - assert f"(memberUid={expected_username})" in filter_call_value - - ldap.ldapobject.SimpleLDAPObject.search_s = mock.MagicMock( - side_effect=[LDAP_USER_SEARCH_RESULT, []] - ) + # Second search_s call (group membership test) + filter_call_value = call_args_2[2] + assert f"(memberUid={expected_username})" in filter_call_value -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT]), +@pytest.mark.parametrize( + "mock_ldap_search", + ([LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT],), + indirect=True, ) -def test_get_user(): +def test_get_user(mock_ldap_bind: MockType, mock_ldap_search: MockType): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, LDAP_BIND_PASSWORD, @@ -906,15 +878,14 @@ def test_get_user(): assert user_info["primaryGroupID"] == ["513"] -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) +@pytest.mark.parametrize( + "mock_ldap_search", + ([2 * LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT],), + indirect=True, ) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock(side_effect=[2 * LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT]), -) -def test_get_user_multiple_results(): +def test_get_user_multiple_results( + mock_ldap_bind: MockType, mock_ldap_search: MockType +): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, LDAP_BIND_PASSWORD, @@ -928,15 +899,12 @@ def test_get_user_multiple_results(): assert user_info is None -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT]), +@pytest.mark.parametrize( + "mock_ldap_search", + ([LDAP_USER_SEARCH_RESULT, LDAP_GROUP_SEARCH_RESULT],), + indirect=True, ) -def test_get_user_groups(): +def test_get_user_groups(mock_ldap_bind: MockType, mock_ldap_search: MockType): backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, LDAP_BIND_PASSWORD, @@ -952,22 +920,21 @@ def test_get_user_groups(): assert groups == expected -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock( - side_effect=[ +@pytest.mark.parametrize( + "mock_ldap_search", + ( + [ LDAP_USER_SEARCH_RESULT, [("cn=group1,dc=stackstorm,dc=net", ())], LDAP_USER_SEARCH_RESULT, [("cn=group1,dc=stackstorm,dc=net", ())], - ] + ], ), + indirect=True, ) -def test_authenticate_and_get_user_groups_caching_disabled(): +def test_authenticate_and_get_user_groups_caching_disabled( + mock_ldap_bind: MockType, mock_ldap_search: MockType +): required_group_dns = ["cn=group1,dc=stackstorm,dc=net"] backend = ldap_backend.LDAPAuthenticationBackend( @@ -981,36 +948,35 @@ def test_authenticate_and_get_user_groups_caching_disabled(): cache_user_groups_response=False, ) - assert ldap.ldapobject.SimpleLDAPObject.search_s.call_count == 0 + assert mock_ldap_search.call_count == 0 authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) assert authenticated # 1 for user dn search, 1 for groups search - assert ldap.ldapobject.SimpleLDAPObject.search_s.call_count == 2 + assert mock_ldap_search.call_count == 2 user_groups = backend.get_user_groups(username=LDAP_USER_UID) assert user_groups == ["cn=group1,dc=stackstorm,dc=net"] - assert ldap.ldapobject.SimpleLDAPObject.search_s.call_count == 4 + assert mock_ldap_search.call_count == 4 assert backend._user_groups_cache is None -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock( - side_effect=[ +@pytest.mark.parametrize( + "mock_ldap_search", + ( + [ LDAP_USER_SEARCH_RESULT, [("cn=group1,dc=stackstorm,dc=net", ())], LDAP_USER_SEARCH_RESULT, [("cn=group1,dc=stackstorm,dc=net", ())], - ] + ], ), + indirect=True, ) -def test_authenticate_and_get_user_groups_caching_enabled(): +def test_authenticate_and_get_user_groups_caching_enabled( + mock_ldap_bind: MockType, mock_ldap_search: MockType +): required_group_dns = ["cn=group1,dc=stackstorm,dc=net"] backend = ldap_backend.LDAPAuthenticationBackend( @@ -1024,27 +990,26 @@ def test_authenticate_and_get_user_groups_caching_enabled(): cache_user_groups_response=True, ) - assert ldap.ldapobject.SimpleLDAPObject.search_s.call_count == 0 + assert mock_ldap_search.call_count == 0 authenticated = backend.authenticate(LDAP_USER_UID, LDAP_USER_BAD_PASSWD) assert authenticated - assert ldap.ldapobject.SimpleLDAPObject.search_s.call_count == 2 + assert mock_ldap_search.call_count == 2 user_groups = backend.get_user_groups(username=LDAP_USER_UID) assert user_groups == ["cn=group1,dc=stackstorm,dc=net"] - assert ldap.ldapobject.SimpleLDAPObject.search_s.call_count == 2 + assert mock_ldap_search.call_count == 2 assert LDAP_USER_UID in backend._user_groups_cache -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock(side_effect=[LDAP_USER_SEARCH_RESULT]), +@pytest.mark.parametrize( + "mock_ldap_search", + ([LDAP_USER_SEARCH_RESULT],), + indirect=True, ) -def test_get_user_specifying_account_pattern(): +def test_get_user_specifying_account_pattern( + mock_ldap_bind: MockType, mock_ldap_search: MockType, mocker: MockerFixture +): expected_username = "unique_username_1" required_group_dns = [ "cn=group3,dc=stackstorm,dc=net", @@ -1077,8 +1042,8 @@ def test_get_user_specifying_account_pattern(): scope=scope, account_pattern=account_pattern, ) - connection = mock.MagicMock() - backend._init_connection = mock.MagicMock(return_value=connection) + connection = mocker.MagicMock() + backend._init_connection = mocker.MagicMock(return_value=connection) backend.get_user(expected_username) connection.search_s.assert_called_once_with( @@ -1086,22 +1051,21 @@ def test_get_user_specifying_account_pattern(): ) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock( - side_effect=[ +@pytest.mark.parametrize( + "mock_ldap_search", + ( + [ LDAP_USER_SEARCH_RESULT, [("cn=group3,dc=stackstorm,dc=net", ())], LDAP_USER_SEARCH_RESULT, [("cn=group4,dc=stackstorm,dc=net", ())], - ] + ], ), + indirect=True, ) -def test_get_user_groups_specifying_group_pattern(): +def test_get_user_groups_specifying_group_pattern( + mock_ldap_bind: MockType, mock_ldap_search: MockType, mocker: MockerFixture +): expected_user_dn = "unique_userdn_1" expected_username = "unique_username_2" required_group_dns = [ @@ -1143,9 +1107,9 @@ def test_get_user_groups_specifying_group_pattern(): group_pattern=group_pattern, cache_user_groups_response=False, ) - connection = mock.MagicMock() - backend._init_connection = mock.MagicMock(return_value=connection) - backend._get_user_dn = mock.MagicMock(return_value=expected_user_dn) + connection = mocker.MagicMock() + backend._init_connection = mocker.MagicMock(return_value=connection) + backend._get_user_dn = mocker.MagicMock(return_value=expected_user_dn) backend.get_user_groups(expected_username) connection.search_s.assert_called_with( @@ -1153,22 +1117,21 @@ def test_get_user_groups_specifying_group_pattern(): ) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock( - side_effect=[ +@pytest.mark.parametrize( + "mock_ldap_search", + ( + [ LDAP_USER_SEARCH_RESULT, [("cn=group3,dc=stackstorm,dc=net", ())], LDAP_USER_SEARCH_RESULT, [("cn=group4,dc=stackstorm,dc=net", ())], - ] + ], ), + indirect=True, ) -def test_get_groups_caching_no_cross_username_cache_polution(): +def test_get_groups_caching_no_cross_username_cache_polution( + mock_ldap_bind: MockType, mock_ldap_search: MockType +): required_group_dns = [ "cn=group3,dc=stackstorm,dc=net", "cn=group4,dc=stackstorm,dc=net", @@ -1197,22 +1160,21 @@ def test_get_groups_caching_no_cross_username_cache_polution(): ] -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, "simple_bind_s", mock.MagicMock(return_value=None) -) -@mock.patch.object( - ldap.ldapobject.SimpleLDAPObject, - "search_s", - mock.MagicMock( - side_effect=[ +@pytest.mark.parametrize( + "mock_ldap_search", + ( + [ LDAP_USER_SEARCH_RESULT, [("cn=group3,dc=stackstorm,dc=net", ())], LDAP_USER_SEARCH_RESULT, [("cn=group4,dc=stackstorm,dc=net", ())], - ] + ], ), + indirect=True, ) -def test_get_groups_caching_cache_ttl(): +def test_get_groups_caching_cache_ttl( + mock_ldap_bind: MockType, mock_ldap_search: MockType +): required_group_dns = [ "cn=group3,dc=stackstorm,dc=net", "cn=group4,dc=stackstorm,dc=net", From f65cf63273720b86626528bbf4725c8d0186a6bc Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Tue, 22 Oct 2024 17:27:16 -0500 Subject: [PATCH 4/4] typos --- tests/unit/test_backend.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/unit/test_backend.py b/tests/unit/test_backend.py index 58cb6ee..eda60cf 100644 --- a/tests/unit/test_backend.py +++ b/tests/unit/test_backend.py @@ -126,7 +126,7 @@ def mock_ldap_search(mocker: MockerFixture, request: pytest.FixtureRequest) -> M ) -def test_instantaite_no_group_dns_provided(): +def test_instantiate_no_group_dns_provided(): # User is member of two of the groups, but none of them are required required_group_dns = [] expected_msg = "One or more user groups must be specified" @@ -253,7 +253,7 @@ def test_authenticate_failure_bad_user_password( def test_authenticate_failure_non_group_member_no_groups( group_dns_check: str, mock_ldap_bind: MockType, mock_ldap_search: MockType ): - # User is not member of any of the required group + # User is not a member of any of the required groups backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, LDAP_BIND_PASSWORD, @@ -280,7 +280,7 @@ def test_authenticate_failure_non_group_member_no_groups( ), indirect=["mock_ldap_search"], ) -def test_authenticatefailure_non_group_member_non_required_group( +def test_authenticate_failure_non_group_member_non_required_group( group_dns_check: str, mock_ldap_bind: MockType, mock_ldap_search: MockType ): # User is member of a group which is not required @@ -458,7 +458,7 @@ def test_authenticate_and_is_default_behavior_non_group_member_of_all_required_g def test_authenticate_or_behavior_success_member_of_single_group_1( mock_ldap_bind: MockType, mock_ldap_search: MockType ): - # User is a memeber of single of possible required groups + # User is a member of single of possible required groups required_group_dns = ["cn=group1,dc=stackstorm,dc=net"] backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -482,7 +482,7 @@ def test_authenticate_or_behavior_success_member_of_single_group_1( def test_authenticate_or_behavior_success_member_of_single_group_2( mock_ldap_bind: MockType, mock_ldap_search: MockType ): - # User is a memeber of single of possible required groups + # User is a member of single of possible required groups required_group_dns = [ "cn=group1,dc=stackstorm,dc=net", "cn=group2,dc=stackstorm,dc=net", @@ -511,7 +511,7 @@ def test_authenticate_or_behavior_success_member_of_single_group_2( def test_authenticate_or_behavior_success_member_of_single_group_2b( mock_ldap_bind: MockType, mock_ldap_search: MockType ): - # User is a memeber of single of possible required groups + # User is a member of single of possible required groups required_group_dns = [ "cn=group1,dc=stackstorm,dc=net", "cn=group2,dc=stackstorm,dc=net", @@ -548,7 +548,7 @@ def test_authenticate_or_behavior_success_member_of_single_group_2b( def test_authenticate_or_behavior_success_member_of_multiple_groups_1( mock_ldap_bind: MockType, mock_ldap_search: MockType ): - # User is a memeber of multiple of required groups + # User is a member of multiple of required groups required_group_dns = [ "cn=group1,dc=stackstorm,dc=net", "cn=group2,dc=stackstorm,dc=net", @@ -586,7 +586,7 @@ def test_authenticate_or_behavior_success_member_of_multiple_groups_1( def test_authenticate_or_behavior_success_member_of_multiple_groups_2( mock_ldap_bind: MockType, mock_ldap_search: MockType ): - # User is a memeber of multiple of required groups + # User is a member of multiple of required groups required_group_dns = [ "cn=group1,dc=stackstorm,dc=net", "cn=group4,dc=stackstorm,dc=net", @@ -622,7 +622,7 @@ def test_authenticate_or_behavior_success_member_of_multiple_groups_2( def test_authenticate_or_behavior_success_member_of_multiple_groups_3( mock_ldap_bind: MockType, mock_ldap_search: MockType ): - # User is a memeber of multiple of required groups + # User is a member of multiple of required groups required_group_dns = ["cn=group3,dc=stackstorm,dc=net"] backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, @@ -655,7 +655,7 @@ def test_authenticate_or_behavior_success_member_of_multiple_groups_3( def test_authenticate_or_behavior_success_member_of_multiple_groups_3b( mock_ldap_bind: MockType, mock_ldap_search: MockType ): - # User is a memeber of multiple of required groups + # User is a member of multiple of required groups required_group_dns = [ "cn=group3,dc=stackstorm,dc=net", "cn=group1,dc=stackstorm,dc=net", @@ -832,7 +832,7 @@ def test_special_characters_in_username_are_escaped( mock_ldap_bind: MockType, mock_ldap_search: MockType, ): - # User is not member of any of the required group + # User is not a member of any of the required groups backend = ldap_backend.LDAPAuthenticationBackend( LDAP_BIND_DN, LDAP_BIND_PASSWORD, @@ -1129,7 +1129,7 @@ def test_get_user_groups_specifying_group_pattern( ), indirect=True, ) -def test_get_groups_caching_no_cross_username_cache_polution( +def test_get_groups_caching_no_cross_username_cache_pollution( mock_ldap_bind: MockType, mock_ldap_search: MockType ): required_group_dns = [