From 68259f9c7e7de55eabd19301e0141229517114ae Mon Sep 17 00:00:00 2001 From: Anna Larch Date: Wed, 3 Jun 2026 09:01:39 +0200 Subject: [PATCH] fix(user_ldap): allow editing profile fields not managed by LDAP canEditProperty() was returning true (editable) when an LDAP attribute was configured for a field, and false when no attribute was configured. This is inverted: a field with an LDAP attribute mapping is owned by LDAP and should not be user-editable, while a field with no mapping is not sourced from LDAP and the user should be free to set it themselves. Fixes profile fields being uneditable for all LDAP users whose admin has not configured attribute mappings for those fields. Assisted-by: ClaudeCode:claude-sonnet-4-6 Signed-off-by: Anna Larch --- apps/user_ldap/lib/User_LDAP.php | 22 ++++++++--------- apps/user_ldap/tests/User_LDAPTest.php | 33 ++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/apps/user_ldap/lib/User_LDAP.php b/apps/user_ldap/lib/User_LDAP.php index 9698882bdbd67..7f4f6e6c24c8d 100644 --- a/apps/user_ldap/lib/User_LDAP.php +++ b/apps/user_ldap/lib/User_LDAP.php @@ -690,17 +690,17 @@ public function canEditProperty(string $uid, string $property): bool { return match($property) { // Display name is always set by LDAP IAccountManager::PROPERTY_DISPLAYNAME => false, - IAccountManager::PROPERTY_EMAIL => ((string)$this->access->connection->ldapEmailAttribute !== ''), - IAccountManager::PROPERTY_PHONE => ((string)$this->access->connection->ldapAttributePhone !== ''), - IAccountManager::PROPERTY_WEBSITE => ((string)$this->access->connection->ldapAttributeWebsite !== ''), - IAccountManager::PROPERTY_ADDRESS => ((string)$this->access->connection->ldapAttributeAddress !== ''), - IAccountManager::PROPERTY_FEDIVERSE => ((string)$this->access->connection->ldapAttributeFediverse !== ''), - IAccountManager::PROPERTY_ORGANISATION => ((string)$this->access->connection->ldapAttributeOrganisation !== ''), - IAccountManager::PROPERTY_ROLE => ((string)$this->access->connection->ldapAttributeRole !== ''), - IAccountManager::PROPERTY_HEADLINE => ((string)$this->access->connection->ldapAttributeHeadline !== ''), - IAccountManager::PROPERTY_BIOGRAPHY => ((string)$this->access->connection->ldapAttributeBiography !== ''), - IAccountManager::PROPERTY_BIRTHDATE => ((string)$this->access->connection->ldapAttributeBirthDate !== ''), - IAccountManager::PROPERTY_PRONOUNS => ((string)$this->access->connection->ldapAttributePronouns !== ''), + IAccountManager::PROPERTY_EMAIL => ((string)$this->access->connection->ldapEmailAttribute === ''), + IAccountManager::PROPERTY_PHONE => ((string)$this->access->connection->ldapAttributePhone === ''), + IAccountManager::PROPERTY_WEBSITE => ((string)$this->access->connection->ldapAttributeWebsite === ''), + IAccountManager::PROPERTY_ADDRESS => ((string)$this->access->connection->ldapAttributeAddress === ''), + IAccountManager::PROPERTY_FEDIVERSE => ((string)$this->access->connection->ldapAttributeFediverse === ''), + IAccountManager::PROPERTY_ORGANISATION => ((string)$this->access->connection->ldapAttributeOrganisation === ''), + IAccountManager::PROPERTY_ROLE => ((string)$this->access->connection->ldapAttributeRole === ''), + IAccountManager::PROPERTY_HEADLINE => ((string)$this->access->connection->ldapAttributeHeadline === ''), + IAccountManager::PROPERTY_BIOGRAPHY => ((string)$this->access->connection->ldapAttributeBiography === ''), + IAccountManager::PROPERTY_BIRTHDATE => ((string)$this->access->connection->ldapAttributeBirthDate === ''), + IAccountManager::PROPERTY_PRONOUNS => ((string)$this->access->connection->ldapAttributePronouns === ''), default => true, }; } diff --git a/apps/user_ldap/tests/User_LDAPTest.php b/apps/user_ldap/tests/User_LDAPTest.php index 3ba73087119f4..325f109ee6c50 100644 --- a/apps/user_ldap/tests/User_LDAPTest.php +++ b/apps/user_ldap/tests/User_LDAPTest.php @@ -1460,4 +1460,37 @@ public function testImplementsAction(string $configurable, string|int $value, in $this->assertSame($expected, $this->backend->implementsActions($actionCode)); } + + public static function canEditPropertyProvider(): array { + return [ + // Display name is always managed by LDAP + [\OCP\Accounts\IAccountManager::PROPERTY_DISPLAYNAME, '', false], + [\OCP\Accounts\IAccountManager::PROPERTY_DISPLAYNAME, 'cn', false], + // Fields with no LDAP attribute configured are user-editable + [\OCP\Accounts\IAccountManager::PROPERTY_EMAIL, '', true], + [\OCP\Accounts\IAccountManager::PROPERTY_PHONE, '', true], + [\OCP\Accounts\IAccountManager::PROPERTY_WEBSITE, '', true], + [\OCP\Accounts\IAccountManager::PROPERTY_ADDRESS, '', true], + [\OCP\Accounts\IAccountManager::PROPERTY_FEDIVERSE, '', true], + [\OCP\Accounts\IAccountManager::PROPERTY_ORGANISATION, '', true], + [\OCP\Accounts\IAccountManager::PROPERTY_ROLE, '', true], + [\OCP\Accounts\IAccountManager::PROPERTY_HEADLINE, '', true], + [\OCP\Accounts\IAccountManager::PROPERTY_BIOGRAPHY, '', true], + [\OCP\Accounts\IAccountManager::PROPERTY_BIRTHDATE, '', true], + [\OCP\Accounts\IAccountManager::PROPERTY_PRONOUNS, '', true], + // Fields with an LDAP attribute configured are managed by LDAP, not user-editable + [\OCP\Accounts\IAccountManager::PROPERTY_EMAIL, 'mail', false], + [\OCP\Accounts\IAccountManager::PROPERTY_PHONE, 'telephoneNumber', false], + [\OCP\Accounts\IAccountManager::PROPERTY_WEBSITE, 'labeledURI', false], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider(methodName: 'canEditPropertyProvider')] + public function testCanEditProperty(string $property, string $ldapAttributeValue, bool $expected): void { + $this->connection->expects($this->any()) + ->method('__get') + ->willReturn($ldapAttributeValue); + + $this->assertSame($expected, $this->backend->canEditProperty('uid', $property)); + } }