diff --git a/apps/user_ldap/lib/LDAPProvider.php b/apps/user_ldap/lib/LDAPProvider.php index fad0da7f206d1..c4e89a5cc929b 100644 --- a/apps/user_ldap/lib/LDAPProvider.php +++ b/apps/user_ldap/lib/LDAPProvider.php @@ -9,6 +9,7 @@ use OCA\User_LDAP\User\DeletedUsersIndex; use OCP\IServerContainer; +use OCP\IUser; use OCP\LDAP\IDeletionFlagSupport; use OCP\LDAP\ILDAPProvider; use Psr\Log\LoggerInterface; @@ -327,4 +328,8 @@ public function getMultiValueUserAttribute(string $uid, string $attribute): arra $connection->writeToCache($key, $values); return $values; } + + public function findOneUser(string $filter, string $attribute, string $searchTerm): ?IUser { + return $this->userBackend->getUserFromCustomAttribute($filter, $attribute, $searchTerm); + } } diff --git a/apps/user_ldap/lib/User_LDAP.php b/apps/user_ldap/lib/User_LDAP.php index 708aeb037fd2b..07fcab965c42d 100644 --- a/apps/user_ldap/lib/User_LDAP.php +++ b/apps/user_ldap/lib/User_LDAP.php @@ -14,7 +14,10 @@ use OCA\User_LDAP\User\DeletedUsersIndex; use OCA\User_LDAP\User\OfflineUser; use OCA\User_LDAP\User\User; +use OCP\IUser; use OCP\IUserBackend; +use OCP\IUserManager; +use OCP\LDAP\MultipleUsersReturnedException; use OCP\Notification\IManager as INotificationManager; use OCP\User\Backend\ICountMappedUsersBackend; use OCP\User\Backend\ILimitAwareCountUsersBackend; @@ -29,6 +32,7 @@ public function __construct( protected UserPluginManager $userPluginManager, protected LoggerInterface $logger, protected DeletedUsersIndex $deletedUsersIndex, + protected IUserManager $userManager, ) { parent::__construct($access); } @@ -643,4 +647,38 @@ public function setUserEnabled(string $uid, bool $enabled, callable $queryDataba public function getDisabledUserList(?int $limit = null, int $offset = 0, string $search = ''): array { throw new \Exception('This is implemented directly in User_Proxy'); } + + /** + * Fetches one user from LDAP based on a filter or a custom attribute and search term. + * + * If no custom filter is provided or the filter is empty, it creates a simple equality filter with the given attribute. + * If a custom filter is provided, it uses that filter directly and the attribute and search term are ignored. + * + * @throws MultipleUsersReturnedException if multiple users have been found (search query should not allow this) + * + * @param string $filter The LDAP filter to use. If null or empty string, a default filter is constructed. + * @param string $attribute The LDAP attribute name to search against (e.g., 'mail', 'cn', 'uid'). + * @param string $searchTerm The search term to match against the attribute. Will be escaped for LDAP filter safety. + * + * @return IUser |null Returns an IUser if found in LDAP using the configured LDAP filter, or null if no user is found. + */ + public function getUserFromCustomAttribute(string $filter, string $attribute, string $searchTerm): ?IUser { + if ($filter === null || $filter === '') { + $searchTerm = $this->access->escapeFilterPart($searchTerm); + $filter = "($attribute=$searchTerm)"; + } + $records = $this->access->searchUsers($filter, ['dn']); + if (count($records) === 1) { + $ldapUser = $this->access->userManager->get($records[0]['dn'][0]); + $user = $this->userManager->get($ldapUser->getUsername()); + return $user; + } elseif (count($records) > 1) { + $this->logger->error( + 'Multiple users found for filter: ' . $filter, + ['app' => 'user_ldap'] + ); + throw new MultipleUsersReturnedException(); + } + return null; + } } diff --git a/apps/user_ldap/lib/User_Proxy.php b/apps/user_ldap/lib/User_Proxy.php index 0d41f495ce9ba..c85b6da78be08 100644 --- a/apps/user_ldap/lib/User_Proxy.php +++ b/apps/user_ldap/lib/User_Proxy.php @@ -10,13 +10,17 @@ use OCA\User_LDAP\User\DeletedUsersIndex; use OCA\User_LDAP\User\OfflineUser; use OCA\User_LDAP\User\User; +use OCP\IUser; use OCP\IUserBackend; +use OCP\IUserManager; use OCP\Notification\IManager as INotificationManager; use OCP\User\Backend\ICountMappedUsersBackend; use OCP\User\Backend\ILimitAwareCountUsersBackend; use OCP\User\Backend\IProvideEnabledStateBackend; use OCP\UserInterface; use Psr\Log\LoggerInterface; +use OCP\LDAP\MultipleUsersReturnedException; + /** * @template-extends Proxy @@ -30,6 +34,7 @@ public function __construct( private UserPluginManager $userPluginManager, private LoggerInterface $logger, private DeletedUsersIndex $deletedUsersIndex, + private IUserManager $userManager, ) { parent::__construct($helper, $ldap, $accessFactory); } @@ -41,6 +46,7 @@ protected function newInstance(string $configPrefix): User_LDAP { $this->userPluginManager, $this->logger, $this->deletedUsersIndex, + $this->userManager, ); } @@ -431,4 +437,18 @@ public function getDisabledUserList(?int $limit = null, int $offset = 0, string ) ); } + + public function getUserFromCustomAttribute(string $filter, string $attribute, string $searchTerm): ?IUser { + $this->setup(); + $user = null; + foreach ($this->backends as $backend) { + $fetchUser = $backend->getUserFromCustomAttribute($filter, $attribute, $searchTerm); + // if we found a different user, no need to continue + if ($user !== null && $fetchUser !== null && $fetchUser->getUID() !== $user->getUID()) { + throw new MultipleUsersReturnedException('Multiple users found for custom attribute search'); + } + $user = $fetchUser; // may be null + } + return $user; + } } diff --git a/lib/public/LDAP/ILDAPProvider.php b/lib/public/LDAP/ILDAPProvider.php index 7e1f27eb36842..e72eb1441f427 100644 --- a/lib/public/LDAP/ILDAPProvider.php +++ b/lib/public/LDAP/ILDAPProvider.php @@ -6,6 +6,8 @@ */ namespace OCP\LDAP; +use OCP\IUser; + /** * Interface ILDAPProvider * @@ -151,4 +153,13 @@ public function getUserAttribute(string $uid, string $attribute): ?string; * @since 22.0.0 */ public function getMultiValueUserAttribute(string $uid, string $attribute): array; + + /** + * Search for a single user in ldap + * + * @return IUser|null Returns a IUser if found in ldap using the configured ldap filter + * @throws MultipleUsersReturnedException if multiple users has been found (search query should not allow this) + * @since 33.0.0 + */ + public function findOneUser(string $filter, string $attribute, string $searchTerm): ?IUser; } diff --git a/lib/public/LDAP/MultipleUsersReturnedException.php b/lib/public/LDAP/MultipleUsersReturnedException.php new file mode 100644 index 0000000000000..38a469579afed --- /dev/null +++ b/lib/public/LDAP/MultipleUsersReturnedException.php @@ -0,0 +1,14 @@ +