diff options
author | yemkareems <yemkareems@gmail.com> | 2024-06-06 16:35:44 +0530 |
---|---|---|
committer | yemkareems <yemkareems@gmail.com> | 2024-07-08 15:42:55 +0530 |
commit | 4cb85f7c9e37af742d170373602709f8db2d525d (patch) | |
tree | 8d63eaabb24787cda4afc4c8b44b3914c4351dec | |
parent | 76c875a5882753e37ef39015a1711b68e7ea0d0b (diff) | |
download | nextcloud-server-4cb85f7c9e37af742d170373602709f8db2d525d.tar.gz nextcloud-server-4cb85f7c9e37af742d170373602709f8db2d525d.zip |
fix: rebased the branch with master and resolved conflicts
fix: added a new endpoint users/recent and getting users based on last login info in the same. Reverted old code that was breaking LDAP
Signed-off-by: yemkareems <yemkareems@gmail.com>
-rw-r--r-- | apps/provisioning_api/appinfo/routes.php | 1 | ||||
-rw-r--r-- | apps/provisioning_api/lib/Controller/UsersController.php | 102 | ||||
-rw-r--r-- | lib/private/AllConfig.php | 43 | ||||
-rw-r--r-- | lib/private/Group/Manager.php | 4 | ||||
-rw-r--r-- | lib/private/User/Backend.php | 8 | ||||
-rw-r--r-- | lib/private/User/Database.php | 30 | ||||
-rw-r--r-- | lib/private/User/Manager.php | 27 | ||||
-rw-r--r-- | lib/public/IConfig.php | 2 | ||||
-rw-r--r-- | lib/public/IUserManager.php | 1 | ||||
-rw-r--r-- | lib/public/UserInterface.php | 6 |
10 files changed, 183 insertions, 41 deletions
diff --git a/apps/provisioning_api/appinfo/routes.php b/apps/provisioning_api/appinfo/routes.php index b44685af175..78526ce6402 100644 --- a/apps/provisioning_api/appinfo/routes.php +++ b/apps/provisioning_api/appinfo/routes.php @@ -28,6 +28,7 @@ return [ ['root' => '/cloud', 'name' => 'Users#getUsers', 'url' => '/users', 'verb' => 'GET'], ['root' => '/cloud', 'name' => 'Users#getUsersDetails', 'url' => '/users/details', 'verb' => 'GET'], ['root' => '/cloud', 'name' => 'Users#getDisabledUsersDetails', 'url' => '/users/disabled', 'verb' => 'GET'], + ['root' => '/cloud', 'name' => 'Users#getLastLoggedInUsers', 'url' => '/users/recent', 'verb' => 'GET'], ['root' => '/cloud', 'name' => 'Users#searchByPhoneNumbers', 'url' => '/users/search/by-phone', 'verb' => 'POST'], ['root' => '/cloud', 'name' => 'Users#addUser', 'url' => '/users', 'verb' => 'POST'], ['root' => '/cloud', 'name' => 'Users#getUser', 'url' => '/users/{userId}', 'verb' => 'GET'], diff --git a/apps/provisioning_api/lib/Controller/UsersController.php b/apps/provisioning_api/lib/Controller/UsersController.php index eced881516f..ec57d3e9b23 100644 --- a/apps/provisioning_api/lib/Controller/UsersController.php +++ b/apps/provisioning_api/lib/Controller/UsersController.php @@ -94,7 +94,7 @@ class UsersController extends AUserData { * * 200: Users returned */ - public function getUsers(string $search = '', ?int $limit = null, int $offset = 0, string $sortMode = 'uid', string $sortOrder = 'asc'): DataResponse { + public function getUsers(string $search = '', ?int $limit = null, int $offset = 0): DataResponse { $user = $this->userSession->getUser(); $users = []; @@ -102,7 +102,7 @@ class UsersController extends AUserData { $uid = $user->getUID(); $subAdminManager = $this->groupManager->getSubAdmin(); if ($this->groupManager->isAdmin($uid)) { - $users = $this->userManager->search($search, $limit, $offset, $sortMode, $sortOrder); + $users = $this->userManager->search($search, $limit, $offset); } elseif ($subAdminManager->isSubAdmin($user)) { $subAdminOfGroups = $subAdminManager->getSubAdminsGroups($user); foreach ($subAdminOfGroups as $key => $group) { @@ -131,13 +131,11 @@ class UsersController extends AUserData { * @param string $search Text to search for * @param int|null $limit Limit the amount of groups returned * @param int $offset Offset for searching for groups - * @param string $sortMode Field to order the results with - * @param string $sortOrder asc or desc * @return DataResponse<Http::STATUS_OK, array{users: array<string, Provisioning_APIUserDetails|array{id: string}>}, array{}> * * 200: Users details returned */ - public function getUsersDetails(string $search = '', ?int $limit = null, int $offset = 0, string $sortMode = 'uid', string $sortOrder = 'asc'): DataResponse { + public function getUsersDetails(string $search = '', ?int $limit = null, int $offset = 0): DataResponse { $currentUser = $this->userSession->getUser(); $users = []; @@ -145,7 +143,7 @@ class UsersController extends AUserData { $uid = $currentUser->getUID(); $subAdminManager = $this->groupManager->getSubAdmin(); if ($this->groupManager->isAdmin($uid)) { - $users = $this->userManager->search($search, $limit, $offset, $sortMode, $sortOrder); + $users = $this->userManager->search($search, $limit, $offset); $users = array_keys($users); } elseif ($subAdminManager->isSubAdmin($currentUser)) { $subAdminOfGroups = $subAdminManager->getSubAdminsGroups($currentUser); @@ -155,7 +153,7 @@ class UsersController extends AUserData { $users = []; foreach ($subAdminOfGroups as $group) { - $users[] = array_keys($this->groupManager->displayNamesInGroup($group, $search, $limit, $offset, $sortMode, $sortOrder)); + $users[] = array_keys($this->groupManager->displayNamesInGroup($group, $search, $limit, $offset)); } $users = array_merge(...$users); } @@ -267,6 +265,96 @@ class UsersController extends AUserData { ]); } + /** + * @NoAdminRequired + * @NoCSRFRequired + * + * Get the list of disabled users and their details + * + * @param string $search Text to search for + * @param ?int $limit Limit the amount of users returned + * @param int $offset Offset + * @param string $sortMode Field to order the results with + * @param string $sortOrder asc or desc + * @return DataResponse<Http::STATUS_OK, array{users: array<string, Provisioning_APIUserDetails|array{id: string}>}, array{}> + * + * 200: Users details returned based on last logged in information + */ + public function getLastLoggedInUsers(string $search = '', + ?int $limit = null, + int $offset = 0, + string $sortMode = 'lastLogin', + string $sortOrder = 'desc' + ): DataResponse { + $currentUser = $this->userSession->getUser(); + if ($currentUser === null) { + return new DataResponse(['users' => []]); + } + if ($limit !== null && $limit < 0) { + throw new InvalidArgumentException("Invalid limit value: $limit"); + } + if ($offset < 0) { + throw new InvalidArgumentException("Invalid offset value: $offset"); + } + + $users = []; + + // Admin? Or SubAdmin? + $uid = $currentUser->getUID(); + $subAdminManager = $this->groupManager->getSubAdmin(); + if ($this->groupManager->isAdmin($uid)) { + $users = $this->userManager->getUsersSortedByLastLogin($limit, $offset, $search, $sortMode, $sortOrder); + $users = array_map(fn (IUser $user): string => $user->getUID(), $users); + } elseif ($subAdminManager->isSubAdmin($currentUser)) { + $subAdminOfGroups = $subAdminManager->getSubAdminsGroups($currentUser); + + $users = []; + /* We have to handle offset ourselve for correctness */ + $tempLimit = ($limit === null ? null : $limit + $offset); + foreach ($subAdminOfGroups as $group) { + $users = array_merge( + $users, + array_map( + fn (IUser $user): string => $user->getUID(), + array_filter( + $group->searchUsers($search, ($tempLimit === null ? null : $tempLimit - count($users))), + fn (IUser $user): bool => !$user->isEnabled() + ) + ) + ); + if (($tempLimit !== null) && (count($users) >= $tempLimit)) { + break; + } + } + $users = array_slice($users, $offset); + } + + $usersDetails = []; + foreach ($users as $userId) { + try { + $userData = $this->getUserData($userId); + } catch (OCSNotFoundException $e) { + // We still want to return all other accounts, but this one was removed from the backends + // yet they are still in our database. Might be a LDAP remnant. + $userData = null; + $this->logger->warning('Found one disabled account that was removed from its backend, but still exists in Nextcloud database', ['accountId' => $userId]); + } + // Do not insert empty entry + if ($userData !== null) { + $usersDetails[$userId] = $userData; + } else { + // Currently logged in user does not have permissions to see this user + // only showing its id + $usersDetails[$userId] = ['id' => $userId]; + } + } + + return new DataResponse([ + 'users' => $usersDetails + ]); + } + + /** * @NoAdminRequired diff --git a/lib/private/AllConfig.php b/lib/private/AllConfig.php index d05fe440202..7fbc4c1e148 100644 --- a/lib/private/AllConfig.php +++ b/lib/private/AllConfig.php @@ -491,6 +491,49 @@ class AllConfig implements IConfig { return $userIDs; } + public function getLastLoggedInUsers($search, $sortMode, $sortOrder): array { + // TODO - FIXME + $this->fixDIInit(); + + $query = $this->connection->getQueryBuilder(); + + if ($sortMode === 'lastLogin') { + $lastLoginSubSelect = $this->connection->getQueryBuilder(); + $lastLoginSubSelect->select('configvalue') + ->from('preferences', 'p2') + ->where($lastLoginSubSelect->expr()->andX( + $lastLoginSubSelect->expr()->eq('p2.userid', 'uid'), + $lastLoginSubSelect->expr()->eq('p2.appid', $lastLoginSubSelect->expr()->literal('login')), + $lastLoginSubSelect->expr()->eq('p2.configkey', $lastLoginSubSelect->expr()->literal('lastLogin')), + )); + $orderByExpression = $query->createFunction('(' . $lastLoginSubSelect->getSQL() .')'); + } else { + $orderByExpression = $query->func()->lower('displayname'); + } + + $query->select('uid', 'displayname', $orderByExpression) + ->from('users', 'u') + ->leftJoin('u', 'preferences', 'p', $query->expr()->andX( + $query->expr()->eq('userid', 'uid'), + $query->expr()->eq('appid', $query->expr()->literal('settings')), + $query->expr()->eq('configkey', $query->expr()->literal('email'))) + ) + // sqlite doesn't like re-using a single named parameter here + ->where($query->expr()->iLike('uid', $query->createPositionalParameter('%' . $this->connection->escapeLikeParameter($search) . '%'))) + ->orWhere($query->expr()->iLike('displayname', $query->createPositionalParameter('%' . $this->connection->escapeLikeParameter($search) . '%'))) + ->orWhere($query->expr()->iLike('configvalue', $query->createPositionalParameter('%' . $this->connection->escapeLikeParameter($search) . '%'))) + ->orderBy($orderByExpression, $sortOrder) + ->addOrderBy('uid_lower', 'ASC'); + + $result = $query->executeQuery(); + $displayNames = []; + while ($row = $result->fetch()) { + $displayNames[(string)$row['uid']] = (string)$row['uid']; + } + + return $displayNames; + } + /** * Determines the users that have the given value set for a specific app-key-pair * diff --git a/lib/private/Group/Manager.php b/lib/private/Group/Manager.php index 362d7424e45..0ab64907c8b 100644 --- a/lib/private/Group/Manager.php +++ b/lib/private/Group/Manager.php @@ -401,7 +401,7 @@ class Manager extends PublicEmitter implements IGroupManager { * @param int $offset * @return array an array of display names (value) and user ids (key) */ - public function displayNamesInGroup($gid, $search = '', $limit = -1, $offset = 0, $sortMode = 'uid', $sortOrder = 'asc') { + public function displayNamesInGroup($gid, $search = '', $limit = -1, $offset = 0) { $group = $this->get($gid); if (is_null($group)) { return []; @@ -419,7 +419,7 @@ class Manager extends PublicEmitter implements IGroupManager { } do { - $filteredUsers = $this->userManager->searchDisplayName($search, $searchLimit, $searchOffset, $sortMode, $sortOrder); + $filteredUsers = $this->userManager->searchDisplayName($search, $searchLimit, $searchOffset); foreach ($filteredUsers as $filteredUser) { if ($group->inGroup($filteredUser)) { $groupUsers[] = $filteredUser; diff --git a/lib/private/User/Backend.php b/lib/private/User/Backend.php index 5d06b54c28b..98b291e5dee 100644 --- a/lib/private/User/Backend.php +++ b/lib/private/User/Backend.php @@ -89,11 +89,9 @@ abstract class Backend implements UserInterface { * @param string $search * @param null|int $limit * @param null|int $offset - * @param string $sortMode - * @param string $sortOrder * @return string[] an array of all uids */ - public function getUsers($search = '', $limit = null, $offset = null, string $sortMode = 'uid', string $sortOrder = 'asc') { + public function getUsers($search = '', $limit = null, $offset = null) { return []; } @@ -130,11 +128,9 @@ abstract class Backend implements UserInterface { * @param string $search * @param int|null $limit * @param int|null $offset - * @param string $sortMode - * @param string $sortOrder * @return array an array of all displayNames (value) and the corresponding uids (key) */ - public function getDisplayNames($search = '', $limit = null, $offset = null, string $sortMode = 'uid', string $sortOrder = 'asc'): array { + public function getDisplayNames($search = '', $limit = null, $offset = null) { $displayNames = []; $users = $this->getUsers($search, $limit, $offset); foreach ($users as $user) { diff --git a/lib/private/User/Database.php b/lib/private/User/Database.php index 9a62bcf3e12..cc7050f2da8 100644 --- a/lib/private/User/Database.php +++ b/lib/private/User/Database.php @@ -227,32 +227,16 @@ class Database extends ABackend implements * @param string $search * @param int|null $limit * @param int|null $offset - * @param string $sortMode - * @param string $sortOrder * @return array an array of all displayNames (value) and the corresponding uids (key) */ - public function getDisplayNames($search = '', $limit = null, $offset = null, string $sortMode = 'uid', string $sortOrder = 'asc'): array { + public function getDisplayNames($search = '', $limit = null, $offset = null) { $limit = $this->fixLimit($limit); $this->fixDI(); $query = $this->dbConn->getQueryBuilder(); - if ($sortMode === 'lastLogin') { - $lastLoginSubSelect = $this->dbConn->getQueryBuilder(); - $lastLoginSubSelect->select('configvalue') - ->from('preferences', 'p2') - ->where($lastLoginSubSelect->expr()->andX( - $lastLoginSubSelect->expr()->eq('p2.userid', 'uid'), - $lastLoginSubSelect->expr()->eq('p2.appid', $lastLoginSubSelect->expr()->literal('login')), - $lastLoginSubSelect->expr()->eq('p2.configkey', $lastLoginSubSelect->expr()->literal('lastLogin')), - )); - $orderByExpression = $query->createFunction('(' . $lastLoginSubSelect->getSQL() .')'); - } else { - $orderByExpression = $query->func()->lower('displayname'); - } - - $query->select('uid', 'displayname', $orderByExpression) + $query->select('uid', 'displayname') ->from($this->table, 'u') ->leftJoin('u', 'preferences', 'p', $query->expr()->andX( $query->expr()->eq('userid', 'uid'), @@ -263,7 +247,7 @@ class Database extends ABackend implements ->where($query->expr()->iLike('uid', $query->createPositionalParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%'))) ->orWhere($query->expr()->iLike('displayname', $query->createPositionalParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%'))) ->orWhere($query->expr()->iLike('configvalue', $query->createPositionalParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%'))) - ->orderBy($orderByExpression, $sortOrder) + ->orderBy($query->func()->lower('displayname'), 'ASC') ->addOrderBy('uid_lower', 'ASC') ->setMaxResults($limit) ->setFirstResult($offset); @@ -397,13 +381,15 @@ class Database extends ABackend implements * @param null|int $offset * @return string[] an array of all uids */ - public function getUsers($search = '', $limit = null, $offset = null, $orderBy = 'lastLogin', $sort = 'DESC'): array { + public function getUsers($search = '', $limit = null, $offset = null) { $limit = $this->fixLimit($limit); - $users = $this->getDisplayNames($search, $limit, $offset, $orderBy, $sort); - return array_map(function ($uid) { + $users = $this->getDisplayNames($search, $limit, $offset); + $userIds = array_map(function ($uid) { return (string)$uid; }, array_keys($users)); + sort($userIds, SORT_STRING | SORT_FLAG_CASE); + return $userIds; } /** diff --git a/lib/private/User/Manager.php b/lib/private/User/Manager.php index 49f96c25a05..2efb93e65be 100644 --- a/lib/private/User/Manager.php +++ b/lib/private/User/Manager.php @@ -402,6 +402,33 @@ class Manager extends PublicEmitter implements IUserManager { return array_slice($users, $offset, $limit); } + public function getUsersSortedByLastLogin(?int $limit = null, int $offset = 0, $search = '', $sortMode = 'lastLogin', $sortOrder = 'desc'): array { + $users = $this->config->getLastLoggedInUsers($search, $sortMode, $sortOrder); + $users = array_combine( + $users, + array_map( + fn (string $uid): IUser => new LazyUser($uid, $this), + $users + ) + ); + + $tempLimit = ($limit === null ? null : $limit + $offset); + foreach ($this->backends as $backend) { + if (($tempLimit !== null) && (count($users) >= $tempLimit)) { + break; + } + if ($backend instanceof IProvideEnabledStateBackend) { + $backendUsers = $backend->getDisabledUserList(($tempLimit === null ? null : $tempLimit - count($users))); + foreach ($backendUsers as $uid) { + $users[$uid] = new LazyUser($uid, $this, null, $backend); + } + } + } + + return array_slice($users, $offset, $limit); + } + + /** * Search known users (from phonebook sync) by displayName * diff --git a/lib/public/IConfig.php b/lib/public/IConfig.php index b7feabd0ef5..39f9ecff94c 100644 --- a/lib/public/IConfig.php +++ b/lib/public/IConfig.php @@ -249,4 +249,6 @@ interface IConfig { * @since 8.0.0 */ public function getUsersForUserValue($appName, $key, $value); + + public function getLastLoggedInUsers($search, $sortMode, $sortOrder); } diff --git a/lib/public/IUserManager.php b/lib/public/IUserManager.php index b4fb265f6eb..70420dc7c7a 100644 --- a/lib/public/IUserManager.php +++ b/lib/public/IUserManager.php @@ -124,6 +124,7 @@ interface IUserManager { */ public function getDisabledUsers(?int $limit = null, int $offset = 0, string $search = ''): array; + public function getUsersSortedByLastLogin(?int $limit = null, int $offset = 0, $search = '', $sortMode = 'lastLogin', $sortOrder = 'desc'): array; /** * Search known users (from phonebook sync) by displayName * diff --git a/lib/public/UserInterface.php b/lib/public/UserInterface.php index a28e9ffe726..34e7a09feb7 100644 --- a/lib/public/UserInterface.php +++ b/lib/public/UserInterface.php @@ -45,7 +45,7 @@ interface UserInterface { * @return string[] an array of all uids * @since 4.5.0 */ - public function getUsers($search = '', $limit = null, $offset = null, string $sortMode = 'uid', string $sortOrder = 'asc'); + public function getUsers($search = '', $limit = null, $offset = null); /** * check if a user exists @@ -69,12 +69,10 @@ interface UserInterface { * @param string $search * @param int|null $limit * @param int|null $offset - * @param string $sortMode - * @param string $sortOrder * @return array an array of all displayNames (value) and the corresponding uids (key) * @since 4.5.0 */ - public function getDisplayNames($search = '', $limit = null, $offset = null, string $sortMode = 'uid', string $sortOrder = 'asc'); + public function getDisplayNames($search = '', $limit = null, $offset = null); /** * Check if a user list is available or not |