diff options
Diffstat (limited to 'apps/user_ldap/lib/User_LDAP.php')
-rw-r--r-- | apps/user_ldap/lib/User_LDAP.php | 251 |
1 files changed, 124 insertions, 127 deletions
diff --git a/apps/user_ldap/lib/User_LDAP.php b/apps/user_ldap/lib/User_LDAP.php index 79f230ae00b..c3f56f5ff9b 100644 --- a/apps/user_ldap/lib/User_LDAP.php +++ b/apps/user_ldap/lib/User_LDAP.php @@ -1,81 +1,40 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Dominik Schmidt <dev@dominik-schmidt.de> - * @author felixboehm <felix@webhippie.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roger Szabo <roger.szabo@web.de> - * @author root <root@localhost.localdomain> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Tom Needham <tom@owncloud.com> - * @author Victor Dubiniuk <dubiniuk@owncloud.com> - * @author Vinicius Cubas Brand <vinicius@eita.org.br> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OCA\User_LDAP; use OC\ServerNotAvailableException; use OC\User\Backend; use OC\User\NoUserException; use OCA\User_LDAP\Exceptions\NotOnLDAP; +use OCA\User_LDAP\User\DeletedUsersIndex; use OCA\User_LDAP\User\OfflineUser; use OCA\User_LDAP\User\User; -use OCP\IConfig; -use OCP\ILogger; -use OCP\IUserSession; +use OCP\IUserBackend; use OCP\Notification\IManager as INotificationManager; -use OCP\Util; - -class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserInterface, IUserLDAP { - /** @var \OCP\IConfig */ - protected $ocConfig; - - /** @var INotificationManager */ - protected $notificationManager; - - /** @var UserPluginManager */ - protected $userPluginManager; - - /** - * @param Access $access - * @param \OCP\IConfig $ocConfig - * @param \OCP\Notification\IManager $notificationManager - * @param IUserSession $userSession - */ - public function __construct(Access $access, IConfig $ocConfig, INotificationManager $notificationManager, IUserSession $userSession, UserPluginManager $userPluginManager) { +use OCP\User\Backend\ICountMappedUsersBackend; +use OCP\User\Backend\ILimitAwareCountUsersBackend; +use OCP\User\Backend\IProvideEnabledStateBackend; +use OCP\UserInterface; +use Psr\Log\LoggerInterface; + +class User_LDAP extends BackendUtility implements IUserBackend, UserInterface, IUserLDAP, ILimitAwareCountUsersBackend, ICountMappedUsersBackend, IProvideEnabledStateBackend { + public function __construct( + Access $access, + protected INotificationManager $notificationManager, + protected UserPluginManager $userPluginManager, + protected LoggerInterface $logger, + protected DeletedUsersIndex $deletedUsersIndex, + ) { parent::__construct($access); - $this->ocConfig = $ocConfig; - $this->notificationManager = $notificationManager; - $this->userPluginManager = $userPluginManager; } /** - * checks whether the user is allowed to change his avatar in Nextcloud + * checks whether the user is allowed to change their avatar in Nextcloud * * @param string $uid the Nextcloud user name * @return boolean either the user can or cannot @@ -108,11 +67,12 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn * @return string|false * @throws \Exception */ - public function loginName2UserName($loginName) { + public function loginName2UserName($loginName, bool $forceLdapRefetch = false) { $cacheKey = 'loginName2UserName-' . $loginName; $username = $this->access->connection->getFromCache($cacheKey); - if ($username !== null) { + $ignoreCache = ($username === false && $forceLdapRefetch); + if ($username !== null && !$ignoreCache) { return $username; } @@ -127,13 +87,16 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn } $username = $user->getUsername(); $this->access->connection->writeToCache($cacheKey, $username); + if ($forceLdapRefetch) { + $user->processAttributes($ldapRecord); + } return $username; } catch (NotOnLDAP $e) { $this->access->connection->writeToCache($cacheKey, false); return false; } } - + /** * returns the username for the given LDAP DN, if available * @@ -156,8 +119,8 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn $attrs = $this->access->userManager->getAttributes(); $users = $this->access->fetchUsersByLoginName($loginName, $attrs); if (count($users) < 1) { - throw new NotOnLDAP('No user available for the given login name on ' . - $this->access->connection->ldapHost . ':' . $this->access->connection->ldapPort); + throw new NotOnLDAP('No user available for the given login name on ' + . $this->access->connection->ldapHost . ':' . $this->access->connection->ldapPort); } return $users[0]; } @@ -170,20 +133,19 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn * @return false|string */ public function checkPassword($uid, $password) { - try { - $ldapRecord = $this->getLDAPUserByLoginName($uid); - } catch (NotOnLDAP $e) { - \OC::$server->getLogger()->logException($e, ['app' => 'user_ldap', 'level' => ILogger::DEBUG]); + $username = $this->loginName2UserName($uid, true); + if ($username === false) { return false; } - $dn = $ldapRecord['dn'][0]; + $dn = $this->access->username2dn($username); $user = $this->access->userManager->get($dn); if (!$user instanceof User) { - Util::writeLog('user_ldap', - 'LDAP Login: Could not get user object for DN ' . $dn . - '. Maybe the LDAP entry has no set display name attribute?', - ILogger::WARN); + $this->logger->warning( + 'LDAP Login: Could not get user object for DN ' . $dn + . '. Maybe the LDAP entry has no set display name attribute?', + ['app' => 'user_ldap'] + ); return false; } if ($user->getUsername() !== false) { @@ -193,7 +155,6 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn } $this->access->cacheUserExists($user->getUsername()); - $user->processAttributes($ldapRecord); $user->markLogin(); return $user->getUsername(); @@ -216,8 +177,8 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn $user = $this->access->userManager->get($uid); if (!$user instanceof User) { - throw new \Exception('LDAP setPassword: Could not get user object for uid ' . $uid . - '. Maybe the LDAP entry has no set display name attribute?'); + throw new \Exception('LDAP setPassword: Could not get user object for uid ' . $uid + . '. Maybe the LDAP entry has no set display name attribute?'); } if ($user->getUsername() !== false && $this->access->setPassword($user->getDN(), $password)) { $ldapDefaultPPolicyDN = $this->access->connection->ldapDefaultPPolicyDN; @@ -247,7 +208,7 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn */ public function getUsers($search = '', $limit = 10, $offset = 0) { $search = $this->access->escapeFilterPart($search, true); - $cachekey = 'getUsers-'.$search.'-'.$limit.'-'.$offset; + $cachekey = 'getUsers-' . $search . '-' . $limit . '-' . $offset; //check if users are cached, if so return $ldap_users = $this->access->connection->getFromCache($cachekey); @@ -266,16 +227,20 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn $this->access->getFilterPartForUserSearch($search) ]); - Util::writeLog('user_ldap', - 'getUsers: Options: search '.$search.' limit '.$limit.' offset '.$offset.' Filter: '.$filter, - ILogger::DEBUG); + $this->logger->debug( + 'getUsers: Options: search ' . $search . ' limit ' . $limit . ' offset ' . $offset . ' Filter: ' . $filter, + ['app' => 'user_ldap'] + ); //do the search and translate results to Nextcloud names $ldap_users = $this->access->fetchListOfUsers( $filter, $this->access->userManager->getAttributes(true), $limit, $offset); $ldap_users = $this->access->nextcloudUserNames($ldap_users); - Util::writeLog('user_ldap', 'getUsers: '.count($ldap_users). ' Users found', ILogger::DEBUG); + $this->logger->debug( + 'getUsers: ' . count($ldap_users) . ' Users found', + ['app' => 'user_ldap'] + ); $this->access->connection->writeToCache($cachekey, $ldap_users); return $ldap_users; @@ -284,13 +249,12 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn /** * checks whether a user is still available on LDAP * - * @param string|\OCA\User_LDAP\User\User $user either the Nextcloud user - * name or an instance of that user - * @return bool + * @param string|User $user either the Nextcloud user + * name or an instance of that user * @throws \Exception * @throws \OC\ServerNotAvailableException */ - public function userExistsOnLDAP($user) { + public function userExistsOnLDAP($user, bool $ignoreCache = false): bool { if (is_string($user)) { $user = $this->access->userManager->get($user); } @@ -299,9 +263,11 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn } $uid = $user instanceof User ? $user->getUsername() : $user->getOCName(); $cacheKey = 'userExistsOnLDAP' . $uid; - $userExists = $this->access->connection->getFromCache($cacheKey); - if (!is_null($userExists)) { - return (bool)$userExists; + if (!$ignoreCache) { + $userExists = $this->access->connection->getFromCache($cacheKey); + if (!is_null($userExists)) { + return (bool)$userExists; + } } $dn = $user->getDN(); @@ -320,8 +286,6 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn return false; } $this->access->getUserMapper()->setDNbyUUID($newDn, $uuid); - $this->access->connection->writeToCache($cacheKey, true); - return true; } catch (ServerNotAvailableException $e) { throw $e; } catch (\Exception $e) { @@ -345,21 +309,22 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn * @throws \Exception when connection could not be established */ public function userExists($uid) { - $userExists = $this->access->connection->getFromCache('userExists'.$uid); + $userExists = $this->access->connection->getFromCache('userExists' . $uid); if (!is_null($userExists)) { return (bool)$userExists; } - //getting dn, if false the user does not exist. If dn, he may be mapped only, requires more checking. - $user = $this->access->userManager->get($uid); + $userExists = $this->access->userManager->exists($uid); - if (is_null($user)) { - Util::writeLog('user_ldap', 'No DN found for '.$uid.' on '. - $this->access->connection->ldapHost, ILogger::DEBUG); - $this->access->connection->writeToCache('userExists'.$uid, false); + if (!$userExists) { + $this->logger->debug( + 'No DN found for ' . $uid . ' on ' . $this->access->connection->ldapHost, + ['app' => 'user_ldap'] + ); + $this->access->connection->writeToCache('userExists' . $uid, false); return false; } - $this->access->connection->writeToCache('userExists'.$uid, true); + $this->access->connection->writeToCache('userExists' . $uid, true); return true; } @@ -377,14 +342,29 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn } } - $marked = $this->ocConfig->getUserValue($uid, 'user_ldap', 'isDeleted', 0); - if ((int)$marked === 0) { - \OC::$server->getLogger()->notice( - 'User '.$uid . ' is not marked as deleted, not cleaning up.', - ['app' => 'user_ldap']); - return false; + $marked = $this->deletedUsersIndex->isUserMarked($uid); + if (!$marked) { + try { + $user = $this->access->userManager->get($uid); + if (($user instanceof User) && !$this->userExistsOnLDAP($uid, true)) { + $user->markUser(); + $marked = true; + } + } catch (\Exception $e) { + $this->logger->debug( + $e->getMessage(), + ['app' => 'user_ldap', 'exception' => $e] + ); + } + if (!$marked) { + $this->logger->notice( + 'User ' . $uid . ' is not marked as deleted, not cleaning up.', + ['app' => 'user_ldap'] + ); + return false; + } } - \OC::$server->getLogger()->info('Cleaning up after user ' . $uid, + $this->logger->info('Cleaning up after user ' . $uid, ['app' => 'user_ldap']); $this->access->getUserMapper()->unmap($uid); // we don't emit unassign signals here, since it is implicit to delete signals fired from core @@ -411,7 +391,7 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn return $this->userPluginManager->getHome($uid); } - $cacheKey = 'getHome'.$uid; + $cacheKey = 'getHome' . $uid; $path = $this->access->connection->getFromCache($cacheKey); if (!is_null($path)) { return $path; @@ -443,7 +423,7 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn return false; } - $cacheKey = 'getDisplayName'.$uid; + $cacheKey = 'getDisplayName' . $uid; if (!is_null($displayName = $this->access->connection->getFromCache($cacheKey))) { return $displayName; } @@ -470,11 +450,10 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn $user = $this->access->userManager->get($uid); if ($user instanceof User) { - $displayName = $user->composeAndStoreDisplayName($displayName, $displayName2); + $displayName = $user->composeAndStoreDisplayName($displayName, (string)$displayName2); $this->access->connection->writeToCache($cacheKey, $displayName); } if ($user instanceof OfflineUser) { - /** @var OfflineUser $user*/ $displayName = $user->getDisplayName(); } return $displayName; @@ -502,12 +481,12 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn * Get a list of all display names * * @param string $search - * @param string|null $limit - * @param string|null $offset + * @param int|null $limit + * @param int|null $offset * @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; + $cacheKey = 'getDisplayNames-' . $search . '-' . $limit . '-' . $offset; if (!is_null($displayNames = $this->access->connection->getFromCache($cacheKey))) { return $displayNames; } @@ -549,24 +528,26 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn /** * counts the users in LDAP - * - * @return int|bool */ - public function countUsers() { + public function countUsers(int $limit = 0): int|false { if ($this->userPluginManager->implementsActions(Backend::COUNT_USERS)) { return $this->userPluginManager->countUsers(); } $filter = $this->access->getFilterForUserCount(); - $cacheKey = 'countUsers-'.$filter; + $cacheKey = 'countUsers-' . $filter . '-' . $limit; if (!is_null($entries = $this->access->connection->getFromCache($cacheKey))) { return $entries; } - $entries = $this->access->countUsers($filter); + $entries = $this->access->countUsers($filter, limit:$limit); $this->access->connection->writeToCache($cacheKey, $entries); return $entries; } + public function countMappedUsers(): int { + return $this->access->getUserMapper()->count(); + } + /** * Backend name to be shown in user management * @return string the name of the backend to be shown @@ -574,7 +555,7 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn public function getBackendName() { return 'LDAP'; } - + /** * Return access for LDAP interaction. * @param string $uid @@ -583,13 +564,13 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn public function getLDAPAccess($uid) { return $this->access; } - + /** * Return LDAP connection resource from a cloned connection. * The cloned connection needs to be closed manually. * of the current access. * @param string $uid - * @return resource of the LDAP connection + * @return \LDAP\Connection The LDAP connection */ public function getNewLDAPConnection($uid) { $connection = clone $this->access->getConnection(); @@ -617,9 +598,8 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn $uuid, true ); - $this->access->cacheUserExists($username); } else { - \OC::$server->getLogger()->warning( + $this->logger->warning( 'Failed to map created LDAP user with userid {userid}, because UUID could not be determined', [ 'app' => 'user_ldap', @@ -628,11 +608,28 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn ); } } else { - throw new \UnexpectedValueException("LDAP Plugin: Method createUser changed to return the user DN instead of boolean."); + throw new \UnexpectedValueException('LDAP Plugin: Method createUser changed to return the user DN instead of boolean.'); } } - return (bool) $dn; + return (bool)$dn; } return false; } + + public function isUserEnabled(string $uid, callable $queryDatabaseValue): bool { + if ($this->deletedUsersIndex->isUserMarked($uid) && ((int)$this->access->connection->markRemnantsAsDisabled === 1)) { + return false; + } else { + return $queryDatabaseValue(); + } + } + + public function setUserEnabled(string $uid, bool $enabled, callable $queryDatabaseValue, callable $setDatabaseValue): bool { + $setDatabaseValue($enabled); + return $enabled; + } + + public function getDisabledUserList(?int $limit = null, int $offset = 0, string $search = ''): array { + throw new \Exception('This is implemented directly in User_Proxy'); + } } |