diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php index dabf046fa7ea4..25b6fcefd976d 100644 --- a/apps/dav/composer/composer/autoload_static.php +++ b/apps/dav/composer/composer/autoload_static.php @@ -7,14 +7,14 @@ class ComposerStaticInitDAV { public static $prefixLengthsPsr4 = array ( - 'O' => + 'O' => array ( 'OCA\\DAV\\' => 8, ), ); public static $prefixDirsPsr4 = array ( - 'OCA\\DAV\\' => + 'OCA\\DAV\\' => array ( 0 => __DIR__ . '/..' . '/../lib', ), diff --git a/apps/user_ldap/lib/IUserLDAP.php b/apps/user_ldap/lib/IUserLDAP.php index 6e730fbe94158..7d8e0fd5f53a4 100644 --- a/apps/user_ldap/lib/IUserLDAP.php +++ b/apps/user_ldap/lib/IUserLDAP.php @@ -8,6 +8,8 @@ */ namespace OCA\User_LDAP; +use OCP\LDAP\Exceptions\MultipleUsersReturnedException; + interface IUserLDAP { //Functions used by LDAPProvider @@ -32,4 +34,14 @@ public function getNewLDAPConnection($uid); * @return string|false with the username */ public function dn2UserName($dn); + + /** + * Fetches one user from LDAP based on a filter or a custom attribute and search term. + * + * @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 string|null Returns the username if found in LDAP using the configured LDAP filter, or null if no user is found. + * @throws MultipleUsersReturnedException if multiple users have been found (search query should not allow this) + */ + public function getUserFromCustomAttribute(string $attribute, string $searchTerm): ?string; } diff --git a/apps/user_ldap/lib/LDAPProvider.php b/apps/user_ldap/lib/LDAPProvider.php index a99c1ccf50656..34d3b4caa703d 100644 --- a/apps/user_ldap/lib/LDAPProvider.php +++ b/apps/user_ldap/lib/LDAPProvider.php @@ -12,6 +12,7 @@ use OCA\User_LDAP\User\DeletedUsersIndex; use OCP\GroupInterface; use OCP\IGroupManager; +use OCP\IUser; use OCP\IUserManager; use OCP\LDAP\IDeletionFlagSupport; use OCP\LDAP\ILDAPProvider; @@ -29,7 +30,7 @@ class LDAPProvider implements ILDAPProvider, IDeletionFlagSupport { * @throws \Exception if user_ldap app was not enabled */ public function __construct( - IUserManager $userManager, + private IUserManager $userManager, IGroupManager $groupManager, private Helper $helper, private DeletedUsersIndex $deletedUsersIndex, @@ -37,7 +38,7 @@ public function __construct( ) { $userBackendFound = false; $groupBackendFound = false; - foreach ($userManager->getBackends() as $backend) { + foreach ($this->userManager->getBackends() as $backend) { $this->logger->debug('instance ' . get_class($backend) . ' user backend.', ['app' => 'user_ldap']); if ($backend instanceof IUserLDAP) { $this->userBackend = $backend; @@ -320,4 +321,13 @@ public function getMultiValueUserAttribute(string $uid, string $attribute): arra $connection->writeToCache($key, $values); return $values; } + + #[\Override] + public function findOneUserByAttributeValue(string $attribute, string $searchTerm): ?IUser { + $userId = $this->userBackend->getUserFromCustomAttribute($attribute, $searchTerm); + if (!$userId) { + return null; + } + return $this->userManager->get($userId); + } } diff --git a/apps/user_ldap/lib/User_LDAP.php b/apps/user_ldap/lib/User_LDAP.php index eefe0a133355d..6a4ba3bf6422f 100644 --- a/apps/user_ldap/lib/User_LDAP.php +++ b/apps/user_ldap/lib/User_LDAP.php @@ -16,21 +16,23 @@ use OCA\User_LDAP\User\User; use OCP\Accounts\IAccountManager; use OCP\IUserBackend; +use OCP\LDAP\Exceptions\MultipleUsersReturnedException; use OCP\Notification\IManager as INotificationManager; use OCP\User\Backend\ICountMappedUsersBackend; use OCP\User\Backend\ILimitAwareCountUsersBackend; use OCP\User\Backend\IPropertyPermissionBackend; use OCP\User\Backend\IProvideEnabledStateBackend; use OCP\UserInterface; +use Override; use Psr\Log\LoggerInterface; class User_LDAP extends BackendUtility implements IUserBackend, UserInterface, IUserLDAP, ILimitAwareCountUsersBackend, ICountMappedUsersBackend, IProvideEnabledStateBackend, IPropertyPermissionBackend { public function __construct( Access $access, - protected INotificationManager $notificationManager, - protected UserPluginManager $userPluginManager, - protected LoggerInterface $logger, - protected DeletedUsersIndex $deletedUsersIndex, + protected readonly INotificationManager $notificationManager, + protected readonly UserPluginManager $userPluginManager, + protected readonly LoggerInterface $logger, + protected readonly DeletedUsersIndex $deletedUsersIndex, ) { parent::__construct($access); } @@ -701,4 +703,25 @@ public function canEditProperty(string $uid, string $property): bool { default => true, }; } + + #[Override] + public function getUserFromCustomAttribute(string $attribute, string $searchTerm): ?string { + $searchTerm = $this->access->escapeFilterPart($searchTerm); + $attribute = $this->access->escapeFilterPart($attribute); + + $filter = "($attribute=$searchTerm)"; + + $records = $this->access->searchUsers($filter, ['dn']); + $this->logger->error($filter); + if (count($records) === 1) { + return $this->access->dn2username($records[0]['dn'][0]) ?: null; + } 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 4119885a1d2a1..9fec6a989a211 100644 --- a/apps/user_ldap/lib/User_Proxy.php +++ b/apps/user_ldap/lib/User_Proxy.php @@ -11,6 +11,7 @@ use OCA\User_LDAP\User\OfflineUser; use OCA\User_LDAP\User\User; use OCP\IUserBackend; +use OCP\LDAP\Exceptions\MultipleUsersReturnedException; use OCP\Notification\IManager as INotificationManager; use OCP\User\Backend\ICountMappedUsersBackend; use OCP\User\Backend\IGetDisplayNameBackend; @@ -18,6 +19,7 @@ use OCP\User\Backend\IPropertyPermissionBackend; use OCP\User\Backend\IProvideEnabledStateBackend; use OCP\UserInterface; +use Override; use Psr\Log\LoggerInterface; /** @@ -25,13 +27,13 @@ */ class User_Proxy extends Proxy implements IUserBackend, UserInterface, IUserLDAP, ILimitAwareCountUsersBackend, ICountMappedUsersBackend, IProvideEnabledStateBackend, IGetDisplayNameBackend, IPropertyPermissionBackend { public function __construct( - private Helper $helper, + Helper $helper, ILDAPWrapper $ldap, AccessFactory $accessFactory, - private INotificationManager $notificationManager, - private UserPluginManager $userPluginManager, - private LoggerInterface $logger, - private DeletedUsersIndex $deletedUsersIndex, + private readonly INotificationManager $notificationManager, + private readonly UserPluginManager $userPluginManager, + private readonly LoggerInterface $logger, + private readonly DeletedUsersIndex $deletedUsersIndex, ) { parent::__construct($helper, $ldap, $accessFactory); } @@ -458,4 +460,19 @@ public function getDisabledUserList(?int $limit = null, int $offset = 0, string public function canEditProperty(string $uid, string $property): bool { return $this->handleRequest($uid, 'canEditProperty', [$uid, $property]); } + + #[Override] + public function getUserFromCustomAttribute(string $attribute, string $searchTerm): ?string { + $this->setup(); + $user = null; + foreach ($this->backends as $backend) { + $fetchUser = $backend->getUserFromCustomAttribute($attribute, $searchTerm); + // if we found a different user, no need to continue + if ($user !== null && $fetchUser !== null && $fetchUser !== $user) { + throw new MultipleUsersReturnedException('Multiple users found for custom attribute search'); + } + $user = $fetchUser; // may be null + } + return $user; + } } diff --git a/lib/composer/composer/LICENSE b/lib/composer/composer/LICENSE index 62ecfd8d0046b..f27399a042d95 100644 --- a/lib/composer/composer/LICENSE +++ b/lib/composer/composer/LICENSE @@ -1,3 +1,4 @@ + Copyright (c) Nils Adermann, Jordi Boggiano Permission is hereby granted, free of charge, to any person obtaining a copy @@ -17,3 +18,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 143304b1b7b72..d367649768b3a 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -659,6 +659,7 @@ 'OCP\\Install\\Events\\InstallationCompletedEvent' => $baseDir . '/lib/public/Install/Events/InstallationCompletedEvent.php', 'OCP\\L10N\\IFactory' => $baseDir . '/lib/public/L10N/IFactory.php', 'OCP\\L10N\\ILanguageIterator' => $baseDir . '/lib/public/L10N/ILanguageIterator.php', + 'OCP\\LDAP\\Exceptions\\MultipleUsersReturnedException' => $baseDir . '/lib/public/LDAP/Exceptions/MultipleUsersReturnedException.php', 'OCP\\LDAP\\IDeletionFlagSupport' => $baseDir . '/lib/public/LDAP/IDeletionFlagSupport.php', 'OCP\\LDAP\\ILDAPProvider' => $baseDir . '/lib/public/LDAP/ILDAPProvider.php', 'OCP\\LDAP\\ILDAPProviderFactory' => $baseDir . '/lib/public/LDAP/ILDAPProviderFactory.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 4342cc3063c9a..1b677846d1507 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -700,6 +700,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\Install\\Events\\InstallationCompletedEvent' => __DIR__ . '/../../..' . '/lib/public/Install/Events/InstallationCompletedEvent.php', 'OCP\\L10N\\IFactory' => __DIR__ . '/../../..' . '/lib/public/L10N/IFactory.php', 'OCP\\L10N\\ILanguageIterator' => __DIR__ . '/../../..' . '/lib/public/L10N/ILanguageIterator.php', + 'OCP\\LDAP\\Exceptions\\MultipleUsersReturnedException' => __DIR__ . '/../../..' . '/lib/public/LDAP/Exceptions/MultipleUsersReturnedException.php', 'OCP\\LDAP\\IDeletionFlagSupport' => __DIR__ . '/../../..' . '/lib/public/LDAP/IDeletionFlagSupport.php', 'OCP\\LDAP\\ILDAPProvider' => __DIR__ . '/../../..' . '/lib/public/LDAP/ILDAPProvider.php', 'OCP\\LDAP\\ILDAPProviderFactory' => __DIR__ . '/../../..' . '/lib/public/LDAP/ILDAPProviderFactory.php', diff --git a/lib/public/LDAP/Exceptions/MultipleUsersReturnedException.php b/lib/public/LDAP/Exceptions/MultipleUsersReturnedException.php new file mode 100644 index 0000000000000..35fca39f5eb3c --- /dev/null +++ b/lib/public/LDAP/Exceptions/MultipleUsersReturnedException.php @@ -0,0 +1,20 @@ +