From 8b86608ae1bf804e2f76b9d6b8f2ac2765d7b541 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Fri, 4 Oct 2024 09:49:08 -0300 Subject: [PATCH] feat: add support to groups Signed-off-by: Vitor Mattos --- README.md | 6 ++ appinfo/info.xml | 2 +- lib/AppInfo/Application.php | 4 ++ lib/Backend/GroupBackend.php | 136 +++++++++++++++++++++++++++++++++++ lib/Config.php | 37 ++++++++++ 5 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 lib/Backend/GroupBackend.php diff --git a/README.md b/README.md index fc1b539..90e8931 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,12 @@ This app has no user interface. All configuration is done via Nextcloud's system //'count_users' => 'SELECT COUNT (*) FROM users', //'get_home' => '', //'create_user' => 'INSERT INTO users (local, domain, password_hash) VALUES (split_part(:username, \'@\', 1), split_part(:username, \'@\', 2), :password_hash)', + // 'get_in_groups' => 'SELECT id AS gid, name AS displayname FROM groups WHERE (id ILIKE :search) OR (name ILIKE :search)', + // 'get_groups' => 'SELECT id AS gid, name AS displayname FROM groups WHERE (id ILIKE :search) OR (name ILIKE :search)', + // 'get_user_groups' => "SELECT u.user_login FROM groups g JOIN users u ON g.user_id = u.id WHERE u.user_login = :username", + // 'get_user_in_group' => 'SELECT id AS gid, name AS displayname FROM groups WHERE (id ILIKE :search) OR (name ILIKE :search)', + // 'get_group_exists' => "SELECT EXISTS(SELECT 1 FROM groups WHERE g.name = :group)", + // 'get_group_details' => "SELECT g.name FROM groups WHERE name = :group", ), //'hash_algorithm_for_new_passwords' => 'bcrypt', ), diff --git a/appinfo/info.xml b/appinfo/info.xml index 37b3936..5b7311e 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -42,6 +42,6 @@ these databases. This has not been tested, though.]]> https://github.com/PanCakeConnaisseur/user_backend_sql_raw https://raw.githubusercontent.com/PanCakeConnaisseur/user_backend_sql_raw/2eb5221f0725a9ab09fde6384dea62463c7c52e5/screenshot-dark-large.jpg - + diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index f834b66..69894bc 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -27,6 +27,7 @@ use OCP\AppFramework\Bootstrap\IRegistrationContext; use Psr\Container\ContainerInterface; use \OCP\AppFramework\App; +use OCP\Server; class Application extends App implements IBootstrap { @@ -46,5 +47,8 @@ public function boot(IBootContext $context): void $userBackendSqlRaw = $context->getAppContainer()->get(\OCA\UserBackendSqlRaw\UserBackend::class); $userManager = $context->getAppContainer()->get('OCP\IUserManager'); $userManager->registerBackend($userBackendSqlRaw); + + $groupBackend = $context->getAppContainer()->get(\OCA\UserBackendSqlRaw\Backend\GroupBackend::class); + Server::get(\OCP\IGroupManager::class)->addBackend($groupBackend); } } diff --git a/lib/Backend/GroupBackend.php b/lib/Backend/GroupBackend.php new file mode 100644 index 0000000..ac424d0 --- /dev/null +++ b/lib/Backend/GroupBackend.php @@ -0,0 +1,136 @@ + + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + +namespace OCA\UserBackendSqlRaw\Backend; + +use OCA\UserBackendSqlRaw\Config; +use OCA\UserBackendSqlRaw\Db; +use OCP\Group\Backend\ABackend; +use OCP\Group\Backend\IGroupDetailsBackend; +use Psr\Log\LoggerInterface; + +class GroupBackend extends ABackend implements IGroupDetailsBackend +{ + public function __construct( + private LoggerInterface $logger, + private Config $config, + private Db $db, + ) { + } + + public function inGroup($uid, $gid): bool + { + $queryFromConfig = $this->config->getQueryInGroup(); + if (empty($queryFromConfig)) { + return false; + } + $statement = $this->db->getDbHandle()->prepare($queryFromConfig); + $statement->execute(['username' => $uid, 'group' => $gid]); + $inGroup = $statement->fetchColumn(); + return (bool) $inGroup; + } + + public function getUserGroups($uid) + { + $queryFromConfig = $this->config->getQueryUserGroups(); + if (empty($queryFromConfig)) { + return []; + } + $statement = $this->db->getDbHandle()->prepare($queryFromConfig); + $statement->execute(['username' => $uid]); + $groups = $statement->fetchAll(\PDO::FETCH_COLUMN, 0); + return $groups; + } + + public function getGroups($search = '', $limit = -1, $offset = 0) + { + $queryFromConfig = $this->config->getQueryGroups(); + if (empty($queryFromConfig)) { + return []; + } + isset($limit) && $limit > 0 ? $limitSegment = ' LIMIT :limit' : $limitSegment = ''; + isset($offset) && $offset > 0? $offsetSegment = ' OFFSET :offset' : $offsetSegment = ''; + $finalQuery = $queryFromConfig . $limitSegment . $offsetSegment; + $statement = $this->db->getDbHandle()->prepare($finalQuery); + // Because MariaDB can not handle string parameters for LIMIT/OFFSET we have to bind the + // values "manually" instead of passing an array to execute(). This is another instance of + // MariaDB making the code "uglier". + $statement->bindValue(':search', '%' . $search . '%', \PDO::PARAM_STR); + if (isset($limit) && $limit > 0) { + $statement->bindValue(':limit', intval($limit), \PDO::PARAM_INT); + } + if (isset($offset) && $offset > 0) { + $statement->bindValue(':offset', intval($offset), \PDO::PARAM_INT); + } + $statement->execute(['search' => $search]); + $groups = $statement->fetchAll(\PDO::FETCH_COLUMN, 0); + return $groups; + } + + public function groupExists($gid) + { + $queryFromConfig = $this->config->getQueryGroupExists(); + if (empty($queryFromConfig)) { + return false; + } + $statement = $this->db->getDbHandle()->prepare($queryFromConfig); + $statement->execute(['group' => $gid]); + $groupExists = $statement->fetchColumn(); + return (bool) $groupExists; + } + + public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) + { + $queryFromConfig = $this->config->getQueryUsersInGroup(); + if (empty($queryFromConfig)) { + return []; + } + isset($limit) && $limit > 0 ? $limitSegment = ' LIMIT :limit' : $limitSegment = ''; + isset($offset) && $offset > 0? $offsetSegment = ' OFFSET :offset' : $offsetSegment = ''; + $finalQuery = $queryFromConfig . $limitSegment . $offsetSegment; + $statement = $this->db->getDbHandle()->prepare($finalQuery); + // Because MariaDB can not handle string parameters for LIMIT/OFFSET we have to bind the + // values "manually" instead of passing an array to execute(). This is another instance of + // MariaDB making the code "uglier". + $statement->bindValue(':search', '%' . $search . '%', \PDO::PARAM_STR); + if (isset($limit) && $limit > 0) { + $statement->bindValue(':limit', intval($limit), \PDO::PARAM_INT); + } + if (isset($offset) && $offset > 0) { + $statement->bindValue(':offset', intval($offset), \PDO::PARAM_INT); + } + $statement->execute(['group' => $gid, 'search' => $search]); + $groups = $statement->fetchAll(\PDO::FETCH_COLUMN, 0); + return $groups; + } + + public function getGroupDetails(string $gid): array + { + $queryFromConfig = $this->config->getQueryGroupDetails(); + if (empty($queryFromConfig)) { + return false; + } + $statement = $this->db->getDbHandle()->prepare($queryFromConfig); + $statement->execute(['group' => $gid]); + $groupDetails = $statement->fetchColumn(); + return ['displayName' => $groupDetails]; + } +} diff --git a/lib/Config.php b/lib/Config.php index cbd0191..0b27d19 100644 --- a/lib/Config.php +++ b/lib/Config.php @@ -48,6 +48,13 @@ class Config const CONFIG_KEY_GET_HOME = 'get_home'; const CONFIG_KEY_CREATE_USER = 'create_user'; + const CONFIG_KEY_IN_GROUPS = 'get_in_groups'; + const CONFIG_KEY_GROUPS = 'get_groups'; + const CONFIG_KEY_USER_GROUPS = 'get_user_groups'; + const CONFIG_KEY_USERS_IN_GROUP = 'get_user_in_group'; + const CONFIG_KEY_GROUP_EXISTS = 'get_group_exists'; + const CONFIG_KEY_GROUP_DETAILS = 'get_group_details'; + /* @var LoggerInterface */ private $logger; private $appConfiguration; @@ -235,6 +242,36 @@ public function getQueryCreateUser() return $this->getQueryStringOrFalse(self::CONFIG_KEY_CREATE_USER); } + public function getQueryInGroup() + { + return $this->getQueryStringOrFalse(self::CONFIG_KEY_IN_GROUPS); + } + + public function getQueryGroups() + { + return $this->getQueryStringOrFalse(self::CONFIG_KEY_GROUPS); + } + + public function getQueryUserGroups() + { + return $this->getQueryStringOrFalse(self::CONFIG_KEY_USER_GROUPS); + } + + public function getQueryUsersInGroup() + { + return $this->getQueryStringOrFalse(self::CONFIG_KEY_USERS_IN_GROUP); + } + + public function getQueryGroupExists() + { + return $this->getQueryStringOrFalse(self::CONFIG_KEY_GROUP_EXISTS); + } + + public function getQueryGroupDetails() + { + return $this->getQueryStringOrFalse(self::CONFIG_KEY_GROUP_DETAILS); + } + /**