From 77e6e6421fcc38f59f0c86a2f07ac26c879853b0 Mon Sep 17 00:00:00 2001 From: "Noah Hanford (spaced)" Date: Fri, 12 Jun 2026 21:28:49 -0400 Subject: [PATCH 1/2] feat: added methods to get member data while combining groups --- README.md | 28 ++++++++++++++ csh_ldap/__init__.py | 92 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) diff --git a/README.md b/README.md index c1778bc..b1bb5bd 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,34 @@ rtp = instance.get_group('rtp') rtp = instance.get_group('rtp').get_member_uids() # returns ['spaced', ...] +# get group member uids (other way) +rtp = instance.get_group_member_uids(groups=["rtp"]) + +# get uids of members in two (or more) groups and not in groups +fancy = instance.get_group_member_uids(groups=["rtp", "onfloor"], excluded=["eboard-opcomm"]) + +# get uuids of members in two (or more) groups and not in groups +# that's right! ipaUniqueId +fancy = instance.get_group_member_uuids(groups=["rtp", "onfloor"], excluded=["eboard-opcomm"]) + +# get other miscellaneous attributes of members in group +# look how rich drink admins are (admin abuse!!!) +admin_abuse = instance.get_group_member_attributes(group=["drink"], attributes=["uid", "drinkBalance"]) +# returns dicts for each member +""" +I wonder what happened here +[ +{ + 'uid': 'cole', + 'drinkBalance': '996246' +}, +{ + 'uid': 'zxcv', + 'drinkBalance': '3847173' +} +] +""" + # Get cn of member print(liam.cn) diff --git a/csh_ldap/__init__.py b/csh_ldap/__init__.py index fc67cce..986b6ed 100644 --- a/csh_ldap/__init__.py +++ b/csh_ldap/__init__.py @@ -149,6 +149,97 @@ def get_directorship_heads(self, val): True) for dn in ret] + def get_query_for_groups(self, groups = [], excluded_groups = []): + """Returns the ldap query string to get members in groups but not in others + + Argumenets: + groups -- the groups members must be a member of + excluded_groups -- the groups members cannot be a part of + """ + + group_dns = [f"(memberOf=cn={group},cn=groups,cn=accounts,dc=csh,dc=rit,dc=edu)" for group in groups] + excluded_group_dns = [f"(memberOf=cn={group},cn=groups,cn=accounts,dc=csh,dc=rit,dc=edu)" for group in excluded_groups] + + query = "" + + for group in group_dns: + if query == "": + query = group + continue + + query = f"(&{query}{group})" + + for group in excluded_group_dns: + group = f"(!{group})" + if query == "": + query = group + continue + + query = f"(&{query}{group})" + + return query + + def get_group_member_attributes(self, groups = [], excluded_groups = [], attributes = ['uid']): + """Returns a list of dicts containing all the attributes requested in the groups listed in groups, but not in exlcuded_groups + + Arguements: + groups -- the groups members must be a member of + excluded_groups -- the groups members cannot be a part of + attributues -- the ldap attributes to return + """ + + query_result = self.__con__.search_s( + "dc=csh,dc=rit,dc=edu", + ldap.SCOPE_SUBTREE, + self.get_query_for_groups(groups=groups, excluded_groups=excluded_groups), + attributes) + + # the rest of this could be one giant list compression but I don't hate you that much so I chose not to + + # filter out subgroups, probably the second check all we need + # but if we used just the second one but the first one was empty that would probably be confusing? + byte_result = [member[1] for member in query_result if not member[1] == {} and "cn=users" in member[0]] + + result = [] + + for byte_member in byte_result: + # decoding the byte strings + result.append({key: value[0].decode('utf-8') for (key, value) in byte_member.items()}) + + return result + + def get_group_member_uids(self, groups = [], excluded_groups = []): + """Get a list of member uids in a group + + Arguements: + groups -- the groups members must be a member of + excluded_groups -- the groups members cannot be a part of + """ + + query_result = self.__con__.search_s( + "dc=csh,dc=rit,dc=edu", + ldap.SCOPE_SUBTREE, + self.get_query_for_groups(groups=groups, excluded_groups=excluded_groups), + ['uid']) + + return [member[1]['uid'][0].decode('utf-8') for member in query_result if not member[1] == {} and "cn=users" in member[0]] + + def get_group_member_uuids(self, groups = [], excluded_groups = []): + """Get a list of member uuids in a group (ipaUniqueId) + + Arguements: + groups -- the groups members must be a member of + excluded_groups -- the groups members cannot be a part of + """ + + query_result = self.__con__.search_s( + "dc=csh,dc=rit,dc=edu", + ldap.SCOPE_SUBTREE, + self.get_query_for_groups(groups=groups, excluded_groups=excluded_groups), + ['ipaUniqueId']) + + return [member[1]['ipaUniqueId'][0].decode('utf-8') for member in query_result if not member[1] == {} and "cn=users" in member[0]] + def enqueue_mod(self, dn, mod): """Enqueue a LDAP modification. @@ -187,3 +278,4 @@ def flush_mod(self): continue self.__mod_queue__[dn] = None self.__pending_mod_dn__ = [] + From 7da9f5afb6d37014b916900c3a0c31c497b0b5a8 Mon Sep 17 00:00:00 2001 From: "Noah Hanford (spaced)" Date: Fri, 12 Jun 2026 22:18:08 -0400 Subject: [PATCH 2/2] fix: lint --- csh_ldap/__init__.py | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/csh_ldap/__init__.py b/csh_ldap/__init__.py index 986b6ed..265dffd 100644 --- a/csh_ldap/__init__.py +++ b/csh_ldap/__init__.py @@ -149,7 +149,7 @@ def get_directorship_heads(self, val): True) for dn in ret] - def get_query_for_groups(self, groups = [], excluded_groups = []): + def get_query_for_groups(self, groups = None, excluded_groups = None): """Returns the ldap query string to get members in groups but not in others Argumenets: @@ -158,7 +158,10 @@ def get_query_for_groups(self, groups = [], excluded_groups = []): """ group_dns = [f"(memberOf=cn={group},cn=groups,cn=accounts,dc=csh,dc=rit,dc=edu)" for group in groups] - excluded_group_dns = [f"(memberOf=cn={group},cn=groups,cn=accounts,dc=csh,dc=rit,dc=edu)" for group in excluded_groups] + excluded_group_dns = [ + f"(memberOf=cn={group},cn=groups,cn=accounts,dc=csh,dc=rit,dc=edu)" + for group in excluded_groups + ] query = "" @@ -179,15 +182,20 @@ def get_query_for_groups(self, groups = [], excluded_groups = []): return query - def get_group_member_attributes(self, groups = [], excluded_groups = [], attributes = ['uid']): - """Returns a list of dicts containing all the attributes requested in the groups listed in groups, but not in exlcuded_groups + def get_group_member_attributes(self, groups = None, excluded_groups = None, attributes = None): + """Returns a list of dicts containing all the attributes requested in the groups listed in groups, + but not in exlcuded_groups Arguements: groups -- the groups members must be a member of excluded_groups -- the groups members cannot be a part of - attributues -- the ldap attributes to return + attributues -- the ldap attributes to return, defaults to uid """ + # I HATE PYTHON + if attributes is None: + attributes = ['uid'] + query_result = self.__con__.search_s( "dc=csh,dc=rit,dc=edu", ldap.SCOPE_SUBTREE, @@ -196,7 +204,7 @@ def get_group_member_attributes(self, groups = [], excluded_groups = [], attribu # the rest of this could be one giant list compression but I don't hate you that much so I chose not to - # filter out subgroups, probably the second check all we need + # filter out subgroups, probably the second check all we need # but if we used just the second one but the first one was empty that would probably be confusing? byte_result = [member[1] for member in query_result if not member[1] == {} and "cn=users" in member[0]] @@ -208,7 +216,7 @@ def get_group_member_attributes(self, groups = [], excluded_groups = [], attribu return result - def get_group_member_uids(self, groups = [], excluded_groups = []): + def get_group_member_uids(self, groups = None, excluded_groups = None): """Get a list of member uids in a group Arguements: @@ -222,9 +230,13 @@ def get_group_member_uids(self, groups = [], excluded_groups = []): self.get_query_for_groups(groups=groups, excluded_groups=excluded_groups), ['uid']) - return [member[1]['uid'][0].decode('utf-8') for member in query_result if not member[1] == {} and "cn=users" in member[0]] + return [ + member[1]['uid'][0].decode('utf-8') + for member in query_result + if not member[1] == {} and "cn=users" in member[0] + ] - def get_group_member_uuids(self, groups = [], excluded_groups = []): + def get_group_member_uuids(self, groups = None, excluded_groups = None): """Get a list of member uuids in a group (ipaUniqueId) Arguements: @@ -238,7 +250,11 @@ def get_group_member_uuids(self, groups = [], excluded_groups = []): self.get_query_for_groups(groups=groups, excluded_groups=excluded_groups), ['ipaUniqueId']) - return [member[1]['ipaUniqueId'][0].decode('utf-8') for member in query_result if not member[1] == {} and "cn=users" in member[0]] + return [ + member[1]['ipaUniqueId'][0].decode('utf-8') + for member in query_result + if not member[1] == {} and "cn=users" in member[0] + ] def enqueue_mod(self, dn, mod): """Enqueue a LDAP modification. @@ -278,4 +294,3 @@ def flush_mod(self): continue self.__mod_queue__[dn] = None self.__pending_mod_dn__ = [] -