From a7d9d7cc8bef643bc5b2f11a5511acad463da1d9 Mon Sep 17 00:00:00 2001 From: d-w-moore Date: Thu, 5 Feb 2026 22:05:31 -0500 Subject: [PATCH 1/2] [_763] Adapt PRC remove remote user call style for GEN_ADMIN When calling to GEN_ADMIN to remove a remote user with separate user and zone parameters, the server will not remove the /tempZone/**/user#hello collections. So, we now force PRC to use an integrated user#zone format. --- irods/manager/user_manager.py | 30 ++++++++++++++++++++++++++++-- irods/test/admin_test.py | 23 ++++++++++++++++++++--- irods/test/user_group_test.py | 1 + 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/irods/manager/user_manager.py b/irods/manager/user_manager.py index 90f4be813..278695728 100644 --- a/irods/manager/user_manager.py +++ b/irods/manager/user_manager.py @@ -45,7 +45,28 @@ def remove_quota(self, user_name, resource="total"): self._get_session, "set-quota", "user", user_name, resource, "0" ) + @staticmethod + def _parse_user_and_zone(user_param, zone_param): + """Parse out the uesr name ane zone name from USER and ZONE string parameters. + If the USER string contains # and a non-null-length ZONE spec, ensure that + multiply specified zone names agree. + """ + if '#' in user_param: + u_parsed_user, u_parsed_zone = user_param.split('#',1) + if not u_parsed_zone: + raise RuntimeError("The compound user#zone specification may not contain a zero-length zone") + else: + if '#' in u_parsed_zone: + raise RuntimeError(f"{u_parsed_zone = } is wrongly formatted") + if zone_param and (u_parsed_zone != zone_param): + raise RuntimeError(f"Two nonzero-length zone names ({u_parsed_zone}, {zone_param}) " + " were given, but they do not agree.") + return u_parsed_user, u_parsed_zone + return user_param, zone_param + def get(self, user_name, user_zone=""): + user_name, user_zone = self._parse_user_and_zone(user_name, user_zone) + if not user_zone: user_zone = self.sess.zone @@ -121,6 +142,12 @@ def create(self, user_name, user_type, user_zone="", auth_str=""): def remove(self, user_name, user_zone="", _object=None): if _object is None: _object = self.get(user_name, user_zone) + + if _object.type == "rodsgroup": + uz_args = (f"{_object.name}",) + else: + uz_args = (f"{_object.name}#{_object.zone}",) + message_body = GeneralAdminRequest( "rm", ( @@ -128,8 +155,7 @@ def remove(self, user_name, user_zone="", _object=None): if (_object.type != "rodsgroup" or self.sess.server_version < (4, 3, 2)) else "group" ), - user_name, - user_zone, + *uz_args, ) request = iRODSMessage( "RODS_API_REQ", msg=message_body, int_info=api_number["GENERAL_ADMIN_AN"] diff --git a/irods/test/admin_test.py b/irods/test/admin_test.py index 91faad393..b0760061b 100644 --- a/irods/test/admin_test.py +++ b/irods/test/admin_test.py @@ -4,16 +4,19 @@ import os import sys import unittest -from irods.models import User, Group + +from irods.column import Like from irods.exception import ( UserDoesNotExist, ResourceDoesNotExist, SYS_NO_API_PRIV, ) -from irods.session import iRODSSession +from irods.models import Collection, Group, User from irods.resource import iRODSResource -import irods.test.helpers as helpers +from irods.session import iRODSSession + import irods.keywords as kw +import irods.test.helpers as helpers class TestAdmin(unittest.TestCase): @@ -531,6 +534,20 @@ def test_set_user_info(self): with self.assertRaises(UserDoesNotExist): self.sess.users.get(self.new_user_name) + def test_deleting_remote_user_including_home_collection_and_trash_artifact__issue_763(self): + remote_zone = remote_user = None + try: + remote_zone = (sess := self.sess).zones.create('other_zone', 'remote') + remote_user = sess.users.create(user_name='myuser', user_type='rodsuser', user_zone=remote_zone.name) + remote_user.remove() + remaining_collections = list( + sess.query(Collection).filter(Like(Collection.name, f'%/{remote_user}#{remote_zone}')) + ) + self.assertEqual(len(remaining_collections), 0) + finally: + if remote_zone: + remote_zone.remove() + if __name__ == "__main__": # let the tests find the parent irods lib diff --git a/irods/test/user_group_test.py b/irods/test/user_group_test.py index 5ce8b84aa..4d9f0c529 100644 --- a/irods/test/user_group_test.py +++ b/irods/test/user_group_test.py @@ -143,6 +143,7 @@ def generator(p=OLDPASS): shutil.rmtree(ENV_DIR) ses.users.remove("alice") + def test_modifying_password_at_various_lengths__issue_328(self): ses = self.sess try: From 506b81fcddbe97a1d11391b4e2e5535dae3b3de2 Mon Sep 17 00:00:00 2001 From: d-w-moore Date: Tue, 17 Feb 2026 11:33:31 -0500 Subject: [PATCH 2/2] patch, ruff chgs. nr.3 --- irods/manager/user_manager.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/irods/manager/user_manager.py b/irods/manager/user_manager.py index 278695728..9a934c304 100644 --- a/irods/manager/user_manager.py +++ b/irods/manager/user_manager.py @@ -52,15 +52,17 @@ def _parse_user_and_zone(user_param, zone_param): multiply specified zone names agree. """ if '#' in user_param: - u_parsed_user, u_parsed_zone = user_param.split('#',1) + u_parsed_user, u_parsed_zone = user_param.split('#', 1) if not u_parsed_zone: raise RuntimeError("The compound user#zone specification may not contain a zero-length zone") else: if '#' in u_parsed_zone: raise RuntimeError(f"{u_parsed_zone = } is wrongly formatted") if zone_param and (u_parsed_zone != zone_param): - raise RuntimeError(f"Two nonzero-length zone names ({u_parsed_zone}, {zone_param}) " - " were given, but they do not agree.") + raise RuntimeError( + f"Two nonzero-length zone names ({u_parsed_zone}, {zone_param}) " + " were given, but they do not agree." + ) return u_parsed_user, u_parsed_zone return user_param, zone_param