From ad513bda3ed368e5fe8388037b77e4fa9360cf69 Mon Sep 17 00:00:00 2001 From: Claus-Justus Heine Date: Thu, 17 Feb 2022 18:23:07 +0100 Subject: [PATCH 01/15] Allow delegation of admin settings. Signed-off-by: Claus-Justus Heine --- lib/Settings/Admin.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/Settings/Admin.php b/lib/Settings/Admin.php index e578dc3..286b9ce 100644 --- a/lib/Settings/Admin.php +++ b/lib/Settings/Admin.php @@ -23,14 +23,14 @@ use OCA\UserSQL\Properties; use OCP\AppFramework\Http\TemplateResponse; -use OCP\Settings\ISettings; +use OCP\Settings\IDelegatedSettings; /** * The administrator's settings page. * * @author Marcin Ɓojewski */ -class Admin implements ISettings +class Admin implements IDelegatedSettings { /** * @var string The application name. @@ -76,4 +76,12 @@ public function getPriority() { return 25; } + + public function getName(): ?string { + return null; // Only one setting in this section + } + + public function getAuthorizedAppConfig(): array { + return []; // Custom controller + } } From b1819aa32eceae3a5f790b968494f7a808ca25d8 Mon Sep 17 00:00:00 2001 From: Claus-Justus Heine Date: Thu, 17 Feb 2022 19:37:35 +0100 Subject: [PATCH 02/15] Add @AuthorizedAdminSetting to all controller methods. Signed-off-by: Claus-Justus Heine --- lib/Controller/SettingsController.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index 845abf9..8ae3668 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -92,6 +92,8 @@ public function __construct( * Verify the database connection parameters. * * @return array The request status. + * + * @AuthorizedAdminSetting(settings=OCA\UserSQL\Settings\Admin) */ public function verifyDbConnection() { @@ -189,6 +191,8 @@ private function getConnection() * Save application properties. * * @return array The request status. + * + * @AuthorizedAdminSetting(settings=OCA\UserSQL\Settings\Admin) */ public function saveProperties() { @@ -329,6 +333,8 @@ private function cryptoClassConfiguration($cryptoClass) * Clear the application cache memory. * * @return array The request status. + * + * @AuthorizedAdminSetting(settings=OCA\UserSQL\Settings\Admin) */ public function clearCache() { @@ -356,6 +362,8 @@ public function clearCache() * Autocomplete for table select options. * * @return array The database table list. + * + * @AuthorizedAdminSetting(settings=OCA\UserSQL\Settings\Admin) */ public function tableAutocomplete() { @@ -385,6 +393,8 @@ public function tableAutocomplete() * Autocomplete for column select options - user table. * * @return array The database table's column list. + * + * @AuthorizedAdminSetting(settings=OCA\UserSQL\Settings\Admin) */ public function userTableAutocomplete() { @@ -430,6 +440,8 @@ private function columnAutocomplete($table) * Autocomplete for column select options - user_group table. * * @return array The database table's column list. + * + * @AuthorizedAdminSetting(settings=OCA\UserSQL\Settings\Admin) */ public function userGroupTableAutocomplete() { @@ -451,6 +463,8 @@ public function userGroupTableAutocomplete() * Autocomplete for column select options - group table. * * @return array The database table's column list. + * + * @AuthorizedAdminSetting(settings=OCA\UserSQL\Settings\Admin) */ public function groupTableAutocomplete() { @@ -473,6 +487,8 @@ public function groupTableAutocomplete() * * @return array Password algorithm parameters. * @throws ReflectionException Whenever Opt class cannot be initiated. + * + * @AuthorizedAdminSetting(settings=OCA\UserSQL\Settings\Admin) */ public function cryptoParams() { From b39421d32dad7ca67e530435092cb541212c86d8 Mon Sep 17 00:00:00 2001 From: Claus-Justus Heine Date: Fri, 18 Feb 2022 12:47:18 +0100 Subject: [PATCH 03/15] Selecting and counting users from the User-Group table needs DISTINCT to avoid duplicates. In particular when using the catch-all group ("Default Group" setting) the queries which count and select users from the User-Group table need the "DISTINCT" option: the catch-all group is replaced by a '%' wild-card in the query. As users may belong by design to more than one group counting and selecting users comes out wrong. Even worse: the many duplicates interfere with the paging logic of the user admin-settings. Signed-off-by: Claus-Justus Heine --- lib/Query/QueryProvider.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Query/QueryProvider.php b/lib/Query/QueryProvider.php index 7252cda..0a1f9c1 100644 --- a/lib/Query/QueryProvider.php +++ b/lib/Query/QueryProvider.php @@ -135,7 +135,7 @@ private function loadQueries() "AND g.$gAdmin", Query::COUNT_GROUPS => - "SELECT COUNT(ug.$ugGID) " . + "SELECT COUNT(DISTINCT ug.$ugUID) " . "FROM $userGroup ug " . "WHERE ug.$ugGID LIKE :$gidParam " . "AND ug.$ugUID LIKE :$searchParam", @@ -152,7 +152,7 @@ private function loadQueries() "WHERE g.$gGID = :$gidParam", Query::FIND_GROUP_USERS => - "SELECT ug.$ugUID AS uid " . + "SELECT DISTINCT ug.$ugUID AS uid " . "FROM $userGroup ug " . "WHERE ug.$ugGID LIKE :$gidParam " . "AND ug.$ugUID LIKE :$searchParam " . From f1ca19cb6759181dc8f52d94536d7fa00c0e2069 Mon Sep 17 00:00:00 2001 From: Claus-Justus Heine Date: Tue, 24 May 2022 14:49:01 +0200 Subject: [PATCH 04/15] Set max version to 24 Signed-off-by: Claus-Justus Heine --- appinfo/info.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appinfo/info.xml b/appinfo/info.xml index be1bfa0..01c1fb2 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -22,7 +22,7 @@ auth - + \OCA\UserSQL\Settings\Admin From 05360d8aa2d4c16ac094829940f0789c2e42084d Mon Sep 17 00:00:00 2001 From: Claus-Justus Heine Date: Wed, 4 Jan 2023 23:47:01 +0100 Subject: [PATCH 05/15] Support NC 25. Signed-off-by: Claus-Justus Heine --- appinfo/info.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appinfo/info.xml b/appinfo/info.xml index 01c1fb2..88aaf65 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -22,7 +22,7 @@ auth - + \OCA\UserSQL\Settings\Admin From e9ccbd95d6077a7edc94442269a63bdba5fe97d0 Mon Sep 17 00:00:00 2001 From: Claus-Justus Heine Date: Mon, 30 Jan 2023 18:25:43 +0100 Subject: [PATCH 06/15] UserBackend: users fetched from the cache have to be converted back from an array representation to the User-object. Signed-off-by: Claus-Justus Heine --- lib/Backend/UserBackend.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/Backend/UserBackend.php b/lib/Backend/UserBackend.php index c78bc0d..1824c4a 100644 --- a/lib/Backend/UserBackend.php +++ b/lib/Backend/UserBackend.php @@ -459,6 +459,18 @@ public function getUsers($search = "", $limit = null, $offset = null, $callback "Returning from cache getUsers($search, $limit, $offset): count(" . count($users) . ")", ["app" => $this->appName] ); + // convert to user-model + foreach ($users as $index => $cachedUser) { + if (!is_array($cachedUser)) { + break; + } + $user = new User(); + foreach ($cachedUser as $key => $value) { + $user->{$key} = $value; + } + $users[$index] = $user; + } + return $users; } From 26930efc255d191fbebf0d9a15650c4a07540480 Mon Sep 17 00:00:00 2001 From: Claus-Justus Heine Date: Fri, 16 Jun 2023 10:06:28 +0200 Subject: [PATCH 07/15] DateQuery: use try-catch around statement prepare. Signed-off-by: Claus-Justus Heine --- lib/Query/DataQuery.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/Query/DataQuery.php b/lib/Query/DataQuery.php index 2f11c55..81e01c6 100644 --- a/lib/Query/DataQuery.php +++ b/lib/Query/DataQuery.php @@ -109,7 +109,15 @@ private function execQuery( } $query = $this->queryProvider[$queryName]; - $result = $this->connection->prepare($query, $limit, $offset); + try { + $result = $this->connection->prepare($query, $limit, $offset); + } catch (DBALException $exception) { + $this->logger->error( + "Could not prepare the query: " . $exception->getMessage(), + ["app" => $this->appName] + ); + return false; + } foreach ($params as $param => $value) { $result->bindValue(":" . $param, $value); From bb1f370e1667fa1251710d5b5c4be1c3e225e40b Mon Sep 17 00:00:00 2001 From: Claus-Justus Heine Date: Fri, 23 Jun 2023 01:17:24 +0200 Subject: [PATCH 08/15] Fix \ArrayAccess type warnings Signed-off-by: Claus-Justus Heine --- lib/Properties.php | 8 ++++---- lib/Query/QueryProvider.php | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/Properties.php b/lib/Properties.php index d30ae1a..762ce3c 100644 --- a/lib/Properties.php +++ b/lib/Properties.php @@ -203,7 +203,7 @@ public function getArray() /** * @inheritdoc */ - public function offsetExists($offset) + public function offsetExists(mixed $offset):bool { return isset($this->data[$offset]); } @@ -211,7 +211,7 @@ public function offsetExists($offset) /** * @inheritdoc */ - public function offsetGet($offset) + public function offsetGet(mixed $offset):mixed { if (isset($this->data[$offset])) { return $this->data[$offset]; @@ -223,7 +223,7 @@ public function offsetGet($offset) /** * @inheritdoc */ - public function offsetSet($offset, $value) + public function offsetSet(mixed $offset, mixed $value):void { if ($offset == Opt::SAFE_STORE) { $this->safeStore = ($value === App::TRUE_VALUE); @@ -255,7 +255,7 @@ public function offsetSet($offset, $value) /** * @inheritdoc */ - public function offsetUnset($offset) + public function offsetUnset(mixed $offset):void { if ($offset == Opt::SAFE_STORE) { $this->safeStore = App::FALSE_VALUE; diff --git a/lib/Query/QueryProvider.php b/lib/Query/QueryProvider.php index 0a1f9c1..f4834ff 100644 --- a/lib/Query/QueryProvider.php +++ b/lib/Query/QueryProvider.php @@ -238,7 +238,7 @@ private function loadQueries() /** * @inheritdoc */ - public function offsetExists($offset) + public function offsetExists(mixed $offset):bool { return isset($this->queries[$offset]); } @@ -246,7 +246,7 @@ public function offsetExists($offset) /** * @inheritdoc */ - public function offsetGet($offset) + public function offsetGet(mixed $offset):mixed { if (isset($this->queries[$offset])) { return $this->queries[$offset]; @@ -258,7 +258,7 @@ public function offsetGet($offset) /** * @inheritdoc */ - public function offsetSet($offset, $value) + public function offsetSet(mixed $offset, mixed $value):void { $this->queries[$offset] = $value; } @@ -266,7 +266,7 @@ public function offsetSet($offset, $value) /** * @inheritdoc */ - public function offsetUnset($offset) + public function offsetUnset(mixed $offset):void { unset($this->queries[$offset]); } From 338c6b6ffb5d2442d7df95cf3a548546908cae8d Mon Sep 17 00:00:00 2001 From: Claus-Justus Heine Date: Mon, 31 Jul 2023 10:05:20 +0200 Subject: [PATCH 09/15] Claim NC 27 is supported Signed-off-by: Claus-Justus Heine --- appinfo/info.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appinfo/info.xml b/appinfo/info.xml index 88aaf65..e5b075c 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -22,7 +22,7 @@ auth - + \OCA\UserSQL\Settings\Admin From df5a550fbd638ac5ecead1f94b1882fadd899ee9 Mon Sep 17 00:00:00 2001 From: Claus-Justus Heine Date: Tue, 22 Aug 2023 11:09:37 +0200 Subject: [PATCH 10/15] DataQuery: properly log exceptions. Signed-off-by: Claus-Justus Heine --- lib/Query/DataQuery.php | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/lib/Query/DataQuery.php b/lib/Query/DataQuery.php index 81e01c6..8feefd1 100644 --- a/lib/Query/DataQuery.php +++ b/lib/Query/DataQuery.php @@ -109,13 +109,13 @@ private function execQuery( } $query = $this->queryProvider[$queryName]; + try { $result = $this->connection->prepare($query, $limit, $offset); } catch (DBALException $exception) { - $this->logger->error( - "Could not prepare the query: " . $exception->getMessage(), - ["app" => $this->appName] - ); + $this->logger->logException( + $exception, [ 'message' => "Could not prepare the query: " . $query ] + ); return false; } @@ -123,20 +123,16 @@ private function execQuery( $result->bindValue(":" . $param, $value); } - $this->logger->debug( - "Executing query: " . $query . ", " . implode(",", $params), - ["app" => $this->appName] - ); + $this->logger->debug("Executing query: " . $query . ", " . implode(",", $params)); try { $result = $result->execute(); return $result; } catch (DBALException $exception) { - $this->logger->error( - "Could not execute the query: " . $exception->getMessage(), - ["app" => $this->appName] - ); + $this->logger->logException( + $exception, [ 'message' => "Could not execute the query: " . $query ] + ); return false; } } From 639b9cf9950fad63ce9a2104477aba64cccb2fdb Mon Sep 17 00:00:00 2001 From: Claus-Justus Heine Date: Tue, 22 Aug 2023 11:10:16 +0200 Subject: [PATCH 11/15] QueryProvider: use left joins and fix the use of the "disabled" column. The code used union selects; left joins at least are easier to read. In some places it was also not so clear that the WHERE part for the disabled column really worked. The query FIND_GROUP_USERS also needs to take the disabled column in to account. Signed-off-by: Claus-Justus Heine --- lib/Query/QueryProvider.php | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/lib/Query/QueryProvider.php b/lib/Query/QueryProvider.php index f4834ff..61278a3 100644 --- a/lib/Query/QueryProvider.php +++ b/lib/Query/QueryProvider.php @@ -129,10 +129,13 @@ private function loadQueries() $this->queries = [ Query::BELONGS_TO_ADMIN => "SELECT COUNT(g.$gGID) > 0 AS admin " . - "FROM $group g, $userGroup ug " . + "FROM $group g " . + "LEFT JOIN $userGroup ug ON ug.$ugGID = g.$gGID " . + (empty($uDisabled) ? "" : "LEFT JOIN $user u ON u.$uUID = ug.$ugUID ") . "WHERE ug.$ugGID = g.$gGID " . "AND ug.$ugUID = :$uidParam " . - "AND g.$gAdmin", + "AND g.$gAdmin" . + (empty($uDisabled) ? "" : " AND NOT u.$uDisabled"), Query::COUNT_GROUPS => "SELECT COUNT(DISTINCT ug.$ugUID) " . @@ -154,8 +157,10 @@ private function loadQueries() Query::FIND_GROUP_USERS => "SELECT DISTINCT ug.$ugUID AS uid " . "FROM $userGroup ug " . + "LEFT JOIN $user u ON u.$uUID = ug.$ugUID " . "WHERE ug.$ugGID LIKE :$gidParam " . "AND ug.$ugUID LIKE :$searchParam " . + (empty($uDisabled) ? "" : "AND NOT u.$uDisabled ") . "ORDER BY ug.$ugUID", Query::FIND_GROUPS => @@ -168,38 +173,38 @@ private function loadQueries() Query::FIND_USER_BY_UID => "SELECT $userColumns " . "FROM $user u " . - "WHERE u.$uUID = :$uidParam " . - (empty($uDisabled) ? "" : "AND NOT u.$uDisabled"), + "WHERE u.$uUID = :$uidParam" . + (empty($uDisabled) ? "" : " AND NOT u.$uDisabled"), Query::FIND_USER_BY_USERNAME => "SELECT $userColumns, u.$uPassword AS password " . "FROM $user u " . - "WHERE u.$uUsername = :$usernameParam " . - (empty($uDisabled) ? "" : "AND NOT u.$uDisabled"), + "WHERE u.$uUsername = :$usernameParam" . + (empty($uDisabled) ? "" : " AND NOT u.$uDisabled"), Query::FIND_USER_BY_USERNAME_CASE_INSENSITIVE => "SELECT $userColumns, u.$uPassword AS password " . "FROM $user u " . - "WHERE lower(u.$uUsername) = lower(:$usernameParam) " . - (empty($uDisabled) ? "" : "AND NOT u.$uDisabled"), + "WHERE lower(u.$uUsername) = lower(:$usernameParam)" . + (empty($uDisabled) ? "" : " AND NOT u.$uDisabled"), Query::FIND_USER_BY_USERNAME_OR_EMAIL => "SELECT $userColumns, u.$uPassword AS password " . "FROM $user u " . - "WHERE u.$uUsername = :$usernameParam OR u.$uEmail = :$emailParam " . - (empty($uDisabled) ? "" : "AND NOT u.$uDisabled"), + "WHERE u.$uUsername = :$usernameParam OR u.$uEmail = :$emailParam" . + (empty($uDisabled) ? "" : " AND NOT u.$uDisabled"), Query::FIND_USER_BY_USERNAME_OR_EMAIL_CASE_INSENSITIVE => "SELECT $userColumns, u.$uPassword AS password " . "FROM $user u " . - "WHERE lower(u.$uUsername) = lower(:$usernameParam) OR lower(u.$uEmail) = lower(:$emailParam) " . - (empty($uDisabled) ? "" : "AND NOT u.$uDisabled"), + "WHERE lower(u.$uUsername) = lower(:$usernameParam) OR lower(u.$uEmail) = lower(:$emailParam)" . + (empty($uDisabled) ? "" : " AND NOT u.$uDisabled"), Query::FIND_USER_GROUPS => "SELECT $groupColumns " . - "FROM $group g, $userGroup ug " . - "WHERE ug.$ugGID = g.$gGID " . - "AND ug.$ugUID = :$uidParam " . + "FROM $group g " . + "LEFT JOIN $userGroup ug ON ug.$ugGID = g.$gGID " . + "WHERE ug.$ugUID = :$uidParam " . "ORDER BY g.$gGID", Query::FIND_USERS => @@ -210,7 +215,7 @@ private function loadQueries() (empty($uName) ? "" : "OR u.$uName LIKE :$searchParam ") . (empty($uEmail) ? "" : "OR u.$uEmail LIKE :$searchParam ") . ")" . - (empty($uDisabled) ? "" : "AND NOT u.$uDisabled ") . + (empty($uDisabled) ? "" : " AND NOT u.$uDisabled ") . "ORDER BY u.$uUID", Query::UPDATE_DISPLAY_NAME => From 662b849ed9292da833476a8b2fe0150691d78127 Mon Sep 17 00:00:00 2001 From: Claus-Justus Heine Date: Tue, 22 Aug 2023 11:41:34 +0200 Subject: [PATCH 12/15] Fix broken MySQL example in the README and clarify the meaning of the username column. Signed-off-by: Claus-Justus Heine --- README.md | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index ee11548..a5b4d09 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ Name | Description | Details **Email sync** | Sync e-mail address with the Nextcloud.
- *None* - Disables this feature. This is the default option.
- *Synchronise only once* - Copy the e-mail address to the Nextcloud preferences if its not set.
- *Nextcloud always wins* - Always copy the e-mail address to the database. This updates the user table.
- *SQL always wins* - Always copy the e-mail address to the Nextcloud preferences. | Optional.
Default: *None*.
Requires: user *Email* column. **Quota sync** | Sync user quota with the Nextcloud.
- *None* - Disables this feature. This is the default option.
- *Synchronise only once* - Copy the user quota to the Nextcloud preferences if its not set.
- *Nextcloud always wins* - Always copy the user quota to the database. This updates the user table.
- *SQL always wins* - Always copy the user quota to the Nextcloud preferences. | Optional.
Default: *None*.
Requires: user *Quota* column. **Home mode** | User storage path.
- *Default* - Let the Nextcloud manage this. The default option.
- *Query* - Use location from the user table pointed by the *home* column.
- *Static* - Use static location pointed by the *Home Location* option. | Optional
Default: *Default*. -**Home location** | User storage path for the `Static` *Home mode*. The `%u` variable is replaced with the username of the user. | Mandatory if the *Home mode* is set to `Static`. +**Home location** | User storage path for the `Static` *Home mode*. The `%u` variable is replaced with the uid of the user. | Mandatory if the *Home mode* is set to `Static`. **Default group** | Default group for all 'User SQL' users. | Optional. #### User table @@ -74,7 +74,7 @@ Name | Description | Details --- | --- | --- **Table name** | The table name. | Mandatory for user backend. **UID** | User ID column. | Mandatory for user backend. -**Username** | Username column. | Optional. +**Username** | Username column which is used **only** for password verification. | Optional. If unsure leave it blank and use only the `uid` column. **Email** | E-mail column. | Mandatory for *Email sync* option. **Quota** | Quota column. | Mandatory for *Quota sync* option. **Home** | Home path column. | Mandatory for `Query` *Home sync* option. @@ -120,12 +120,15 @@ For all options to work three tables are required: If you already have an existing database you can always create database views which fits this model, but be aware that some functionalities requires data changes (update queries). -If you don't have any database model yet you can use below tables (MySQL): +If you don't have any database model yet you can use below tables +(MySQL). Please note that the optional `username` above really is only +used for password matching and defaults to be equal to the `uid` +column. You also may want to compare with the `oc_users` and +`oc_groups` table from you Nextcloud instance. ``` CREATE TABLE sql_user ( - uid INT PRIMARY KEY AUTO_INCREMENT, - username VARCHAR(16) NOT NULL UNIQUE, + uid VARCHAR(64) PRIMARY KEY, display_name TEXT NULL, email TEXT NULL, quota TEXT NULL, @@ -139,15 +142,15 @@ CREATE TABLE sql_user CREATE TABLE sql_group ( - gid INT PRIMARY KEY AUTO_INCREMENT, - name VARCHAR(16) NOT NULL UNIQUE, - admin BOOLEAN NOT NULL DEFAULT FALSE + gid VARCHAR(64) PRIMARY KEY, + name VARCHAR(255) NOT NULL, + admin BOOLEAN NOT NULL DEFAULT FALSE ); CREATE TABLE sql_user_group ( - uid INT NOT NULL, - gid INT NOT NULL, + uid VARCHAR(64), + gid VARCHAR(64), PRIMARY KEY (uid, gid), FOREIGN KEY (uid) REFERENCES sql_user (uid), FOREIGN KEY (gid) REFERENCES sql_group (gid), From 7da80d207a2d897e7fcb8cacd4913a91004dc699 Mon Sep 17 00:00:00 2001 From: Claus-Justus Heine Date: Tue, 22 Aug 2023 12:35:26 +0200 Subject: [PATCH 13/15] Implement ISearchableGroupBackend Signed-off-by: Claus-Justus Heine --- lib/Backend/GroupBackend.php | 61 +++++++++++++++++++++++++++--- lib/Constant/Query.php | 1 + lib/Query/DataQuery.php | 21 ++++++++++ lib/Query/QueryProvider.php | 19 +++++++--- lib/Repository/GroupRepository.php | 23 +++++++++++ 5 files changed, 114 insertions(+), 11 deletions(-) diff --git a/lib/Backend/GroupBackend.php b/lib/Backend/GroupBackend.php index c41a40d..e48a2a5 100644 --- a/lib/Backend/GroupBackend.php +++ b/lib/Backend/GroupBackend.php @@ -31,7 +31,11 @@ use OCP\Group\Backend\ICountUsersBackend; use OCP\Group\Backend\IGroupDetailsBackend; use OCP\Group\Backend\IIsAdminBackend; +use OCP\Group\Backend\ISearchableGroupBackend; use OCP\ILogger; +use OCP\IUserManager; + +use OC\User\LazyUser; /** * The SQL group backend manager. @@ -41,7 +45,8 @@ final class GroupBackend extends ABackend implements ICountUsersBackend, IGroupDetailsBackend, - IIsAdminBackend + IIsAdminBackend, + ISearchableGroupBackend { const USER_SQL_GID = "user_sql"; @@ -354,16 +359,16 @@ public function usersInGroup($gid, $search = "", $limit = -1, $offset = 0) ["app" => $this->appName] ); - $cacheKey = self::class . "group_users_" . $gid . "_" . $search . "_" + $cacheKey = self::class . "group_uids_" . $gid . "_" . $search . "_" . $limit . "_" . $offset; - $users = $this->cache->get($cacheKey); + $uids = $this->cache->get($cacheKey); - if (!is_null($users)) { + if (!is_null($uids)) { $this->logger->debug( "Returning from cache usersInGroup($gid, $search, $limit, $offset): count(" - . count($users) . ")", ["app" => $this->appName] + . count($uids) . ")", ["app" => $this->appName] ); - return $users; + return $uids; } $uids = $this->groupRepository->findAllUidsBySearchTerm( @@ -383,6 +388,50 @@ public function usersInGroup($gid, $search = "", $limit = -1, $offset = 0) return $uids; } + /** + * @inheritdoc + */ + public function searchInGroup(string $gid, string $search = '', int $limit = -1, int $offset = 0): array + { + $this->logger->debug( + "Entering searchInGroup($gid, $search, $limit, $offset)", + ["app" => $this->appName] + ); + + $cacheKey = self::class . "group_users_" . $gid . "_" . $search . "_" + . $limit . "_" . $offset; + $names = $this->cache->get($cacheKey); + + if ($names === null) { + $names = $this->groupRepository->findAllUsersBySearchTerm( + $this->substituteGid($gid), "%" . $search . "%", $limit, $offset + ); + + if ($names === false) { + return []; + } + + $this->cache->set($cacheKey, $names); + $this->logger->debug( + "Using from DB searchInGroup($gid, $search, $limit, $offset): count(" + . count($names) . ")", ["app" => $this->appName] + ); + } else { + $this->logger->debug( + "Using from cache searchInGroup($gid, $search, $limit, $offset): count(" + . count($names) . ")", ["app" => $this->appName] + ); + } + + $users = []; + $userManager = \OCP\Server::get(IUserManager::class); + foreach ($names as $uid => $name) { + $users[$uid] = new LazyUser($uid, $userManager, $name); + } + + return $users; + } + /** * @inheritdoc */ diff --git a/lib/Constant/Query.php b/lib/Constant/Query.php index 68ceff8..373e2ca 100644 --- a/lib/Constant/Query.php +++ b/lib/Constant/Query.php @@ -32,6 +32,7 @@ final class Query const COUNT_GROUPS = "count_groups"; const COUNT_USERS = "count_users"; const FIND_GROUP = "find_group"; + const FIND_GROUP_UIDS = "find_group_uids"; const FIND_GROUP_USERS = "find_group_users"; const FIND_GROUPS = "find_groups"; const FIND_USER_BY_UID = "find_user_by_uid"; diff --git a/lib/Query/DataQuery.php b/lib/Query/DataQuery.php index 8feefd1..1a7e2ae 100644 --- a/lib/Query/DataQuery.php +++ b/lib/Query/DataQuery.php @@ -223,6 +223,27 @@ public function queryColumn( return $result->fetchFirstColumn(); } + /** + * Fetch values from all columns which the given query returns. + * + * @param string $queryName The query to execute. + * @param array $params The query parameters to bind. + * @param int $limit Results limit. Defaults to -1 (no limit). + * @param int $offset Results offset. Defaults to 0. + * + * @return array|bool Queried column or FALSE on failure. + */ + public function queryColumns( + $queryName, $params = [], $limit = -1, $offset = 0 + ) { + $result = $this->execQuery($queryName, $params, $limit, $offset); + if ($result === false) { + return false; + } + + return $result->fetchAll(); + } + /** * Fetch entity returned by the given query. * diff --git a/lib/Query/QueryProvider.php b/lib/Query/QueryProvider.php index 61278a3..70323c8 100644 --- a/lib/Query/QueryProvider.php +++ b/lib/Query/QueryProvider.php @@ -154,14 +154,23 @@ private function loadQueries() "FROM $group g " . "WHERE g.$gGID = :$gidParam", + Query::FIND_GROUP_UIDS => + "SELECT DISTINCT u.$uUID AS uid " . + "FROM $user u " . + "LEFT JOIN $userGroup ug ON u.$uUID = ug.$ugUID " . + "WHERE ug.$ugGID LIKE :$gidParam " . + "AND u.$uUID LIKE :$searchParam " . + (empty($uDisabled) ? "" : "AND NOT u.$uDisabled ") . + "ORDER BY u.$uUID", + Query::FIND_GROUP_USERS => - "SELECT DISTINCT ug.$ugUID AS uid " . - "FROM $userGroup ug " . - "LEFT JOIN $user u ON u.$uUID = ug.$ugUID " . + "SELECT DISTINCT u.$uUID AS uid, u.$uName AS name " . + "FROM $user u " . + "LEFT JOIN $userGroup ug ON u.$uUID = ug.$ugUID " . "WHERE ug.$ugGID LIKE :$gidParam " . - "AND ug.$ugUID LIKE :$searchParam " . + "AND u.$uUID LIKE :$searchParam " . (empty($uDisabled) ? "" : "AND NOT u.$uDisabled ") . - "ORDER BY ug.$ugUID", + "ORDER BY u.$uUID", Query::FIND_GROUPS => "SELECT $groupColumns " . diff --git a/lib/Repository/GroupRepository.php b/lib/Repository/GroupRepository.php index f0203c3..1ac8470 100644 --- a/lib/Repository/GroupRepository.php +++ b/lib/Repository/GroupRepository.php @@ -92,10 +92,33 @@ public function findAllUidsBySearchTerm( $gid, $search = "", $limit = -1, $offset = 0 ) { return $this->dataQuery->queryColumn( + Query::FIND_GROUP_UIDS, + [Query::GID_PARAM => $gid, Query::SEARCH_PARAM => $search], $limit, + $offset + ); + } + + /** + * Get a list of all user IDs and their display-name belonging to the group. + * + * @param string $gid The group ID. + * @param string $search The UID search term. Defaults to "" (empty string). + * @param int $limit (optional) Results limit. + * Defaults to -1 (no limit). + * @param int $offset (optional) Results offset. Defaults to 0. + * + * @return array Array of display-names indexed by UIDs belonging to the group + * or FALSE on failure. + */ + public function findAllUsersBySearchTerm( + $gid, $search = "", $limit = -1, $offset = 0 + ) { + $data = $this->dataQuery->queryColumns( Query::FIND_GROUP_USERS, [Query::GID_PARAM => $gid, Query::SEARCH_PARAM => $search], $limit, $offset ); + return array_column($data, QUERY::NAME_PARAM, Query::UID_PARAM); } /** From 2d83a888f7a21fda4bac6aa8d33be61f569b7781 Mon Sep 17 00:00:00 2001 From: Claus-Justus Heine Date: Tue, 22 Aug 2023 12:45:55 +0200 Subject: [PATCH 14/15] Claim to support >= PHP 8.0 and NC 25-27 Signed-off-by: Claus-Justus Heine --- appinfo/info.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appinfo/info.xml b/appinfo/info.xml index e5b075c..78b30f4 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -21,8 +21,8 @@ auth - - + + \OCA\UserSQL\Settings\Admin From 3cc3bf294a929fe0e756e814e5c7a82a2a8349a0 Mon Sep 17 00:00:00 2001 From: Claus-Justus Heine Date: Tue, 22 Aug 2023 15:58:46 +0200 Subject: [PATCH 15/15] Do not count disabled users. Signed-off-by: Claus-Justus Heine --- lib/Query/QueryProvider.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Query/QueryProvider.php b/lib/Query/QueryProvider.php index 70323c8..13514f0 100644 --- a/lib/Query/QueryProvider.php +++ b/lib/Query/QueryProvider.php @@ -140,8 +140,10 @@ private function loadQueries() Query::COUNT_GROUPS => "SELECT COUNT(DISTINCT ug.$ugUID) " . "FROM $userGroup ug " . + (empty($uDisabled) ? "" : "LEFT JOIN $user u ON u.$uUID = ug.$ugUID ") . "WHERE ug.$ugGID LIKE :$gidParam " . - "AND ug.$ugUID LIKE :$searchParam", + "AND ug.$ugUID LIKE :$searchParam" . + (empty($uDisabled) ? "" : " AND NOT u.$uDisabled"), Query::COUNT_USERS => "SELECT COUNT(u.$uUID) AS count " .