From 6d58ef4a30cf610b2c912a1e34c629b412fe76a8 Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Tue, 17 Mar 2026 14:19:27 +0100 Subject: [PATCH 1/3] Fix user.create() to respect use_uuid_as_userid setting Use generate_user_id/generate_login_name from plone.app.users.utils instead of hardcoded user_id logic. This respects use_uuid_as_userid, use_email_as_login, and custom IUserIdGenerator/ILoginNameGenerator utilities. Part of PLIP 4292. Co-Authored-By: Claude Opus 4.6 --- news/4292.bugfix | 3 +++ setup.py | 1 + src/plone/api/user.py | 30 ++++++++++++++++++++++++++---- 3 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 news/4292.bugfix diff --git a/news/4292.bugfix b/news/4292.bugfix new file mode 100644 index 00000000..d3e01008 --- /dev/null +++ b/news/4292.bugfix @@ -0,0 +1,3 @@ +Fix ``plone.api.user.create()`` to respect ``use_uuid_as_userid`` and +``use_email_as_login`` registry settings, as well as custom ``IUserIdGenerator`` +and ``ILoginNameGenerator`` utilities. diff --git a/setup.py b/setup.py index fbf415e0..11688d91 100644 --- a/setup.py +++ b/setup.py @@ -28,6 +28,7 @@ "Products.PlonePAS", "Products.CMFPlone", "decorator", + "plone.app.users", "plone.app.uuid", "plone.app.dexterity", "plone.app.intid", diff --git a/src/plone/api/user.py b/src/plone/api/user.py index 96a47083..5402b2c2 100644 --- a/src/plone/api/user.py +++ b/src/plone/api/user.py @@ -65,16 +65,31 @@ def create( "that is not email so you need to pass a username.", ) + # Generate user_id and login_name using the canonical implementation + # from plone.app.users, respecting use_uuid_as_userid, + # use_email_as_login, IUserIdGenerator, and ILoginNameGenerator. + from plone.app.users.utils import generate_login_name + from plone.app.users.utils import generate_user_id + + site = portal.get() + data = { + "username": username, + "email": email, + "fullname": properties.get("fullname", ""), + } + generate_user_id(site, data) + generate_login_name(site, data) + user_id = data["user_id"] + login_name = data.get("login_name", username or email) + registration = portal.get_tool("portal_registration") - user_id = use_email_as_username and email or username # Generate a random 8-char password if not password: chars = string.ascii_letters + string.digits password = "".join(random.choice(chars) for char in range(8)) - properties.update(username=user_id) - properties.update(email=email) + properties.update(username=user_id, email=email) registration.addMember( user_id, @@ -82,7 +97,14 @@ def create( roles, properties=properties, ) - return get(username=user_id) + + # If user_id differs from login_name (e.g. UUID as user id with + # email as login), update the login name accordingly. + if user_id != login_name: + pas = portal.get_tool("acl_users") + pas.updateLoginName(user_id, login_name) + + return get(userid=user_id) @mutually_exclusive_parameters("userid", "username") From af0002bfc73ee6b2043b49162bfd30ac287ea954 Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Mon, 23 Mar 2026 17:07:37 +0100 Subject: [PATCH 2/3] Update src/plone/api/user.py Co-authored-by: Maurits van Rees --- src/plone/api/user.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/plone/api/user.py b/src/plone/api/user.py index 5402b2c2..2e93b98f 100644 --- a/src/plone/api/user.py +++ b/src/plone/api/user.py @@ -77,10 +77,8 @@ def create( "email": email, "fullname": properties.get("fullname", ""), } - generate_user_id(site, data) - generate_login_name(site, data) - user_id = data["user_id"] - login_name = data.get("login_name", username or email) + user_id = generate_user_id(site, data) or username or email + login_name = generate_login_name(site, data) or username or email registration = portal.get_tool("portal_registration") From 6d536674c012a73a5c8b19d14d840cbcb33cdfef Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Mon, 23 Mar 2026 19:08:56 +0100 Subject: [PATCH 3/3] Fix test_create_with_username to work with generate_user_id With the new generate_user_id(), both calls (with and without email login) produce the same user_id "chuck" from the username. Delete the first user before recreating with different login settings. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/plone/api/tests/test_user.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plone/api/tests/test_user.py b/src/plone/api/tests/test_user.py index a7140bdb..4f8283fb 100644 --- a/src/plone/api/tests/test_user.py +++ b/src/plone/api/tests/test_user.py @@ -113,6 +113,9 @@ def test_create_with_username(self): ) self.assertEqual(user.getUserName(), "chuck@norris.org") + # Delete user before recreating with different login settings, + # because generate_user_id uses username as user_id in both cases. + api.user.delete(user=user) self._set_emaillogin(False) user = api.user.create(