diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php index dabf046fa7e..25b6fcefd97 100644 --- a/apps/dav/composer/composer/autoload_static.php +++ b/apps/dav/composer/composer/autoload_static.php @@ -7,14 +7,14 @@ namespace Composer\Autoload; 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 6e730fbe941..7d8e0fd5f53 100644 --- a/apps/user_ldap/lib/IUserLDAP.php +++ b/apps/user_ldap/lib/IUserLDAP.php @@ -8,6 +8,8 @@ declare(strict_types=1); */ namespace OCA\User_LDAP; +use OCP\LDAP\Exceptions\MultipleUsersReturnedException; + interface IUserLDAP { //Functions used by LDAPProvider @@ -32,4 +34,14 @@ interface IUserLDAP { * @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 a99c1ccf506..34d3b4caa70 100644 --- a/apps/user_ldap/lib/LDAPProvider.php +++ b/apps/user_ldap/lib/LDAPProvider.php @@ -12,6 +12,7 @@ use LDAP\Connection; 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 @@ class LDAPProvider implements ILDAPProvider, IDeletionFlagSupport { ) { $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 @@ class LDAPProvider implements ILDAPProvider, IDeletionFlagSupport { $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 eefe0a13335..6a4ba3bf642 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\OfflineUser; 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 @@ class User_LDAP extends BackendUtility implements IUserBackend, UserInterface, I 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 4119885a1d2..9fec6a989a2 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\DeletedUsersIndex; 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\ILimitAwareCountUsersBackend; use OCP\User\Backend\IPropertyPermissionBackend; use OCP\User\Backend\IProvideEnabledStateBackend; use OCP\UserInterface; +use Override; use Psr\Log\LoggerInterface; /** @@ -25,13 +27,13 @@ use Psr\Log\LoggerInterface; */ 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 @@ class User_Proxy extends Proxy implements IUserBackend, UserInterface, IUserLDAP 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 62ecfd8d004..f27399a042d 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 143304b1b7b..d367649768b 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -659,6 +659,7 @@ return array( '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 4342cc3063c..1b677846d15 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 00000000000..35fca39f5eb --- /dev/null +++ b/lib/public/LDAP/Exceptions/MultipleUsersReturnedException.php @@ -0,0 +1,20 @@ +