From 4ace6a1b779733a28a0ef1855e5a299b92740736 Mon Sep 17 00:00:00 2001 From: George Taylor Date: Tue, 23 Jul 2024 16:19:21 +0100 Subject: [PATCH 1/9] :bug: Fix incorrect module path for update user roles (#51) --- cli/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/__init__.py b/cli/__init__.py index 1f0ae38..09a8d75 100644 --- a/cli/__init__.py +++ b/cli/__init__.py @@ -34,7 +34,7 @@ def add_roles_to_users( root_dn, user_role_list, ): - cli.ldap.user.process_user_roles_list( + cli.ldap_cmds.user.process_user_roles_list( user_role_list, user_ou, root_dn, @@ -73,7 +73,7 @@ def update_user_home_areas( user_ou, root_dn, ): - cli.ldap.user.change_home_areas( + cli.ldap_cmds.user.change_home_areas( old_home_area, new_home_area, user_ou, @@ -143,7 +143,7 @@ def update_user_roles( user_filter, role_filter, ): - cli.ldap.user.update_roles( + cli.ldap_cmds.user.update_roles( roles, user_ou, root_dn, From c2d35c5177ac49c0f6caeb2d596d3fdb533c8cf6 Mon Sep 17 00:00:00 2001 From: George Taylor Date: Wed, 24 Jul 2024 10:24:27 +0100 Subject: [PATCH 2/9] =?UTF-8?q?=F0=9F=9A=91=20=20Update=20User=20Roles:=20?= =?UTF-8?q?Use=20sync=20search=20and=20add=20summary=20(#52)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :ambulance: Use sync search and add summary for searches and actions * Formatted code with black --line-length 120 --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- cli/ldap_cmds/user.py | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/cli/ldap_cmds/user.py b/cli/ldap_cmds/user.py index 3196ec8..2313548 100644 --- a/cli/ldap_cmds/user.py +++ b/cli/ldap_cmds/user.py @@ -49,7 +49,7 @@ def change_home_areas( search_filter = ( f"(&(objectclass={object_class})(userHomeArea={old_home_area})(!(cn={old_home_area}))(!(endDate=*)))" ) - ldap_connection.search( + ldap_connection.search_s( ",".join( [ user_ou, @@ -169,7 +169,7 @@ def update_roles( # # Search for users matching the user_filter try: - ldap_connection_user_filter.search( + ldap_connection_user_filter.search_s( ",".join([user_ou, root_dn]), user_filter, attributes=["cn"], @@ -181,6 +181,7 @@ def update_roles( users_found = sorted([entry.cn.value for entry in ldap_connection_user_filter.entries if entry.cn.value]) log.debug("users found from user filter") log.debug(users_found) + log.info(f"Found {len(users_found)} users matching the user filter") ldap_connection_user_filter.unbind() roles_filter_list = role_filter.split(",") @@ -207,7 +208,7 @@ def update_roles( raise e try: - ldap_connection_role_filter.search( + ldap_connection_role_filter.search_s( ",".join([user_ou, root_dn]), full_role_filter, attributes=["cn"], @@ -222,6 +223,7 @@ def update_roles( ) log.debug("users found from roles filter: ") log.debug(roles_found) + log.info(f"Found {len(roles_found)} users with roles matching the role filter") ldap_connection_role_filter.unbind() @@ -233,6 +235,7 @@ def update_roles( # cartesian_product = [(user, role) for user in matched_users for role in roles] cartesian_product = list(product(matched_users, roles)) + log.info(f"Found {len(cartesian_product)} combinations of users and roles") log.debug("cartesian product: ") log.debug(cartesian_product) @@ -268,17 +271,36 @@ def update_roles( log.e(f"Failed to add role '{item[1]}' to user '{item[0]}'") log.debug(ldap_connection_action.result) elif remove: + removed = 0 + not_removed = 0 + failed = 0 ldap_connection_action.delete(f"cn={item[1]},cn={item[0]},{user_ou},{root_dn}") if ldap_connection_action.result["result"] == 0: log.info(f"Successfully removed role '{item[1]}' from user '{item[0]}'") + removed = removed + 1 elif ldap_connection_action.result["result"] == 32: log.info(f"Role '{item[1]}' already absent for user '{item[0]}'") + not_removed = not_removed + 1 else: log.error(f"Failed to remove role '{item[1]}' from user '{item[0]}'") log.debug(ldap_connection_action.result) + failed = failed + 1 else: log.error("No action specified") + log.info("SUMMARY") + log.info("User/role searches:") + log.info(f"Found {len(roles_found)} users with roles matching the role filter") + log.info(f"Found {len(users_found)} users matching the user filter") + + log.info("This produces the following matches:") + log.info(f"Found {len(matched_users)} users with roles matching the role filter and user filter") + + log.info("Actions:") + log.info(f"Successfully removed {removed} roles") + log.info(f"Roles already absent for {not_removed} users") + log.info(f"Failed to remove {failed} roles due to errors") + if update_notes: connection = cli.database.connection() log.debug("Created database cursor successfully") @@ -374,7 +396,7 @@ def deactivate_crc_users(user_ou, root_dn): found_users = [] for home_area in home_areas: - ldap_connection.search( + ldap_connection.search_s( ",".join( [ user_ou, @@ -387,7 +409,7 @@ def deactivate_crc_users(user_ou, root_dn): found_users.append(entry.entry_dn for entry in ldap_connection.entries) - ldap_connection.search( + ldap_connection.search_s( ",".join([user_ou, root_dn]), f"(&(!(userHomeArea=*)){user_filter})", attributes=["dn"], @@ -441,7 +463,7 @@ def user_expiry(user_ou, root_dn): env.secrets.get("LDAP_BIND_PASSWORD"), ) try: - ldap_connection_lock.search( + ldap_connection_lock.search_s( ",".join( [ user_ou, @@ -480,7 +502,7 @@ def user_expiry(user_ou, root_dn): ) try: - ldap_connection_unlock.search( + ldap_connection_unlock.search_s( ",".join([user_ou, root_dn]), f"(&(pwdAccountLockedTime=000001010000Z)(|(!(endDate=*))(endDate>={date_str}))(|(!(startDate=*))(startDate<={date_str})))", attributes=["cn"], @@ -520,7 +542,7 @@ def remove_all_user_passwords(user_ou, root_dn): user_filter = "(!(cn=AutomatedTestUser))" try: - ldap_connection.search( + ldap_connection.search_s( ",".join([user_ou, root_dn]), user_filter, attributes=["cn"], From 1c51796e5247c12e01d60850a4f3fe532d8ca74b Mon Sep 17 00:00:00 2001 From: George Taylor Date: Wed, 24 Jul 2024 11:58:51 +0100 Subject: [PATCH 3/9] Better summary for update user roles job (#53) * :ambulance: Use sync search and add summary for searches and actions * Formatted code with black --line-length 120 * better debugging --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- cli/ldap_cmds/user.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/cli/ldap_cmds/user.py b/cli/ldap_cmds/user.py index 2313548..6432b66 100644 --- a/cli/ldap_cmds/user.py +++ b/cli/ldap_cmds/user.py @@ -249,6 +249,9 @@ def update_roles( log.exception("Failed to connect to LDAP") raise e + removed = 0 + not_removed = 0 + failed = 0 for item in cartesian_product: if add: try: @@ -287,19 +290,19 @@ def update_roles( failed = failed + 1 else: log.error("No action specified") - - log.info("SUMMARY") - log.info("User/role searches:") - log.info(f"Found {len(roles_found)} users with roles matching the role filter") - log.info(f"Found {len(users_found)} users matching the user filter") - - log.info("This produces the following matches:") - log.info(f"Found {len(matched_users)} users with roles matching the role filter and user filter") - - log.info("Actions:") - log.info(f"Successfully removed {removed} roles") - log.info(f"Roles already absent for {not_removed} users") - log.info(f"Failed to remove {failed} roles due to errors") + + log.info("\n==========================\n\tSUMMARY\n==========================") + log.info("User/role searches:") + log.info(f" - Found {len(roles_found)} users with roles matching the role filter") + log.info(f" - Found {len(users_found)} users matching the user filter") + + log.info("This produces the following matches:") + log.info(f" - Found {len(matched_users)} users with roles matching the role filter and user filter") + + log.info("Actions:") + log.info(f" - Successfully removed {removed} roles") + log.info(f" - Roles already absent for {not_removed} users") + log.info(f" - Failed to remove {failed} roles due to errors") if update_notes: connection = cli.database.connection() From 360fc22d8b48ea0a0cd3ad3863bb2acdb4606a7a Mon Sep 17 00:00:00 2001 From: George Taylor Date: Wed, 24 Jul 2024 12:11:20 +0100 Subject: [PATCH 4/9] Update user.py --- cli/ldap_cmds/user.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cli/ldap_cmds/user.py b/cli/ldap_cmds/user.py index 6432b66..c3ae0af 100644 --- a/cli/ldap_cmds/user.py +++ b/cli/ldap_cmds/user.py @@ -49,7 +49,7 @@ def change_home_areas( search_filter = ( f"(&(objectclass={object_class})(userHomeArea={old_home_area})(!(cn={old_home_area}))(!(endDate=*)))" ) - ldap_connection.search_s( + ldap_connection.search( ",".join( [ user_ou, @@ -169,7 +169,7 @@ def update_roles( # # Search for users matching the user_filter try: - ldap_connection_user_filter.search_s( + ldap_connection_user_filter.search( ",".join([user_ou, root_dn]), user_filter, attributes=["cn"], @@ -208,7 +208,7 @@ def update_roles( raise e try: - ldap_connection_role_filter.search_s( + ldap_connection_role_filter.search( ",".join([user_ou, root_dn]), full_role_filter, attributes=["cn"], @@ -399,7 +399,7 @@ def deactivate_crc_users(user_ou, root_dn): found_users = [] for home_area in home_areas: - ldap_connection.search_s( + ldap_connection.search( ",".join( [ user_ou, @@ -412,7 +412,7 @@ def deactivate_crc_users(user_ou, root_dn): found_users.append(entry.entry_dn for entry in ldap_connection.entries) - ldap_connection.search_s( + ldap_connection.search( ",".join([user_ou, root_dn]), f"(&(!(userHomeArea=*)){user_filter})", attributes=["dn"], @@ -466,7 +466,7 @@ def user_expiry(user_ou, root_dn): env.secrets.get("LDAP_BIND_PASSWORD"), ) try: - ldap_connection_lock.search_s( + ldap_connection_lock.search( ",".join( [ user_ou, @@ -505,7 +505,7 @@ def user_expiry(user_ou, root_dn): ) try: - ldap_connection_unlock.search_s( + ldap_connection_unlock.search( ",".join([user_ou, root_dn]), f"(&(pwdAccountLockedTime=000001010000Z)(|(!(endDate=*))(endDate>={date_str}))(|(!(startDate=*))(startDate<={date_str})))", attributes=["cn"], @@ -545,7 +545,7 @@ def remove_all_user_passwords(user_ou, root_dn): user_filter = "(!(cn=AutomatedTestUser))" try: - ldap_connection.search_s( + ldap_connection.search( ",".join([user_ou, root_dn]), user_filter, attributes=["cn"], From 2217277dc1335146f03fe27e6d6ceb2941a2ff0b Mon Sep 17 00:00:00 2001 From: George Taylor Date: Thu, 25 Jul 2024 09:56:40 +0100 Subject: [PATCH 5/9] :ambulance: Correct logic for matched user sets + role filtering (#55) * :ambulance: correct logic for matched user sets + role filtering * Formatted code with black --line-length 120 * Update role filter logic in user.py --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- cli/__init__.py | 30 ++++------------------------- cli/ldap_cmds/user.py | 44 ++++++++++++++++++++++++------------------- setup.py | 2 +- 3 files changed, 30 insertions(+), 46 deletions(-) diff --git a/cli/__init__.py b/cli/__init__.py index 09a8d75..1a9fcf5 100644 --- a/cli/__init__.py +++ b/cli/__init__.py @@ -118,31 +118,9 @@ def update_user_home_areas( help="Remove role from users", is_flag=True, ) -@click.option( - "-rf", - "--role-filter", - help='Comma separated string to generate roles filter from eg "role1,role2,role3"', - required=False, - default="*", -) -@click.option( - "-uf", - "--user-filter", - help="Filter to find users", - required=False, - default="(userSector=*)", -) -def update_user_roles( - roles, - user_ou, - root_dn, - add, - remove, - update_notes, - user_note, - user_filter, - role_filter, -): +@click.option("-uf", "--user-filter", help="Filter to find users", required=False, default="(objectclass=*)") +@click.option("--roles-to-filter", help="Roles to filter", required=False, default="*") +def update_user_roles(roles, user_ou, root_dn, add, remove, update_notes, user_note, user_filter, roles_to_filter): cli.ldap_cmds.user.update_roles( roles, user_ou, @@ -152,7 +130,7 @@ def update_user_roles( update_notes, user_note=user_note, user_filter=user_filter, - role_filter=role_filter, + roles_to_filter=roles_to_filter, ) diff --git a/cli/ldap_cmds/user.py b/cli/ldap_cmds/user.py index c3ae0af..3474a8f 100644 --- a/cli/ldap_cmds/user.py +++ b/cli/ldap_cmds/user.py @@ -150,9 +150,7 @@ def process_user_roles_list(user_role_list, user_ou="ou=Users", root_dn="dc=moj, ######################################### -def update_roles( - roles, user_ou, root_dn, add, remove, update_notes, user_note, user_filter="(userSector=*)", role_filter="*" -): +def update_roles(roles, user_ou, root_dn, add, remove, update_notes, user_note, user_filter, roles_to_filter): if update_notes and (user_note is None or len(user_note) < 1): log.error("User note must be provided when updating notes") raise Exception("User note must be provided when updating notes") @@ -168,6 +166,9 @@ def update_roles( raise e # # Search for users matching the user_filter + + user_filter = f"(&(objectclass=NDUser){user_filter})" + log.debug(f"User filter: {user_filter}") try: ldap_connection_user_filter.search( ",".join([user_ou, root_dn]), @@ -184,17 +185,16 @@ def update_roles( log.info(f"Found {len(users_found)} users matching the user filter") ldap_connection_user_filter.unbind() - roles_filter_list = role_filter.split(",") roles = roles.split(",") # create role filter - if len(roles_filter_list) > 0: - full_role_filter = ( - f"(&(objectclass=NDRoleAssociation)(|{''.join(['(cn=' + role + ')' for role in roles_filter_list])}))" - ) + if len(roles_to_filter) > 0: + full_role_filter = f"(&(objectclass=NDRoleAssociation)(|{''.join(['(cn=' + role + ')' for role in roles_to_filter.split(',')])}))" else: full_role_filter = "(&(objectclass=NDRoleAssociation)(cn=*))" + log.debug(full_role_filter) + # Search for roles matching the role_filter try: @@ -217,7 +217,6 @@ def update_roles( except Exception as e: log.exception("Failed to search for roles") raise e - roles_found = sorted( set({entry.entry_dn.split(",")[1].split("=")[1] for entry in ldap_connection_role_filter.entries}) ) @@ -228,14 +227,19 @@ def update_roles( ldap_connection_role_filter.unbind() # generate a list of matches in roles and users - matched_users = set(users_found) & set(roles_found) + users_found_set = set(users_found) + roles_found_set = set(roles_found) + + log.debug(users_found_set) + log.debug(roles_found_set) + + matched_users = sorted(users_found_set.intersection(roles_found_set)) log.debug("matched users: ") log.debug(matched_users) # cartesian_product = [(user, role) for user in matched_users for role in roles] - cartesian_product = list(product(matched_users, roles)) - log.info(f"Found {len(cartesian_product)} combinations of users and roles") + log.info(f"Created {len(cartesian_product)} combinations of users and roles") log.debug("cartesian product: ") log.debug(cartesian_product) @@ -249,8 +253,8 @@ def update_roles( log.exception("Failed to connect to LDAP") raise e - removed = 0 - not_removed = 0 + actioned = 0 + not_actioned = 0 failed = 0 for item in cartesian_product: if add: @@ -268,8 +272,10 @@ def update_roles( raise e if ldap_connection_action.result["result"] == 0: log.info(f"Successfully added role '{item[1]}' to user '{item[0]}'") + actioned = actioned + 1 elif ldap_connection_action.result["result"] == 68: log.info(f"Role '{item[1]}' already present for user '{item[0]}'") + not_actioned = not_actioned + 1 else: log.e(f"Failed to add role '{item[1]}' to user '{item[0]}'") log.debug(ldap_connection_action.result) @@ -280,17 +286,17 @@ def update_roles( ldap_connection_action.delete(f"cn={item[1]},cn={item[0]},{user_ou},{root_dn}") if ldap_connection_action.result["result"] == 0: log.info(f"Successfully removed role '{item[1]}' from user '{item[0]}'") - removed = removed + 1 + actioned = actioned + 1 elif ldap_connection_action.result["result"] == 32: log.info(f"Role '{item[1]}' already absent for user '{item[0]}'") - not_removed = not_removed + 1 + not_actioned = not_actioned + 1 else: log.error(f"Failed to remove role '{item[1]}' from user '{item[0]}'") log.debug(ldap_connection_action.result) failed = failed + 1 else: log.error("No action specified") - + log.info("\n==========================\n\tSUMMARY\n==========================") log.info("User/role searches:") log.info(f" - Found {len(roles_found)} users with roles matching the role filter") @@ -300,8 +306,8 @@ def update_roles( log.info(f" - Found {len(matched_users)} users with roles matching the role filter and user filter") log.info("Actions:") - log.info(f" - Successfully removed {removed} roles") - log.info(f" - Roles already absent for {not_removed} users") + log.info(f" - Successfully actioned {actioned} roles") + log.info(f" - Roles already in desired state for {not_actioned} users") log.info(f" - Failed to remove {failed} roles due to errors") if update_notes: diff --git a/setup.py b/setup.py index 10818b2..d22494e 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ setup( name="ldap-automation", - version="0.1", + version="0.2", packages=find_packages(), install_requires=all_reqs, entry_points=""" From edc628134f72d675a8addcc485b959fc56361bfe Mon Sep 17 00:00:00 2001 From: George Taylor Date: Thu, 25 Jul 2024 16:35:27 +0100 Subject: [PATCH 6/9] Fix update user role cmd (#56) * :ambulance: correct logic for matched user sets + role filtering * Formatted code with black --line-length 120 * Update role filter logic in user.py * Formatted code with black --line-length 120 * aliasing * Update user.py --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- cli/ldap_cmds/user.py | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/cli/ldap_cmds/user.py b/cli/ldap_cmds/user.py index 3474a8f..2f6d69b 100644 --- a/cli/ldap_cmds/user.py +++ b/cli/ldap_cmds/user.py @@ -9,13 +9,15 @@ env, ) +import ldap + from cli.ldap_cmds import ( ldap_connect, ) from ldap3 import ( MODIFY_REPLACE, MODIFY_DELETE, - DEREF_NEVER, + DEREF_ALWAYS, ) import cli.database @@ -156,11 +158,8 @@ def update_roles(roles, user_ou, root_dn, add, remove, update_notes, user_note, raise Exception("User note must be provided when updating notes") try: - ldap_connection_user_filter = ldap_connect( - env.vars.get("LDAP_HOST"), - env.vars.get("LDAP_USER"), - env.secrets.get("LDAP_BIND_PASSWORD"), - ) + ldap_connection_user_filter = ldap.initialize("ldap://" + env.vars.get("LDAP_HOST")) + ldap_connection_user_filter.simple_bind_s(env.vars.get("LDAP_USER"), env.secrets.get("LDAP_BIND_PASSWORD")) except Exception as e: log.exception("Failed to connect to LDAP") raise e @@ -170,16 +169,17 @@ def update_roles(roles, user_ou, root_dn, add, remove, update_notes, user_note, user_filter = f"(&(objectclass=NDUser){user_filter})" log.debug(f"User filter: {user_filter}") try: - ldap_connection_user_filter.search( + user_filter_results = ldap_connection_user_filter.search_s( ",".join([user_ou, root_dn]), + ldap.SCOPE_SUBTREE, user_filter, - attributes=["cn"], + ["cn"], ) except Exception as e: log.exception("Failed to search for users") raise e - users_found = sorted([entry.cn.value for entry in ldap_connection_user_filter.entries if entry.cn.value]) + users_found = sorted(set([entry[1]["cn"][0].decode("utf-8") for entry in user_filter_results])) log.debug("users found from user filter") log.debug(users_found) log.info(f"Found {len(users_found)} users matching the user filter") @@ -198,28 +198,24 @@ def update_roles(roles, user_ou, root_dn, add, remove, update_notes, user_note, # Search for roles matching the role_filter try: - ldap_connection_role_filter = ldap_connect( - env.vars.get("LDAP_HOST"), - env.vars.get("LDAP_USER"), - env.secrets.get("LDAP_BIND_PASSWORD"), - ) + ldap_connection_role_filter = ldap.initialize("ldap://" + env.vars.get("LDAP_HOST")) + ldap_connection_role_filter.simple_bind_s(env.vars.get("LDAP_USER"), env.secrets.get("LDAP_BIND_PASSWORD")) except Exception as e: log.exception("Failed to connect to LDAP") raise e try: - ldap_connection_role_filter.search( + role_filter_results = ldap_connection_role_filter.search_s( ",".join([user_ou, root_dn]), + ldap.SCOPE_SUBTREE, full_role_filter, - attributes=["cn"], - dereference_aliases=DEREF_NEVER, + ["cn"], ) except Exception as e: log.exception("Failed to search for roles") raise e - roles_found = sorted( - set({entry.entry_dn.split(",")[1].split("=")[1] for entry in ldap_connection_role_filter.entries}) - ) + + roles_found = sorted(set({dn.split(",")[1].split("=")[1] for dn, entry in role_filter_results})) log.debug("users found from roles filter: ") log.debug(roles_found) log.info(f"Found {len(roles_found)} users with roles matching the role filter") From 341303bed154e8ad12778d8df8cece9e48c7c75e Mon Sep 17 00:00:00 2001 From: George Taylor Date: Thu, 25 Jul 2024 17:00:01 +0100 Subject: [PATCH 7/9] Fix update user role cmd (#57) * :ambulance: correct logic for matched user sets + role filtering * Formatted code with black --line-length 120 * Update role filter logic in user.py * Formatted code with black --line-length 120 * aliasing * Update user.py * Update user.py * Formatted code with black --line-length 120 --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- cli/ldap_cmds/user.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/cli/ldap_cmds/user.py b/cli/ldap_cmds/user.py index 2f6d69b..3f67140 100644 --- a/cli/ldap_cmds/user.py +++ b/cli/ldap_cmds/user.py @@ -205,11 +205,8 @@ def update_roles(roles, user_ou, root_dn, add, remove, update_notes, user_note, raise e try: - role_filter_results = ldap_connection_role_filter.search_s( - ",".join([user_ou, root_dn]), - ldap.SCOPE_SUBTREE, - full_role_filter, - ["cn"], + role_filter_results = ldap_connection_role_filter.search_ext_s( + ",".join([user_ou, root_dn]), ldap.SCOPE_SUBTREE, full_role_filter, ["cn"], sizelimit=0 ) except Exception as e: log.exception("Failed to search for roles") From 535372ce78b577aa93d2ac46ac4fee6718b44f2b Mon Sep 17 00:00:00 2001 From: George Taylor Date: Thu, 25 Jul 2024 18:25:23 +0100 Subject: [PATCH 8/9] =?UTF-8?q?=E2=9C=A8=20Use=20paged=20search=20to=20sup?= =?UTF-8?q?port=20larger=20data=20sets=20(#58)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :ambulance: correct logic for matched user sets + role filtering * Formatted code with black --line-length 120 * Update role filter logic in user.py * Formatted code with black --line-length 120 * aliasing * Update user.py * Update user.py * Formatted code with black --line-length 120 * implement paging because we love ldap :heart: * remove test case + add var for page size * Formatted code with black --line-length 120 --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- cli/ldap_cmds/user.py | 63 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 52 insertions(+), 11 deletions(-) diff --git a/cli/ldap_cmds/user.py b/cli/ldap_cmds/user.py index 3f67140..2ad4a02 100644 --- a/cli/ldap_cmds/user.py +++ b/cli/ldap_cmds/user.py @@ -10,6 +10,8 @@ ) import ldap +from ldap.controls import SimplePagedResultsControl +import ldap.modlist as modlist from cli.ldap_cmds import ( ldap_connect, @@ -187,7 +189,7 @@ def update_roles(roles, user_ou, root_dn, add, remove, update_notes, user_note, roles = roles.split(",") - # create role filter + # Create role filter if len(roles_to_filter) > 0: full_role_filter = f"(&(objectclass=NDRoleAssociation)(|{''.join(['(cn=' + role + ')' for role in roles_to_filter.split(',')])}))" else: @@ -195,30 +197,69 @@ def update_roles(roles, user_ou, root_dn, add, remove, update_notes, user_note, log.debug(full_role_filter) - # Search for roles matching the role_filter - try: ldap_connection_role_filter = ldap.initialize("ldap://" + env.vars.get("LDAP_HOST")) ldap_connection_role_filter.simple_bind_s(env.vars.get("LDAP_USER"), env.secrets.get("LDAP_BIND_PASSWORD")) - except Exception as e: + ldap_connection_role_filter.set_option(ldap.OPT_REFERRALS, 0) + except ldap.LDAPError as e: log.exception("Failed to connect to LDAP") raise e + roles_search_result = [] + pages = 0 + if env.vars.get("LDAP_PAGE_SIZE") is None: + ldap_page_size = 100 + else: + try: + ldap_page_size = int(env.vars.get("LDAP_PAGE_SIZE")) + except ValueError: + log.error("LDAP_PAGE_SIZE must be an integer") + raise ValueError("LDAP_PAGE_SIZE must be an integer") + + page_control = SimplePagedResultsControl(True, size=ldap_page_size, cookie="") + try: - role_filter_results = ldap_connection_role_filter.search_ext_s( - ",".join([user_ou, root_dn]), ldap.SCOPE_SUBTREE, full_role_filter, ["cn"], sizelimit=0 + response = ldap_connection_role_filter.search_ext( + ",".join([user_ou, root_dn]), ldap.SCOPE_SUBTREE, full_role_filter, ["cn"], serverctrls=[page_control] ) - except Exception as e: + + while True: + pages += 1 + log.debug(f"Processing page {pages}") + try: + rtype, rdata, rmsgid, serverctrls = ldap_connection_role_filter.result3(response) + roles_search_result.extend(rdata) + cookie = serverctrls[0].cookie + print(cookie) + if cookie: + page_control.cookie = cookie + response = ldap_connection_role_filter.search_ext( + ",".join([user_ou, root_dn]), + ldap.SCOPE_SUBTREE, + full_role_filter, + ["cn"], + serverctrls=[page_control], + ) + else: + break + except ldap.LDAPError as e: + log.exception("Error retrieving LDAP results") + raise e + + except ldap.LDAPError as e: log.exception("Failed to search for roles") raise e - roles_found = sorted(set({dn.split(",")[1].split("=")[1] for dn, entry in role_filter_results})) - log.debug("users found from roles filter: ") + finally: + ldap_connection_role_filter.unbind_s() + + roles_found = sorted(set({dn.split(",")[1].split("=")[1] for dn, entry in roles_search_result})) + + roles_found = sorted(roles_found) + log.debug("Users found from roles filter: ") log.debug(roles_found) log.info(f"Found {len(roles_found)} users with roles matching the role filter") - ldap_connection_role_filter.unbind() - # generate a list of matches in roles and users users_found_set = set(users_found) roles_found_set = set(roles_found) From e4d6a939bce056c896bf5a497bddfa1641beea2d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 14:05:17 +0100 Subject: [PATCH 9/9] Bump setuptools from 71.1.0 to 72.0.0 (#60) Bumps [setuptools](https://github.com/pypa/setuptools) from 71.1.0 to 72.0.0. - [Release notes](https://github.com/pypa/setuptools/releases) - [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst) - [Commits](https://github.com/pypa/setuptools/compare/v71.1.0...v72.0.0) --- updated-dependencies: - dependency-name: setuptools dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index dad8c3e..3582ac3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,4 +8,4 @@ python-dotenv==1.0.1 Jinja2==3.1.4 python-ldap requests==2.32.3 -setuptools==71.1.0 +setuptools==72.0.0