def test_match_dn_in_cursor(self): self.delete_at_teardown.append( add_user(self.connection, testcase_id, 'mat-1')) self.delete_at_teardown.append( add_user(self.connection, testcase_id, 'mat-2')) self.delete_at_teardown.append( add_user(self.connection, testcase_id, 'mat-3')) o = ObjectDef('inetOrgPerson') o += AttrDef('cn', 'Common Name') o += AttrDef('sn', 'Surname') o += AttrDef(test_multivalued_attribute, 'Given Name') query_text = 'Common Name:=' + testcase_id + 'mat-*' r = Reader(self.connection, o, test_base, query_text) results = r.search() self.assertEqual(len(results), 3) e = r.match_dn('mat') # multiple matches self.assertEqual(len(e), 3) e = r.match_dn( '-3' ) # single match. there's domains with -20xx in their names, so use -3 to match self.assertEqual(len(e), 1) e = r.match_dn('no-match') # no match self.assertEqual(len(e), 0)
def test_find_entry_with_text_index_match(self): self.delete_at_teardown.append( add_user(self.connection, testcase_id, 'mat-1')) self.delete_at_teardown.append( add_user(self.connection, testcase_id, 'mat-2')) self.delete_at_teardown.append( add_user(self.connection, testcase_id, 'mat-3')) o = ObjectDef('inetOrgPerson') o += AttrDef('cn', 'Common Name') o += AttrDef('sn', 'Surname') o += AttrDef(test_multivalued_attribute, 'Given Name') query_text = 'Common Name:=' + testcase_id + 'mat-*' r = Reader(self.connection, o, test_base, query_text) results = r.search() self.assertEqual(len(results), 3) try: # multiple matches e = r['match'] except KeyError: pass e = r['-2'] # exact match self.assertTrue('mat-2' in e.entry_dn) try: e = r['no-match'] # no match except KeyError: pass
def test_search_with_None_default(self): self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'abs-12')) ou = ObjectDef('inetOrgPerson') ou += AttrDef('cn', 'CommonName') ou += AttrDef('employeeType', key='Employee', default=None) qu = 'CommonName := ' + testcase_id + 'abs-12' ru = Reader(self.connection, ou, test_base, qu) lu = ru.search() self.assertEqual(lu[0].employee.value, None)
def test_search_with_default(self): self.delete_at_teardown.append( add_user(self.connection, testcase_id, 'abstract-member-10')) ou = ObjectDef('inetOrgPerson') ou += AttrDef('cn', 'CommonName') ou += AttrDef('employeeType', key='Employee', default='not employed') qu = 'CommonName := ' + testcase_id + 'abstract-member-10' ru = Reader(self.connection, ou, test_base, qu) lu = ru.search() self.assertEqual(str(lu[0].employee), 'not employed')
def test_validate_query_filter(self): o = ObjectDef() o += AttrDef('cn', 'Common Name') o += AttrDef('sn', 'Surname') o += AttrDef('givenName', 'Given Name') query_text = '|Common Name:=john;=Bob, Surname:=smith' r = Reader(self.connection, o, test_base, query_text) r._validate_query() self.assertEqual('Surname: =smith, |CommonName: =Bob;=john', r.validated_query)
def test_create_query_filter(self): o = ObjectDef() o += AttrDef('cn', 'Common Name') o += AttrDef('sn', 'Surname') o += AttrDef('givenName', 'Given Name') query_text = '|Common Name:=john;Bob, Surname:=smith' r = Reader(self.connection, o, test_base, query_text) r._create_query_filter() self.assertEqual('(&(sn=smith)(|(cn=Bob)(cn=john)))', r.query_filter)
def test_search_with_dereference(self): reverse = lambda a, e: e[::-1] def raise_parentheses_rank(_, l): up = {'(': '[', ')': ']', '[': '{', ']': '}', '{': '<', '}': '>'} r = [] for e in l: s = '' for c in e: s += up[c] if c in up else c r.append(s) return r self.delete_at_teardown.append( add_user(self.connection, testcase_id, 'abstract-member-4')) self.delete_at_teardown.append( add_user(self.connection, testcase_id, 'abstract-member-5')) self.delete_at_teardown.append( add_user(self.connection, testcase_id, 'abstract-member-6')) self.delete_at_teardown.append( add_group(self.connection, testcase_id, 'abstract-group', self.delete_at_teardown)) ou = ObjectDef('inetOrgPerson') ou += AttrDef('cn', 'Common Name', post_query=reverse) ou += AttrDef('sn', 'Surname') ou += AttrDef('givenName', 'Given Name', post_query=raise_parentheses_rank) ou += AttrDef('ACL') qu = 'Common Name: ' + testcase_id + 'abstract-member-*' ru = Reader(self.connection, ou, test_base, qu) lu = ru.search() self.assertEqual(len(lu), 3) og = ObjectDef('groupOfNames') og += AttrDef('member', dereference_dn=ou) og += 'cn' qg = 'cn := ' + testcase_id + 'abstract-group' rg = Reader(self.connection, og, test_base, qg) lg = rg.search() self.assertEqual(len(lg), 1) eg = lg[0] mg = eg.member self.assertEqual(len(mg), 3) ug = eg.member[0] self.assertTrue( str(ug.surname) in ['abstract-member-4', 'abstract-member-5', 'abstract-member-6'])
def test_match_in_multiple_attribute(self): self.delete_at_teardown.append( add_user(self.connection, testcase_id, 'mat-1', attributes={ test_multivalued_attribute: ['givenname-1', 'givenname-1a'], 'street': '1a' })) self.delete_at_teardown.append( add_user(self.connection, testcase_id, 'mat-2', attributes={ test_multivalued_attribute: ['givenname-2', 'givenname-2a'], 'street': '3a' })) self.delete_at_teardown.append( add_user(self.connection, testcase_id, 'mat-3', attributes={ test_multivalued_attribute: ['givenname-3', 'givenname-3a'], 'street': '4a' })) o = ObjectDef('inetOrgPerson') o += AttrDef('cn', 'Common Name') o += AttrDef('sn', 'Surname') o += AttrDef(test_multivalued_attribute, 'Given Name') o += AttrDef('street', 'Street') query_text = 'Common Name:=' + testcase_id + 'mat-*' r = Reader(self.connection, o, test_base, query_text) results = r.search() self.assertEqual(len(results), 3) e = r.match(['Given Name', 'Street'], '3a') # multiple matches self.assertEqual(len(e), 2) e = r.match(['Given Name', 'street'], '1a') # single match self.assertEqual(len(e), 1) e = r.match(['Given Name', 'street'], 'no-match') # no match self.assertEqual(len(e), 0)
def test_search_filter_with_object_class(self): self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'abs-1')) self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'abs-2')) self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'abs-3')) self.delete_at_teardown.append(add_group(self.connection, testcase_id, 'abs-grp', self.delete_at_teardown)) reverse = lambda a, e: e[::-1] o = ObjectDef('inetOrgPerson') o += AttrDef('cn', 'Common Name') o += AttrDef('sn', 'Surname') o += AttrDef(test_multivalued_attribute, 'Given Name', post_query=reverse) query_text = 'Common Name:=' + testcase_id + 'abs-*' r = Reader(self.connection, o, test_base, query_text) results = r.search() self.assertEqual(len(results), 3)
def get_definition(self, object_class: Union[str, List[str]], attributes: Iterable[str] = None) -> ObjectDef: """ Get a new object class definition automatically from LDAP Schema. object definitions are save as cache for later use in connection. :param object_class: The LDAP objectClass type to define :param attributes: Extra LDAP attributes to include :return: ldap3 ObjectDef instance """ # create definition if missing if object_class not in self.definitions: with LogExecutionTime( f"Loading LDAP Schema definition for objectClass {object_class}" ): self.definitions[object_class] = ObjectDef( object_class, self.connection) # add missing attributes if attributes: for attr in attributes: if attr not in self.definitions[object_class]: self.definitions[object_class] += AttrDef(attr) return self.definitions[object_class]
def test_search_with_pre_query(self): change = lambda attr, value: testcase_id + 'abs-*' self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'abs-7')) self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'abs-8')) self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'abs-9')) self.delete_at_teardown.append(add_group(self.connection, testcase_id, 'abstract-group', self.delete_at_teardown)) ou = ObjectDef('inetOrgPerson') ou += AttrDef('cn', 'Common Name', pre_query=change) ou += AttrDef('sn', 'Surname') ou += AttrDef(test_multivalued_attribute, 'Given Name') ou += AttrDef('ACL') qu = 'Common Name := bug' ru = Reader(self.connection, ou, test_base, qu) lu = ru.search() self.assertEqual(len(lu), 3)
def test_create_query_filter_single_attribute_multiple_value(self): o = ObjectDef() o += AttrDef('cn', 'Common Name') query_text = '|Common Name:=john;=Bob' r = Reader(self.connection, o, test_base, query_text) r._create_query_filter() self.assertEqual('(|(cn=Bob)(cn=john))', r.query_filter)
def test_create_query_filter_single_attribute_single_value(self): o = ObjectDef() o += AttrDef('cn', 'Common Name') query_text = 'Common Name:John' r = Reader(self.connection, o, test_base, query_text) r._create_query_filter() self.assertEqual('(cn=John)', r.query_filter)
def __init__(self, server, group): self.server = server self.group = group self.obj = ObjectDef(['top', 'group']) self.obj += AttrDef('distinguishedName', key='dn') self.obj += AttrDef('objectClass', key='object_class') self.obj += AttrDef('cn', key='cn') self.obj += AttrDef('mail', key='email') self.obj += AttrDef('extensionName', key='email_canonical') self.obj += AttrDef('sAMAccountName', key='slug_name') self.obj += AttrDef('groupType', key='type') self.obj += AttrDef('flags', key='flags')
def test_match_dn_in_cursor(self): self.delete_at_teardown.append( add_user(self.connection, testcase_id, 'match-1')) self.delete_at_teardown.append( add_user(self.connection, testcase_id, 'match-2')) self.delete_at_teardown.append( add_user(self.connection, testcase_id, 'match-3')) o = ObjectDef('inetOrgPerson') o += AttrDef('cn', 'Common Name') o += AttrDef('sn', 'Surname') o += AttrDef('givenName', 'Given Name') query_text = 'Common Name:=' + testcase_id + 'match-*' r = Reader(self.connection, o, test_base, query_text) results = r.search() self.assertEqual(len(results), 3) e = r.match_dn('match') # multiple matches self.assertEqual(len(e), 3) e = r.match_dn('-2') # single match self.assertEqual(len(e), 1) e = r.match_dn('no-match') # no match self.assertEqual(len(e), 0)
def test_modify_entry_with_attrdef_with_friendly_name(self): self.delete_at_teardown.append( add_user(self.connection, testcase_id, 'new-9', attributes={ test_multivalued_attribute: testcase_id + 'friendly-attr-name-1' })) a = AttrDef(name=test_multivalued_attribute, key='myname') o = ObjectDef('inetorgperson') o += a r = Reader(self.connection, o, test_base, 'myname:=' + testcase_id + 'friendly*') r.search() self.assertTrue(r[0].myname, testcase_id + 'friendly-attr-name-1') w = Writer.from_cursor(r) e = w[0] e.myname += 'xyz' w.commit() self.assertTrue('xyz' in e.myname)
def authenticate(self, request, ticket, service): """Verifies CAS ticket and gets or creates User object""" user = super(CASLDAPBackend, self).authenticate(request, ticket, service) # Populate user attributes if user: try: server = Server(settings.LDAP_GROUPS_SERVER_URI) connection = Connection( server=server, auto_bind=True, user=settings.LDAP_GROUPS_BIND_DN, password=settings.LDAP_GROUPS_BIND_PASSWORD, raise_exceptions=True) connection.start_tls() account_def = ObjectDef('user') account_def += AttrDef('userPrincipalName') account_def += AttrDef('displayName') account_def += AttrDef('givenName') account_def += AttrDef('sn') account_def += AttrDef('mail') account_reader = Reader( connection=connection, object_def=account_def, query="userPrincipalName: {principal_name}".format( principal_name=user.username), base=settings.LDAP_GROUPS_BASE_DN) account_reader.search_subtree() user_info = account_reader.entries[0] except Exception as msg: logger.exception(msg, exc_info=True, extra={'request': request}) else: principal_name = str(user_info["userPrincipalName"]) username = principal_name.split("@")[0] user_group_objects = Reader( connection=connection, object_def=account_def, query= '(&(member=CN={username},OU=People,OU=Enterprise,OU=Accounts,DC=ad,DC=calpoly,DC=edu)(objectClass=group))' .format(username=username), base=settings.LDAP_GROUPS_BASE_DN).search() user_groups = [] for group in user_group_objects: user_groups.append(group.entry_dn) def AD_get_children(connection, parent): connection.search( settings.LDAP_GROUPS_BASE_DN, "(&(objectCategory=group)(memberOf={group_name}))". format(group_name=escape_query(parent))) children = connection.entries results = [] for child in children: results.append(child.entry_dn) return results def get_descendants(connection, parent): descendants = [] queue = [] queue.append(parent) visited = set() while len(queue): node = queue.pop() if node not in visited: children = AD_get_children(connection, node) for child in children: if child not in descendants: descendants.append(child) queue.append(child) visited.add(node) return descendants # New Code should use the ad_groups property of the user to enforce permissions user.ad_groups.clear() for group_id, group_dn in ADGroup.objects.all().values_list( 'id', 'distinguished_name'): if group_dn in user_groups: user.ad_groups.add(group_id) else: children = get_descendants(connection, group_dn) for child in children: if child in user_groups: user.ad_groups.add(group_id) if not user.ad_groups.exists(): raise PermissionDenied( 'User %s is not in any of the allowed groups.' % principal_name) if not user.ad_groups.all().filter( distinguished_name= 'CN=UH-RN-DevTeam,OU=Technology,OU=UH,OU=Manual,OU=Groups,DC=ad,DC=calpoly,DC=edu' ).exists() and settings.RESTRICT_LOGIN_TO_DEVELOPERS: raise PermissionDenied( 'Only developers can access the site on this server. Please use the primary site.' ) def get_group_members(group): cache_key = 'group_members::' + ( group if " " not in group else group.replace(" ", "_")) group_members = cache.get(cache_key) if group_members is None: try: group_members = LDAPADGroup( group).get_tree_members() except InvalidGroupDN: logger.exception( 'Could not retrieve group members for DN: ' + group, exc_info=True, extra={'request': request}) return [] group_members = [ member["userPrincipalName"] for member in group_members ] cache.set(cache_key, group_members, 60) return group_members # Django Flags developer_list = get_group_members( 'CN=UH-RN-DevTeam,OU=Technology,OU=UH,OU=Manual,OU=Groups,DC=ad,DC=calpoly,DC=edu' ) user.is_developer = principal_name in developer_list user.is_staff = principal_name in developer_list user.is_superuser = principal_name in developer_list user.full_name = user_info["displayName"] user.first_name = user_info["givenName"] user.last_name = user_info["sn"] user.email = user_info["mail"] user.save() return user
def get_abstract_attr_def(self, key): return AttrDef(name=self.name, key=key, validate=self.validate, default=self.default, dereference_dn=self.dereference_dn)
def authenticate(self, ticket, service, request): """Verifies CAS ticket and gets or creates User object""" user = super(CASLDAPBackend, self).authenticate(ticket, service, request) # Populate user attributes if user: try: server = Server(settings.LDAP_GROUPS_SERVER_URI) connection = Connection( server=server, auto_bind=True, user=settings.LDAP_GROUPS_BIND_DN, password=settings.LDAP_GROUPS_BIND_PASSWORD, raise_exceptions=True) connection.start_tls() account_def = ObjectDef('user') account_def.add(AttrDef('userPrincipalName')) account_def.add(AttrDef('displayName')) account_def.add(AttrDef('givenName')) account_def.add(AttrDef('sn')) account_def.add(AttrDef('mail')) account_reader = Reader( connection=connection, object_def=account_def, query="userPrincipalName: {principal_name}".format( principal_name=user.username), base=settings.LDAP_GROUPS_BASE_DN) account_reader.search_subtree() user_info = account_reader.entries[0] except Exception as msg: logger.exception(msg, exc_info=True, extra={'request': request}) else: principal_name = str(user_info["userPrincipalName"]) def get_group_members(group): cache_key = 'group_members::' + group group_members = cache.get(cache_key) if group_members is None: try: group_members = LDAPADGroup( group).get_tree_members() except InvalidGroupDN: logger.exception( 'Could not retrieve group members for DN: ' + group, exc_info=True, extra={'request': request}) return [] group_members = [ member["userPrincipalName"] for member in group_members ] cache.set(cache_key, group_members, 60) return group_members def check_group_for_user(group): group_members = get_group_members(group.distinguished_name) if principal_name in group_members: user.ad_groups.add(group) # New Code should use the ad_groups property of the user to enforce permissions user.ad_groups.clear() with ThreadPoolExecutor(ADGroup.objects.count()) as pool: pool.map(check_group_for_user, ADGroup.objects.all()) if not user.ad_groups.exists(): raise PermissionDenied( 'User %s is not in any of the allowed groups.' % principal_name) if not user.ad_groups.all().filter( distinguished_name= 'CN=UH-RN-DevTeam,OU=Technology,OU=UH,OU=Manual,OU=Groups,DC=ad,DC=calpoly,DC=edu' ).exists() and settings.RESTRICT_LOGIN_TO_DEVELOPERS: raise PermissionDenied( 'Only developers can access the site on this server. Please use the primary site.' ) # Django Flags developer_list = get_group_members( 'CN=UH-RN-DevTeam,OU=Technology,OU=UH,OU=Manual,OU=Groups,DC=ad,DC=calpoly,DC=edu' ) user.is_developer = principal_name in developer_list user.is_staff = principal_name in developer_list user.is_superuser = principal_name in developer_list user.full_name = user_info["displayName"] user.first_name = user_info["givenName"] user.last_name = user_info["sn"] user.email = user_info["mail"] user.save() return user
def __init__(self, server, user): self.server = server self.user = user self.person = ObjectDef(['top', 'organizationalPerson', 'user']) self.person += AttrDef('distinguishedName', key='dn') self.person += AttrDef('objectClass', key='object_class') self.person += AttrDef('cn', key='cn') self.person += AttrDef('sAMAccountName', key='username') self.person += AttrDef('givenName', key='first_name') self.person += AttrDef('sn', key='last_name') self.person += AttrDef('displayName', key='full_name') self.person += AttrDef('userPrincipalName', key='logon') self.person += AttrDef('mail', key='email') self.person += AttrDef('extensionName', key='email_canonical') self.person += AttrDef('userAccountControl', key='control') self.person += AttrDef('memberOf', key='groups') self.person += AttrDef(server.email_domain, key='email_domain') self.person += AttrDef(server.email_buzon_size, key='email_buzon_size') self.person += AttrDef(server.email_message_size, key='email_message_size') self.person += AttrDef(server.internet_domain, key='internet_domain') self.person += AttrDef(server.internet_quota_type, key='internet_quota_type') self.person += AttrDef(server.internet_quota_size, key='internet_quota_size') self.person += AttrDef(server.internet_extra_quota_size, key='internet_extra_quota_size') self.person += AttrDef(server.ftp_home, key='ftp_folder') self.person += AttrDef(server.ftp_size, key='ftp_size') self.query = 'username: {}'.format(self.user.username, )