Compare commits
29 Commits
91e8cac761
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
a9f9eaf7a7
|
|||
|
|
5cc12730c5 | ||
|
|
f565705b4f | ||
|
|
cafa6bbd78 | ||
|
|
1354242744 | ||
|
|
dc5458e9a8 | ||
|
|
964260ab7a | ||
|
|
560f8cdf08 | ||
|
|
47f598d42a | ||
|
|
f6bcde7e6d | ||
|
|
1dfe332e78 | ||
|
|
c1475ce743 | ||
|
|
9d0ef4e4fe | ||
|
|
a484e1a327 | ||
|
|
3cc3bf294a | ||
|
|
2d83a888f7 | ||
|
|
7da80d207a | ||
|
|
662b849ed9 | ||
|
|
639b9cf995 | ||
|
|
df5a550fbd | ||
|
|
338c6b6ffb | ||
|
|
bb1f370e16 | ||
|
|
26930efc25 | ||
|
|
e9ccbd95d6 | ||
|
|
05360d8aa2 | ||
|
|
f1ca19cb67 | ||
|
|
b39421d32d | ||
|
|
b1819aa32e | ||
|
|
ad513bda3e |
21
README.md
21
README.md
@@ -63,7 +63,7 @@ Name | Description | Details
|
||||
**Email sync** | Sync e-mail address with the Nextcloud.<br/>- *None* - Disables this feature. This is the default option.<br/>- *Synchronise only once* - Copy the e-mail address to the Nextcloud preferences if its not set.<br/>- *Nextcloud always wins* - Always copy the e-mail address to the database. This updates the user table.<br/>- *SQL always wins* - Always copy the e-mail address to the Nextcloud preferences. | Optional.<br/>Default: *None*.<br/>Requires: user *Email* column.
|
||||
**Quota sync** | Sync user quota with the Nextcloud.<br/>- *None* - Disables this feature. This is the default option.<br/>- *Synchronise only once* - Copy the user quota to the Nextcloud preferences if its not set.<br/>- *Nextcloud always wins* - Always copy the user quota to the database. This updates the user table.<br/>- *SQL always wins* - Always copy the user quota to the Nextcloud preferences. | Optional.<br/>Default: *None*.<br/>Requires: user *Quota* column.
|
||||
**Home mode** | User storage path.<br/>- *Default* - Let the Nextcloud manage this. The default option.<br/>- *Query* - Use location from the user table pointed by the *home* column.<br/>- *Static* - Use static location pointed by the *Home Location* option. | Optional<br/>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,
|
||||
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),
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2012-2015 Andreas Böhler <dev (at) aboehler (dot) at>
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use OCA\UserSQL\AppInfo\Application;
|
||||
use OCP\AppFramework\QueryException;
|
||||
|
||||
try {
|
||||
$app = new Application();
|
||||
$app->registerBackends();
|
||||
} catch (QueryException $queryException) {
|
||||
OC::$server->getLogger()->logException($queryException);
|
||||
}
|
||||
@@ -21,8 +21,8 @@
|
||||
</types>
|
||||
<category>auth</category>
|
||||
<dependencies>
|
||||
<php min-version="7.1"/>
|
||||
<nextcloud min-version="21" max-version="23"/>
|
||||
<php min-version="8.0"/>
|
||||
<nextcloud min-version="31" max-version="31"/>
|
||||
</dependencies>
|
||||
<settings>
|
||||
<admin>\OCA\UserSQL\Settings\Admin</admin>
|
||||
|
||||
@@ -27,7 +27,7 @@ use OCA\UserSQL\Model\User;
|
||||
use OCA\UserSQL\Properties;
|
||||
use OCA\UserSQL\Repository\UserRepository;
|
||||
use OCP\IConfig;
|
||||
use OCP\ILogger;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Synchronizes the user email address.
|
||||
@@ -41,7 +41,7 @@ class EmailSync implements IUserAction
|
||||
*/
|
||||
private $appName;
|
||||
/**
|
||||
* @var ILogger The logger instance.
|
||||
* @var LoggerInterface The logger instance.
|
||||
*/
|
||||
private $logger;
|
||||
/**
|
||||
@@ -61,13 +61,13 @@ class EmailSync implements IUserAction
|
||||
* The default constructor.
|
||||
*
|
||||
* @param string $appName The application name.
|
||||
* @param ILogger $logger The logger instance.
|
||||
* @param LoggerInterface $logger The logger instance.
|
||||
* @param Properties $properties The properties array.
|
||||
* @param IConfig $config The config instance.
|
||||
* @param UserRepository $userRepository The user repository.
|
||||
*/
|
||||
public function __construct(
|
||||
$appName, ILogger $logger, Properties $properties, IConfig $config,
|
||||
$appName, LoggerInterface $logger, Properties $properties, IConfig $config,
|
||||
UserRepository $userRepository
|
||||
) {
|
||||
$this->appName = $appName;
|
||||
@@ -87,6 +87,11 @@ class EmailSync implements IUserAction
|
||||
"Entering EmailSync#doAction($user->uid)", ["app" => $this->appName]
|
||||
);
|
||||
|
||||
// enforce lowercase
|
||||
if (!empty($user->email)) {
|
||||
$user->email = strtolower($user->email);
|
||||
}
|
||||
|
||||
$ncMail = $this->config->getUserValue(
|
||||
$user->uid, "settings", "email", ""
|
||||
);
|
||||
@@ -117,7 +122,7 @@ class EmailSync implements IUserAction
|
||||
|
||||
break;
|
||||
case App::SYNC_FORCE_SQL:
|
||||
if (!empty($user->email) && $user->email !== $ncMail) {
|
||||
if (!empty($user->email) && $user->email !== strtolower($ncMail)) {
|
||||
$this->config->setUserValue(
|
||||
$user->uid, "settings", "email", $user->email
|
||||
);
|
||||
|
||||
@@ -27,7 +27,7 @@ use OCA\UserSQL\Model\User;
|
||||
use OCA\UserSQL\Properties;
|
||||
use OCA\UserSQL\Repository\UserRepository;
|
||||
use OCP\IConfig;
|
||||
use OCP\ILogger;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Synchronizes the user name.
|
||||
@@ -41,7 +41,7 @@ class NameSync implements IUserAction
|
||||
*/
|
||||
private $appName;
|
||||
/**
|
||||
* @var ILogger The logger instance.
|
||||
* @var LoggerInterface The logger instance.
|
||||
*/
|
||||
private $logger;
|
||||
/**
|
||||
@@ -61,13 +61,13 @@ class NameSync implements IUserAction
|
||||
* The default constructor.
|
||||
*
|
||||
* @param string $appName The application name.
|
||||
* @param ILogger $logger The logger instance.
|
||||
* @param LoggerInterface $logger The logger instance.
|
||||
* @param Properties $properties The properties array.
|
||||
* @param IConfig $config The config instance.
|
||||
* @param UserRepository $userRepository The user repository.
|
||||
*/
|
||||
public function __construct(
|
||||
$appName, ILogger $logger, Properties $properties, IConfig $config,
|
||||
$appName, LoggerInterface $logger, Properties $properties, IConfig $config,
|
||||
UserRepository $userRepository
|
||||
) {
|
||||
$this->appName = $appName;
|
||||
|
||||
@@ -27,7 +27,7 @@ use OCA\UserSQL\Model\User;
|
||||
use OCA\UserSQL\Properties;
|
||||
use OCA\UserSQL\Repository\UserRepository;
|
||||
use OCP\IConfig;
|
||||
use OCP\ILogger;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Synchronizes the user quota.
|
||||
@@ -41,7 +41,7 @@ class QuotaSync implements IUserAction
|
||||
*/
|
||||
private $appName;
|
||||
/**
|
||||
* @var ILogger The logger instance.
|
||||
* @var LoggerInterface The logger instance.
|
||||
*/
|
||||
private $logger;
|
||||
/**
|
||||
@@ -61,13 +61,13 @@ class QuotaSync implements IUserAction
|
||||
* The default constructor.
|
||||
*
|
||||
* @param string $appName The application name.
|
||||
* @param ILogger $logger The logger instance.
|
||||
* @param LoggerInterface $logger The logger instance.
|
||||
* @param Properties $properties The properties array.
|
||||
* @param IConfig $config The config instance.
|
||||
* @param UserRepository $userRepository The user repository.
|
||||
*/
|
||||
public function __construct(
|
||||
$appName, ILogger $logger, Properties $properties, IConfig $config,
|
||||
$appName, LoggerInterface $logger, Properties $properties, IConfig $config,
|
||||
UserRepository $userRepository
|
||||
) {
|
||||
$this->appName = $appName;
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
* @copyright 2025 Claus-Justus Heine <himself@claus-justus-heine.de>
|
||||
* @author Claus-Justus Heine <himself@claus-justus-heine.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -22,14 +24,20 @@
|
||||
namespace OCA\UserSQL\AppInfo;
|
||||
|
||||
use OCP\AppFramework\App;
|
||||
use OCP\AppFramework\QueryException;
|
||||
use OCP\AppFramework\Bootstrap\IBootContext;
|
||||
use OCP\AppFramework\Bootstrap\IBootstrap;
|
||||
use OCP\AppFramework\Bootstrap\IRegistrationContext;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\IUserManager;
|
||||
|
||||
use OCA\UserSQL\Backend;
|
||||
|
||||
/**
|
||||
* The application bootstrap class.
|
||||
*
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*/
|
||||
class Application extends App
|
||||
class Application extends App implements IBootstrap
|
||||
{
|
||||
/**
|
||||
* The class constructor.
|
||||
@@ -42,26 +50,25 @@ class Application extends App
|
||||
parent::__construct("user_sql", $urlParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the application backends
|
||||
* if all necessary configuration is provided.
|
||||
*
|
||||
* @throws QueryException If the query container's could not be resolved
|
||||
*/
|
||||
public function registerBackends()
|
||||
{
|
||||
$userBackend = $this->getContainer()->query(
|
||||
'\OCA\UserSQL\Backend\UserBackend'
|
||||
);
|
||||
$groupBackend = $this->getContainer()->query(
|
||||
'\OCA\UserSQL\Backend\GroupBackend'
|
||||
);
|
||||
/** {@inheritdoc} */
|
||||
public function register(IRegistrationContext $context): void
|
||||
{}
|
||||
|
||||
/** {@inheritdoc} */
|
||||
public function boot(IBootContext $context): void
|
||||
{
|
||||
$context->injectFn(function(
|
||||
IUserManager $userManager,
|
||||
Backend\UserBackend $userBackend,
|
||||
IGroupManager $groupManager,
|
||||
Backend\GroupBackend $groupBackend,
|
||||
) {
|
||||
if ($userBackend->isConfigured()) {
|
||||
\OC::$server->getUserManager()->registerBackend($userBackend);
|
||||
$userManager->registerBackend($userBackend);
|
||||
}
|
||||
if ($groupBackend->isConfigured()) {
|
||||
\OC::$server->getGroupManager()->addBackend($groupBackend);
|
||||
}
|
||||
$groupManager->addBackend($groupBackend);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Nextcloud - user_sql
|
||||
*
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @copyright 2022-2025 Claus-Justus Heine <himself@claus-justus-heine.de>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@@ -31,7 +32,12 @@ use OCP\Group\Backend\ABackend;
|
||||
use OCP\Group\Backend\ICountUsersBackend;
|
||||
use OCP\Group\Backend\IGroupDetailsBackend;
|
||||
use OCP\Group\Backend\IIsAdminBackend;
|
||||
use OCP\ILogger;
|
||||
use OCP\Group\Backend\ISearchableGroupBackend;
|
||||
use OCP\Group\Backend\INamedBackedn;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use OCP\IUserManager;
|
||||
|
||||
use OC\User\LazyUser;
|
||||
|
||||
/**
|
||||
* The SQL group backend manager.
|
||||
@@ -41,7 +47,8 @@ use OCP\ILogger;
|
||||
final class GroupBackend extends ABackend implements
|
||||
ICountUsersBackend,
|
||||
IGroupDetailsBackend,
|
||||
IIsAdminBackend
|
||||
IIsAdminBackend,
|
||||
ISearchableGroupBackend
|
||||
{
|
||||
const USER_SQL_GID = "user_sql";
|
||||
|
||||
@@ -50,7 +57,7 @@ final class GroupBackend extends ABackend implements
|
||||
*/
|
||||
private $appName;
|
||||
/**
|
||||
* @var ILogger The logger instance.
|
||||
* @var LoggerInterface The logger instance.
|
||||
*/
|
||||
private $logger;
|
||||
/**
|
||||
@@ -71,12 +78,12 @@ final class GroupBackend extends ABackend implements
|
||||
*
|
||||
* @param string $AppName The application name.
|
||||
* @param Cache $cache The cache instance.
|
||||
* @param ILogger $logger The logger instance.
|
||||
* @param LoggerInterface $logger The logger instance.
|
||||
* @param Properties $properties The properties array.
|
||||
* @param GroupRepository $groupRepository The group repository.
|
||||
*/
|
||||
public function __construct(
|
||||
$AppName, Cache $cache, ILogger $logger, Properties $properties,
|
||||
$AppName, Cache $cache, LoggerInterface $logger, Properties $properties,
|
||||
GroupRepository $groupRepository
|
||||
) {
|
||||
$this->appName = $AppName;
|
||||
@@ -88,6 +95,14 @@ final class GroupBackend extends ABackend implements
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getBackendName(): string
|
||||
{
|
||||
return "User SQL";
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getGroups($search = "", $limit = null, $offset = null)
|
||||
{
|
||||
@@ -354,16 +369,16 @@ final class GroupBackend extends ABackend implements
|
||||
["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(
|
||||
@@ -386,7 +401,51 @@ final class GroupBackend extends ABackend implements
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function isAdmin(string $uid = null): bool
|
||||
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
|
||||
*/
|
||||
public function isAdmin(?string $uid = null): bool
|
||||
{
|
||||
$this->logger->debug(
|
||||
"Entering isAdmin($uid)", ["app" => $this->appName]
|
||||
|
||||
@@ -37,7 +37,7 @@ use OCA\UserSQL\Repository\UserRepository;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\IConfig;
|
||||
use OCP\IL10N;
|
||||
use OCP\ILogger;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use OCP\Security\Events\ValidatePasswordPolicyEvent;
|
||||
use OCP\User\Backend\ABackend;
|
||||
use OCP\User\Backend\ICheckPasswordBackend;
|
||||
@@ -69,7 +69,7 @@ final class UserBackend extends ABackend implements
|
||||
*/
|
||||
private $appName;
|
||||
/**
|
||||
* @var ILogger The logger instance.
|
||||
* @var LoggerInterface The logger instance.
|
||||
*/
|
||||
private $logger;
|
||||
/**
|
||||
@@ -106,7 +106,7 @@ final class UserBackend extends ABackend implements
|
||||
*
|
||||
* @param string $AppName The application name.
|
||||
* @param Cache $cache The cache instance.
|
||||
* @param ILogger $logger The logger instance.
|
||||
* @param LoggerInterface $logger The logger instance.
|
||||
* @param Properties $properties The properties array.
|
||||
* @param UserRepository $userRepository The user repository.
|
||||
* @param IL10N $localization The localization service.
|
||||
@@ -114,7 +114,7 @@ final class UserBackend extends ABackend implements
|
||||
* @param IEventDispatcher $eventDispatcher The event dispatcher.
|
||||
*/
|
||||
public function __construct(
|
||||
$AppName, Cache $cache, ILogger $logger, Properties $properties,
|
||||
$AppName, Cache $cache, LoggerInterface $logger, Properties $properties,
|
||||
UserRepository $userRepository, IL10N $localization, IConfig $config,
|
||||
IEventDispatcher $eventDispatcher
|
||||
) {
|
||||
@@ -261,9 +261,13 @@ final class UserBackend extends ABackend implements
|
||||
if ($user instanceof User) {
|
||||
$this->cache->set($cacheKey, $user);
|
||||
|
||||
// avoid recursion as the action may very well call into the UserManager again ...
|
||||
$actions = $this->actions;
|
||||
$this->actions = [];
|
||||
foreach ($this->actions as $action) {
|
||||
$action->doAction($user);
|
||||
}
|
||||
$this->actions = $actions;
|
||||
}
|
||||
|
||||
return $user;
|
||||
@@ -345,9 +349,12 @@ final class UserBackend extends ABackend implements
|
||||
}
|
||||
|
||||
if ($isCorrect !== true) {
|
||||
$this->logger->info(
|
||||
$this->logger->error(
|
||||
"Invalid password attempt for user: $uid",
|
||||
["app" => $this->appName]
|
||||
[
|
||||
"app" => $this->appName,
|
||||
'exception' => new \Exception('TRACE PROVIDER'),
|
||||
]
|
||||
);
|
||||
return false;
|
||||
}
|
||||
@@ -459,6 +466,18 @@ final class UserBackend extends ABackend implements
|
||||
"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;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
* @copyright 2018 Marcin Łojewski <dev@mlojewski.me>
|
||||
* @author Marcin Łojewski <dev@mlojewski.me>
|
||||
*
|
||||
* @copyright 2025 Claus-Justus Heine <himself@claus-justus-heine.de>
|
||||
* @author Claus-Justus Heine <himself@claus-justus-heine.de>
|
||||
*
|
||||
* 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
|
||||
@@ -26,7 +29,7 @@ use OCA\UserSQL\Constant\App;
|
||||
use OCA\UserSQL\Constant\Opt;
|
||||
use OCP\ICache;
|
||||
use OCP\IConfig;
|
||||
use OCP\ILogger;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Used to store key-value pairs in the cache memory.
|
||||
@@ -39,16 +42,16 @@ class Cache
|
||||
/**
|
||||
* @var ICache The cache instance.
|
||||
*/
|
||||
private $cache;
|
||||
private $cache = null;
|
||||
|
||||
/**
|
||||
* The default constructor. Initiates the cache memory.
|
||||
*
|
||||
* @param string $AppName The application name.
|
||||
* @param IConfig $config The config instance.
|
||||
* @param ILogger $logger The logger instance.
|
||||
* @param LoggerInterface $logger The logger instance.
|
||||
*/
|
||||
public function __construct($AppName, IConfig $config, ILogger $logger)
|
||||
public function __construct($AppName, IConfig $config, LoggerInterface $logger)
|
||||
{
|
||||
$factory = \OC::$server->getMemCacheFactory();
|
||||
$useCache = $config->getAppValue(
|
||||
@@ -57,15 +60,19 @@ class Cache
|
||||
|
||||
if ($useCache === App::FALSE_VALUE) {
|
||||
$this->cache = new NullCache();
|
||||
} elseif ($factory->isAvailable()) {
|
||||
$this->cache = $factory->createDistributed();
|
||||
$logger->debug("Distributed cache initiated.", ["app" => $AppName]);
|
||||
} else {
|
||||
$logger->warning(
|
||||
"There's no distributed cache available, fallback to null cache.",
|
||||
if ($factory->isAvailable()) {
|
||||
$this->cache = $factory->createDistributed();
|
||||
}
|
||||
if ($this->cache === null || ($this->cache instanceof NullCache)) {
|
||||
$logger->debug(
|
||||
"There's no distributed cache available, fallback to capped in-memory cache.",
|
||||
["app" => $AppName]
|
||||
);
|
||||
$this->cache = new NullCache();
|
||||
$this->cache = $factory->createInMemory(128);
|
||||
} else {
|
||||
$logger->debug("Distributed cache initiated.", ["app" => $AppName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -37,7 +37,7 @@ use OCA\UserSQL\Platform\PlatformFactory;
|
||||
use OCA\UserSQL\Properties;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\IL10N;
|
||||
use OCP\ILogger;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use OCP\IRequest;
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
@@ -50,7 +50,7 @@ use ReflectionException;
|
||||
class SettingsController extends Controller
|
||||
{
|
||||
/**
|
||||
* @var ILogger The logger instance.
|
||||
* @var LoggerInterface The logger instance.
|
||||
*/
|
||||
private $logger;
|
||||
/**
|
||||
@@ -71,13 +71,13 @@ class SettingsController extends Controller
|
||||
*
|
||||
* @param string $appName The application name.
|
||||
* @param IRequest $request An instance of the request.
|
||||
* @param ILogger $logger The logger instance.
|
||||
* @param LoggerInterface $logger The logger instance.
|
||||
* @param IL10N $localization The localization service.
|
||||
* @param Properties $properties The properties array.
|
||||
* @param Cache $cache The cache instance.
|
||||
*/
|
||||
public function __construct(
|
||||
$appName, IRequest $request, ILogger $logger, IL10N $localization,
|
||||
$appName, IRequest $request, LoggerInterface $logger, IL10N $localization,
|
||||
Properties $properties, Cache $cache
|
||||
) {
|
||||
parent::__construct($appName, $request);
|
||||
@@ -92,6 +92,8 @@ class SettingsController extends Controller
|
||||
* Verify the database connection parameters.
|
||||
*
|
||||
* @return array The request status.
|
||||
*
|
||||
* @AuthorizedAdminSetting(settings=OCA\UserSQL\Settings\Admin)
|
||||
*/
|
||||
public function verifyDbConnection()
|
||||
{
|
||||
@@ -189,6 +191,8 @@ class SettingsController extends Controller
|
||||
* Save application properties.
|
||||
*
|
||||
* @return array The request status.
|
||||
*
|
||||
* @AuthorizedAdminSetting(settings=OCA\UserSQL\Settings\Admin)
|
||||
*/
|
||||
public function saveProperties()
|
||||
{
|
||||
@@ -329,6 +333,8 @@ class SettingsController extends Controller
|
||||
* Clear the application cache memory.
|
||||
*
|
||||
* @return array The request status.
|
||||
*
|
||||
* @AuthorizedAdminSetting(settings=OCA\UserSQL\Settings\Admin)
|
||||
*/
|
||||
public function clearCache()
|
||||
{
|
||||
@@ -356,6 +362,8 @@ class SettingsController extends Controller
|
||||
* Autocomplete for table select options.
|
||||
*
|
||||
* @return array The database table list.
|
||||
*
|
||||
* @AuthorizedAdminSetting(settings=OCA\UserSQL\Settings\Admin)
|
||||
*/
|
||||
public function tableAutocomplete()
|
||||
{
|
||||
@@ -376,7 +384,7 @@ class SettingsController extends Controller
|
||||
|
||||
return $tables;
|
||||
} catch (Exception $e) {
|
||||
$this->logger->logException($e);
|
||||
$this->logger->error('Error during table autocompletion', [ 'exception' => $e ]);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -385,6 +393,8 @@ class SettingsController extends Controller
|
||||
* Autocomplete for column select options - user table.
|
||||
*
|
||||
* @return array The database table's column list.
|
||||
*
|
||||
* @AuthorizedAdminSetting(settings=OCA\UserSQL\Settings\Admin)
|
||||
*/
|
||||
public function userTableAutocomplete()
|
||||
{
|
||||
@@ -421,7 +431,7 @@ class SettingsController extends Controller
|
||||
|
||||
return $columns;
|
||||
} catch (Exception $e) {
|
||||
$this->logger->logException($e);
|
||||
$this->logger->error('Error during column autocompletion', [ 'exception' => $e ]);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -430,6 +440,8 @@ class SettingsController extends Controller
|
||||
* 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 @@ class SettingsController extends Controller
|
||||
* 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 @@ class SettingsController extends Controller
|
||||
*
|
||||
* @return array Password algorithm parameters.
|
||||
* @throws ReflectionException Whenever Opt class cannot be initiated.
|
||||
*
|
||||
* @AuthorizedAdminSetting(settings=OCA\UserSQL\Settings\Admin)
|
||||
*/
|
||||
public function cryptoParams()
|
||||
{
|
||||
|
||||
@@ -25,7 +25,7 @@ use OCA\UserSQL\Constant\App;
|
||||
use OCA\UserSQL\Constant\DB;
|
||||
use OCA\UserSQL\Constant\Opt;
|
||||
use OCP\IConfig;
|
||||
use OCP\ILogger;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Store and retrieve application properties.
|
||||
@@ -48,7 +48,7 @@ class Properties implements \ArrayAccess
|
||||
*/
|
||||
private $config;
|
||||
/**
|
||||
* @var ILogger The logger instance.
|
||||
* @var LoggerInterface The logger instance.
|
||||
*/
|
||||
private $logger;
|
||||
/**
|
||||
@@ -69,11 +69,11 @@ class Properties implements \ArrayAccess
|
||||
*
|
||||
* @param string $AppName The application name.
|
||||
* @param IConfig $config The config instance.
|
||||
* @param ILogger $logger The logger instance.
|
||||
* @param LoggerInterface $logger The logger instance.
|
||||
* @param Cache $cache The cache instance.
|
||||
*/
|
||||
public function __construct(
|
||||
$AppName, IConfig $config, ILogger $logger, Cache $cache
|
||||
$AppName, IConfig $config, LoggerInterface $logger, Cache $cache
|
||||
) {
|
||||
$this->appName = $AppName;
|
||||
$this->config = $config;
|
||||
@@ -144,9 +144,7 @@ class Properties implements \ArrayAccess
|
||||
$params, array_values($reflection->getConstants())
|
||||
);
|
||||
} catch (\ReflectionException $exception) {
|
||||
$this->logger->logException(
|
||||
$exception, ["app" => $this->appName]
|
||||
);
|
||||
$this->logger->error('Unable to determine parameter names', [ 'exception'=> $exception ]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,7 +201,7 @@ class Properties implements \ArrayAccess
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
public function offsetExists(mixed $offset):bool
|
||||
{
|
||||
return isset($this->data[$offset]);
|
||||
}
|
||||
@@ -211,7 +209,7 @@ class Properties implements \ArrayAccess
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
public function offsetGet(mixed $offset):mixed
|
||||
{
|
||||
if (isset($this->data[$offset])) {
|
||||
return $this->data[$offset];
|
||||
@@ -223,7 +221,7 @@ class Properties implements \ArrayAccess
|
||||
/**
|
||||
* @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 +253,7 @@ class Properties implements \ArrayAccess
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
public function offsetUnset(mixed $offset):void
|
||||
{
|
||||
if ($offset == Opt::SAFE_STORE) {
|
||||
$this->safeStore = App::FALSE_VALUE;
|
||||
|
||||
@@ -28,7 +28,7 @@ use OC\DB\ConnectionFactory;
|
||||
use OCA\UserSQL\Constant\DB;
|
||||
use OCA\UserSQL\Constant\Query;
|
||||
use OCA\UserSQL\Properties;
|
||||
use OCP\ILogger;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Used to query a database.
|
||||
@@ -42,7 +42,7 @@ class DataQuery
|
||||
*/
|
||||
private $appName;
|
||||
/**
|
||||
* @var ILogger The logger instance.
|
||||
* @var LoggerInterface The logger instance.
|
||||
*/
|
||||
private $logger;
|
||||
/**
|
||||
@@ -62,12 +62,12 @@ class DataQuery
|
||||
* The class constructor.
|
||||
*
|
||||
* @param string $AppName The application name.
|
||||
* @param ILogger $logger The logger instance.
|
||||
* @param LoggerInterface $logger The logger instance.
|
||||
* @param Properties $properties The properties array.
|
||||
* @param QueryProvider $queryProvider The query provider.
|
||||
*/
|
||||
public function __construct(
|
||||
$AppName, ILogger $logger, Properties $properties,
|
||||
$AppName, LoggerInterface $logger, Properties $properties,
|
||||
QueryProvider $queryProvider
|
||||
) {
|
||||
$this->appName = $AppName;
|
||||
@@ -109,26 +109,26 @@ class DataQuery
|
||||
}
|
||||
|
||||
$query = $this->queryProvider[$queryName];
|
||||
|
||||
try {
|
||||
$result = $this->connection->prepare($query, $limit, $offset);
|
||||
} catch (DBALException $exception) {
|
||||
$this->logger->error('Could not prepare the query: ' . $query, [ 'exception' => $exception ]);
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($params as $param => $value) {
|
||||
$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->error('Could not execute the query: ' . $query, [ 'exception' => $exception ]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -219,6 +219,27 @@ class DataQuery
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -129,16 +129,21 @@ class QueryProvider implements \ArrayAccess
|
||||
$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(ug.$ugGID) " .
|
||||
"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 " .
|
||||
@@ -151,12 +156,23 @@ class QueryProvider implements \ArrayAccess
|
||||
"FROM $group g " .
|
||||
"WHERE g.$gGID = :$gidParam",
|
||||
|
||||
Query::FIND_GROUP_USERS =>
|
||||
"SELECT ug.$ugUID AS uid " .
|
||||
"FROM $userGroup ug " .
|
||||
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 ug.$ugUID LIKE :$searchParam " .
|
||||
"ORDER BY ug.$ugUID",
|
||||
"AND u.$uUID LIKE :$searchParam " .
|
||||
(empty($uDisabled) ? "" : "AND NOT u.$uDisabled ") .
|
||||
"ORDER BY u.$uUID",
|
||||
|
||||
Query::FIND_GROUP_USERS =>
|
||||
"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 u.$uUID LIKE :$searchParam " .
|
||||
(empty($uDisabled) ? "" : "AND NOT u.$uDisabled ") .
|
||||
"ORDER BY u.$uUID",
|
||||
|
||||
Query::FIND_GROUPS =>
|
||||
"SELECT $groupColumns " .
|
||||
@@ -197,9 +213,9 @@ class QueryProvider implements \ArrayAccess
|
||||
|
||||
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 =>
|
||||
@@ -238,7 +254,7 @@ class QueryProvider implements \ArrayAccess
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
public function offsetExists(mixed $offset):bool
|
||||
{
|
||||
return isset($this->queries[$offset]);
|
||||
}
|
||||
@@ -246,7 +262,7 @@ class QueryProvider implements \ArrayAccess
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
public function offsetGet(mixed $offset):mixed
|
||||
{
|
||||
if (isset($this->queries[$offset])) {
|
||||
return $this->queries[$offset];
|
||||
@@ -258,7 +274,7 @@ class QueryProvider implements \ArrayAccess
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
public function offsetSet(mixed $offset, mixed $value):void
|
||||
{
|
||||
$this->queries[$offset] = $value;
|
||||
}
|
||||
@@ -266,7 +282,7 @@ class QueryProvider implements \ArrayAccess
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
public function offsetUnset(mixed $offset):void
|
||||
{
|
||||
unset($this->queries[$offset]);
|
||||
}
|
||||
|
||||
@@ -92,10 +92,33 @@ class GroupRepository
|
||||
$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<string, string> 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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,14 +23,14 @@ namespace OCA\UserSQL\Settings;
|
||||
|
||||
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 <dev@mlojewski.me>
|
||||
*/
|
||||
class Admin implements ISettings
|
||||
class Admin implements IDelegatedSettings
|
||||
{
|
||||
/**
|
||||
* @var string The application name.
|
||||
@@ -76,4 +76,12 @@ class Admin implements ISettings
|
||||
{
|
||||
return 25;
|
||||
}
|
||||
|
||||
public function getName(): ?string {
|
||||
return null; // Only one setting in this section
|
||||
}
|
||||
|
||||
public function getAuthorizedAppConfig(): array {
|
||||
return []; // Custom controller
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user