From c8f3c0423e7a3d36d8e19427f23ebfec2eede841 Mon Sep 17 00:00:00 2001 From: Peter Ringelmann Date: Thu, 2 Apr 2026 18:03:19 +0200 Subject: [PATCH] fix(provisioning_api): allow clearing display name via editUserMultiField Map empty string to userId since User::setDisplayName() rejects "" -e Signed-off-by: Peter Ringelmann --- .../lib/Controller/UsersController.php | 4 +++- .../tests/Controller/UsersControllerTest.php | 23 +++++++++++++++++++ .../components/Users/userFormUtils.spec.ts | 12 ++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/apps/provisioning_api/lib/Controller/UsersController.php b/apps/provisioning_api/lib/Controller/UsersController.php index 67e391ba491..e398d7b4c9f 100644 --- a/apps/provisioning_api/lib/Controller/UsersController.php +++ b/apps/provisioning_api/lib/Controller/UsersController.php @@ -1078,7 +1078,9 @@ class UsersController extends AUserDataOCSController // Apply remaining changes — all fully validated, setters won't throw if ($displayName !== null) { - $targetUser->setDisplayName($displayName); + // OC\User\User::setDisplayName() rejects empty strings (!empty check), + // so "clear display name" means "reset to userId" — the default. + $targetUser->setDisplayName($displayName !== '' ? $displayName : $userId); } if ($email !== null) { diff --git a/apps/provisioning_api/tests/Controller/UsersControllerTest.php b/apps/provisioning_api/tests/Controller/UsersControllerTest.php index ce4b77d22e1..e4eec967c99 100644 --- a/apps/provisioning_api/tests/Controller/UsersControllerTest.php +++ b/apps/provisioning_api/tests/Controller/UsersControllerTest.php @@ -2852,6 +2852,29 @@ class UsersControllerTest extends TestCase { $this->assertArrayHasKey('language', $result->getData()['errors']); } + public function testEditUserMultiFieldClearDisplayNameResetsToUserId(): void { + $currentUser = $this->createMock(IUser::class); + $currentUser->method('getUID')->willReturn('admin'); + $this->userSession->method('getUser')->willReturn($currentUser); + + $targetUser = $this->createMock(IUser::class); + $targetUser->method('getUID')->willReturn('targetuser'); + $targetUser->method('canChangeDisplayName')->willReturn(true); + $targetUser->method('getBackend')->willReturn($this->createMock(UserInterface::class)); + $this->userManager->method('get')->with('targetuser')->willReturn($targetUser); + + $this->groupManager->method('isAdmin')->with('admin')->willReturn(true); + $this->groupManager->method('isDelegatedAdmin')->willReturn(false); + $subAdmin = $this->createMock(ISubAdmin::class); + $this->groupManager->method('getSubAdmin')->willReturn($subAdmin); + + // Clearing display name (empty string) should reset to userId + $targetUser->expects($this->once())->method('setDisplayName')->with('targetuser')->willReturn(true); + + $result = $this->api->editUserMultiField('targetuser', displayName: ''); + $this->assertSame(Http::STATUS_OK, $result->getStatus()); + } + public function testDeleteUserNotExistingUser(): void { $this->expectException(OCSException::class); diff --git a/apps/settings/src/components/Users/userFormUtils.spec.ts b/apps/settings/src/components/Users/userFormUtils.spec.ts index adf1cb3da3f..01482e30e71 100644 --- a/apps/settings/src/components/Users/userFormUtils.spec.ts +++ b/apps/settings/src/components/Users/userFormUtils.spec.ts @@ -153,6 +153,18 @@ describe('diffPayload', () => { expect(diffPayload(initial, current)).toEqual({ displayName: 'Robert' }) }) + it('detects displayName cleared to empty string', () => { + const initial = makeFormData({ displayName: 'Bob' }) + const current = makeFormData({ displayName: '' }) + expect(diffPayload(initial, current)).toEqual({ displayName: '' }) + }) + + it('detects email cleared to empty string', () => { + const initial = makeFormData({ email: 'bob@example.com' }) + const current = makeFormData({ email: '' }) + expect(diffPayload(initial, current)).toEqual({ email: '' }) + }) + it('always includes password when non-empty', () => { const initial = makeFormData() const current = makeFormData({ password: 'secret123' })