From a1255539b838b89f7946ce9b819678e7d2057759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Tue, 8 Aug 2023 14:54:40 +0200 Subject: [PATCH 1/5] Add endpoint for getting disabled user list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- apps/provisioning_api/appinfo/routes.php | 1 + .../lib/Controller/UsersController.php | 66 ++++++++++++++++++- lib/private/User/Manager.php | 30 +++++++++ lib/public/IUserManager.php | 6 ++ 4 files changed, 102 insertions(+), 1 deletion(-) diff --git a/apps/provisioning_api/appinfo/routes.php b/apps/provisioning_api/appinfo/routes.php index ab404ff84597d..6d9be753d453d 100644 --- a/apps/provisioning_api/appinfo/routes.php +++ b/apps/provisioning_api/appinfo/routes.php @@ -47,6 +47,7 @@ // Users ['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#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 b7e9b5d0de086..7d8f949b24417 100644 --- a/apps/provisioning_api/lib/Controller/UsersController.php +++ b/apps/provisioning_api/lib/Controller/UsersController.php @@ -231,6 +231,71 @@ public function getUsersDetails(string $search = '', int $limit = null, int $off ]); } + /** + * @NoAdminRequired + * + * Get the list of disabled users and their details + * + * @param ?int $limit Limit the amount of users returned + * @param int $offset Offset + * @return DataResponse}, array{}> + */ + public function getDisabledUsersDetails(?int $limit = null, int $offset = 0): DataResponse { + $currentUser = $this->userSession->getUser(); + if ($currentUser === null) { + return new DataResponse(['users' => []]); + } + $users = []; + + // Admin? Or SubAdmin? + $uid = $currentUser->getUID(); + $subAdminManager = $this->groupManager->getSubAdmin(); + if ($this->groupManager->isAdmin($uid)) { + $users = $this->userManager->getDisabledUsers($limit, $offset); + $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('', ($tempLimit === null ? null : $tempLimit - count($users))), + fn (IUser $user): bool => $user->isEnabled() + ) + ) + ); + if (($tempLimit !== null) && (count($users) >= $tempLimit)) { + break; + } + } + $users = array_slice($users, $offset); + } + + /** @var array $usersDetails */ + $usersDetails = []; + foreach ($users as $userId) { + $userData = $this->getUserData($userId); + // Do not insert empty entry + if ($userData !== null) { + $usersDetails[$userId] = $userData; + } else { + // Logged user does not have permissions to see this user + // only showing its id + $usersDetails[$userId] = ['id' => $userId]; + } + } + + return new DataResponse([ + 'users' => $usersDetails + ]); + } + /** * @NoAdminRequired @@ -852,7 +917,6 @@ public function editUser(string $userId, string $key, string $value): DataRespon if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())) { $permittedFields[] = self::USER_FIELD_QUOTA; $permittedFields[] = self::USER_FIELD_MANAGER; - } } else { // Check if admin / subadmin diff --git a/lib/private/User/Manager.php b/lib/private/User/Manager.php index fb1afb6582564..8ec8ef0c4bec9 100644 --- a/lib/private/User/Manager.php +++ b/lib/private/User/Manager.php @@ -52,6 +52,7 @@ use OCP\User\Backend\ISearchKnownUsersBackend; use OCP\User\Backend\ICheckPasswordBackend; use OCP\User\Backend\ICountUsersBackend; +use OCP\User\Backend\IProvideEnabledStateBackend; use OCP\User\Events\BeforeUserCreatedEvent; use OCP\User\Events\UserCreatedEvent; use OCP\UserInterface; @@ -337,6 +338,35 @@ public function searchDisplayName($pattern, $limit = null, $offset = null) { return $users; } + /** + * @return IUser[] + */ + public function getDisabledUsers(?int $limit = null, int $offset = 0): array { + $users = $this->config->getUsersForUserValue('core', 'enabled', 'false'); + $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/IUserManager.php b/lib/public/IUserManager.php index 1efb3d5f0c250..0a94c5ad92846 100644 --- a/lib/public/IUserManager.php +++ b/lib/public/IUserManager.php @@ -139,6 +139,12 @@ public function search($pattern, $limit = null, $offset = null); */ public function searchDisplayName($pattern, $limit = null, $offset = null); + /** + * @return IUser[] + * @since 28.0.0 + */ + public function getDisabledUsers(?int $limit = null, int $offset = 0): array; + /** * Search known users (from phonebook sync) by displayName * From cac3d465f0474ec72ef8f84405919a84d67b4bc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Mon, 28 Aug 2023 15:51:48 +0200 Subject: [PATCH 2/5] Remove psalm @var annotations which should not be needed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- apps/provisioning_api/lib/Controller/UsersController.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/provisioning_api/lib/Controller/UsersController.php b/apps/provisioning_api/lib/Controller/UsersController.php index 7d8f949b24417..4ee87b960f15b 100644 --- a/apps/provisioning_api/lib/Controller/UsersController.php +++ b/apps/provisioning_api/lib/Controller/UsersController.php @@ -211,7 +211,6 @@ public function getUsersDetails(string $search = '', int $limit = null, int $off $users = array_merge(...$users); } - /** @var array $usersDetails */ $usersDetails = []; foreach ($users as $userId) { $userId = (string) $userId; @@ -277,7 +276,6 @@ public function getDisabledUsersDetails(?int $limit = null, int $offset = 0): Da $users = array_slice($users, $offset); } - /** @var array $usersDetails */ $usersDetails = []; foreach ($users as $userId) { $userData = $this->getUserData($userId); From 61da2b97d0eaa194a9a83138c9a66a5d615c8f45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Mon, 28 Aug 2023 15:56:14 +0200 Subject: [PATCH 3/5] Generated openapi.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- apps/provisioning_api/openapi.json | 106 +++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/apps/provisioning_api/openapi.json b/apps/provisioning_api/openapi.json index cb30b5faad422..115189e8008e7 100644 --- a/apps/provisioning_api/openapi.json +++ b/apps/provisioning_api/openapi.json @@ -2028,6 +2028,112 @@ } } }, + "/ocs/v2.php/cloud/users/disabled": { + "get": { + "operationId": "users-get-disabled-users-details", + "summary": "Get the list of disabled users and their details", + "tags": [ + "users" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "limit", + "in": "query", + "description": "Limit the amount of users returned", + "schema": { + "type": "integer", + "format": "int64", + "nullable": true + } + }, + { + "name": "offset", + "in": "query", + "description": "Offset", + "schema": { + "type": "integer", + "format": "int64", + "default": 0 + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "required": true, + "schema": { + "type": "string", + "default": "true" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "type": "object", + "required": [ + "users" + ], + "properties": { + "users": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "$ref": "#/components/schemas/UserDetails" + }, + { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + } + } + } + ] + } + } + } + } + } + } + } + } + } + } + } + } + } + }, "/ocs/v2.php/cloud/users/search/by-phone": { "post": { "operationId": "users-search-by-phone-numbers", From afcebd1e928f6131122bc166af2b37c24717891f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Mon, 9 Oct 2023 14:50:12 +0200 Subject: [PATCH 4/5] Fix api description for Users#getDisabledUsers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- apps/provisioning_api/lib/Controller/UsersController.php | 2 ++ apps/provisioning_api/openapi.json | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/provisioning_api/lib/Controller/UsersController.php b/apps/provisioning_api/lib/Controller/UsersController.php index 4ee87b960f15b..95778eff36640 100644 --- a/apps/provisioning_api/lib/Controller/UsersController.php +++ b/apps/provisioning_api/lib/Controller/UsersController.php @@ -238,6 +238,8 @@ public function getUsersDetails(string $search = '', int $limit = null, int $off * @param ?int $limit Limit the amount of users returned * @param int $offset Offset * @return DataResponse}, array{}> + * + * 200: Disabled users details returned */ public function getDisabledUsersDetails(?int $limit = null, int $offset = 0): DataResponse { $currentUser = $this->userSession->getUser(); diff --git a/apps/provisioning_api/openapi.json b/apps/provisioning_api/openapi.json index 115189e8008e7..566e907bac138 100644 --- a/apps/provisioning_api/openapi.json +++ b/apps/provisioning_api/openapi.json @@ -2067,16 +2067,17 @@ { "name": "OCS-APIRequest", "in": "header", + "description": "Required to be true for the API request to pass", "required": true, "schema": { - "type": "string", - "default": "true" + "type": "boolean", + "default": true } } ], "responses": { "200": { - "description": "", + "description": "Disabled users details returned", "content": { "application/json": { "schema": { From e51d20479e70320486385a4787f5d09abdd6ed4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Tue, 10 Oct 2023 10:43:18 +0200 Subject: [PATCH 5/5] Check limit and offset parameters sent to controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- apps/provisioning_api/lib/Controller/UsersController.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/provisioning_api/lib/Controller/UsersController.php b/apps/provisioning_api/lib/Controller/UsersController.php index 95778eff36640..97d94ecb407d2 100644 --- a/apps/provisioning_api/lib/Controller/UsersController.php +++ b/apps/provisioning_api/lib/Controller/UsersController.php @@ -246,6 +246,13 @@ public function getDisabledUsersDetails(?int $limit = null, int $offset = 0): Da 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?