From 50e2cb6193a2dbb36d88fafd34f071167d4b90a3 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Wed, 18 Mar 2026 10:49:57 +0100 Subject: [PATCH 1/3] fix(LDAP): use displayname from DB, before reaching out to LDAP As we do it with other information of the user, we now use the known value of a users displayname, and leave the updating to the background job. This improves performance of user facing actions where the display name is required and reduces queries to the LDAP server that are typically more expensive. Signed-off-by: Arthur Schiwon --- apps/user_ldap/lib/Access.php | 20 ++++------ apps/user_ldap/lib/User/User.php | 12 +++--- apps/user_ldap/lib/User_LDAP.php | 64 +++++++++++++++++++++----------- 3 files changed, 56 insertions(+), 40 deletions(-) diff --git a/apps/user_ldap/lib/Access.php b/apps/user_ldap/lib/Access.php index f97396c5a9a..fd86ee0656d 100644 --- a/apps/user_ldap/lib/Access.php +++ b/apps/user_ldap/lib/Access.php @@ -704,7 +704,7 @@ class Access extends LDAPUtility { continue; } $sndName = $ldapObject[$sndAttribute][0] ?? ''; - $this->cacheUserDisplayName($ncName, $nameByLDAP, $sndName); + $this->applyUserDisplayName($ncName, $nameByLDAP, $sndName); } elseif ($nameByLDAP !== null) { $this->cacheGroupDisplayName($ncName, $nameByLDAP); } @@ -752,20 +752,16 @@ class Access extends LDAPUtility { $this->connection->writeToCache('groupExists' . $gid, true); } - /** - * caches the user display name - * - * @param string $ocName the internal Nextcloud username - * @param string $displayName the display name - * @param string $displayName2 the second display name - * @throws \Exception - */ - public function cacheUserDisplayName(string $ocName, string $displayName, string $displayName2 = ''): void { - $user = $this->userManager->get($ocName); + public function applyUserDisplayName(string $uid, string $displayName, string $displayName2 = ''): void { + $user = $this->userManager->get($uid); if ($user === null) { return; } - $displayName = $user->composeAndStoreDisplayName($displayName, $displayName2); + $composedDisplayName = $user->composeAndStoreDisplayName($displayName, $displayName2); + $this->cacheUserDisplayName($uid, $composedDisplayName); + } + + public function cacheUserDisplayName(string $ocName, string $displayName): void { $cacheKeyTrunk = 'getDisplayName'; $this->connection->writeToCache($cacheKeyTrunk . $ocName, $displayName); } diff --git a/apps/user_ldap/lib/User/User.php b/apps/user_ldap/lib/User/User.php index 207051c1e64..2fcd754b467 100644 --- a/apps/user_ldap/lib/User/User.php +++ b/apps/user_ldap/lib/User/User.php @@ -113,12 +113,8 @@ class User { $displayName2 = (string)$ldapEntry[$attr][0]; } if ($displayName !== '') { - $this->composeAndStoreDisplayName($displayName, $displayName2); - $this->access->cacheUserDisplayName( - $this->getUsername(), - $displayName, - $displayName2 - ); + $composedDisplayName = $this->composeAndStoreDisplayName($displayName, $displayName2); + $this->access->cacheUserDisplayName($this->getUsername(), $composedDisplayName); } unset($attr); @@ -451,6 +447,10 @@ class User { return $displayName; } + public function fetchStoredDisplayName(): string { + return $this->config->getUserValue($this->uid, 'user_ldap', 'displayName', ''); + } + /** * Stores the LDAP Username in the Database */ diff --git a/apps/user_ldap/lib/User_LDAP.php b/apps/user_ldap/lib/User_LDAP.php index 6a648b2624c..5f2300783bf 100644 --- a/apps/user_ldap/lib/User_LDAP.php +++ b/apps/user_ldap/lib/User_LDAP.php @@ -409,26 +409,21 @@ class User_LDAP extends BackendUtility implements IUserBackend, UserInterface, I return $path; } - /** - * get display name of the user - * @param string $uid user ID of the user - * @return string|false display name - */ - public function getDisplayName($uid) { - if ($this->userPluginManager->implementsActions(Backend::GET_DISPLAYNAME)) { - return $this->userPluginManager->getDisplayName($uid); + private function getDisplayNameFromDatabase(string $uid): ?string { + $user = $this->access->userManager->get($uid); + if ($user instanceof User) { + $displayName = $user->fetchStoredDisplayName(); + if ($displayName !== '') { + return $displayName; + } } - - if (!$this->userExists($uid)) { - return false; + if ($user instanceof OfflineUser) { + return $user->getDisplayName(); } + return null; + } - $cacheKey = 'getDisplayName' . $uid; - if (!is_null($displayName = $this->access->connection->getFromCache($cacheKey))) { - return $displayName; - } - - //Check whether the display name is configured to have a 2nd feature + private function getDisplayNameFromLdap(string $uid): string { $additionalAttribute = $this->access->connection->ldapUserDisplayName2; $displayName2 = ''; if ($additionalAttribute !== '') { @@ -450,16 +445,40 @@ class User_LDAP extends BackendUtility implements IUserBackend, UserInterface, I $user = $this->access->userManager->get($uid); if ($user instanceof User) { - $displayName = $user->composeAndStoreDisplayName($displayName, (string)$displayName2); - $this->access->connection->writeToCache($cacheKey, $displayName); + return $user->composeAndStoreDisplayName($displayName, (string)$displayName2); } if ($user instanceof OfflineUser) { - $displayName = $user->getDisplayName(); + return $user->getDisplayName(); } + } + + return ''; + } + + public function getDisplayName($uid): string { + if ($this->userPluginManager->implementsActions(Backend::GET_DISPLAYNAME)) { + return $this->userPluginManager->getDisplayName($uid); + } + + if (!$this->userExists($uid)) { + return ''; + } + + $cacheKey = 'getDisplayName' . $uid; + if (!is_null($displayName = $this->access->connection->getFromCache($cacheKey))) { return $displayName; } - return null; + if ($displayName = $this->getDisplayNameFromDatabase($uid)) { + $this->access->connection->writeToCache($cacheKey, $displayName); + return $displayName; + } + + if ($displayName = $this->getDisplayNameFromLdap($uid)) { + $this->access->connection->writeToCache($cacheKey, $displayName); + } + + return $displayName; } /** @@ -483,7 +502,8 @@ class User_LDAP extends BackendUtility implements IUserBackend, UserInterface, I * @param string $search * @param int|null $limit * @param int|null $offset - * @return array an array of all displayNames (value) and the corresponding uids (key) + * @return array an array of all displayNames (value) and the corresponding + * uids (key) */ public function getDisplayNames($search = '', $limit = null, $offset = null) { $cacheKey = 'getDisplayNames-' . $search . '-' . $limit . '-' . $offset; From a6aee62940f0c0a5fac32f27804eb3527d55a7f0 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Wed, 18 Mar 2026 10:55:22 +0100 Subject: [PATCH 2/3] fix(LDAP): do not use count() inside a loop Signed-off-by: Arthur Schiwon --- apps/user_ldap/lib/User/User.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/user_ldap/lib/User/User.php b/apps/user_ldap/lib/User/User.php index 2fcd754b467..9a3a254fed8 100644 --- a/apps/user_ldap/lib/User/User.php +++ b/apps/user_ldap/lib/User/User.php @@ -124,7 +124,8 @@ class User { $attr = strtolower($this->connection->ldapEmailAttribute); if (isset($ldapEntry[$attr])) { $mailValue = 0; - for ($x = 0; $x < count($ldapEntry[$attr]); $x++) { + $emailValues = count($ldapEntry[$attr]); + for ($x = 0; $x < $emailValues; $x++) { if (filter_var($ldapEntry[$attr][$x], FILTER_VALIDATE_EMAIL)) { $mailValue = $x; break; From a7159c23f978b37259f7f1c656179361c5036771 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Wed, 18 Mar 2026 11:09:31 +0100 Subject: [PATCH 3/3] ci: update psalm baseline Signed-off-by: Arthur Schiwon --- build/psalm-baseline.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/build/psalm-baseline.xml b/build/psalm-baseline.xml index cf6dc91d7c0..63288887cd3 100644 --- a/build/psalm-baseline.xml +++ b/build/psalm-baseline.xml @@ -1101,16 +1101,10 @@ - - - - - - 0)]]>