Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 13 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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,
Expand All @@ -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),
Expand Down
4 changes: 2 additions & 2 deletions appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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="25" max-version="27"/>
</dependencies>
<settings>
<admin>\OCA\UserSQL\Settings\Admin</admin>
Expand Down
61 changes: 55 additions & 6 deletions lib/Backend/GroupBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -41,7 +45,8 @@
final class GroupBackend extends ABackend implements
ICountUsersBackend,
IGroupDetailsBackend,
IIsAdminBackend
IIsAdminBackend,
ISearchableGroupBackend
{
const USER_SQL_GID = "user_sql";

Expand Down Expand Up @@ -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(
Expand All @@ -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
*/
Expand Down
12 changes: 12 additions & 0 deletions lib/Backend/UserBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
1 change: 1 addition & 0 deletions lib/Constant/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
16 changes: 16 additions & 0 deletions lib/Controller/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down Expand Up @@ -189,6 +191,8 @@ private function getConnection()
* Save application properties.
*
* @return array The request status.
*
* @AuthorizedAdminSetting(settings=OCA\UserSQL\Settings\Admin)
*/
public function saveProperties()
{
Expand Down Expand Up @@ -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()
{
Expand Down Expand Up @@ -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()
{
Expand Down Expand Up @@ -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()
{
Expand Down Expand Up @@ -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()
{
Expand All @@ -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()
{
Expand All @@ -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()
{
Expand Down
8 changes: 4 additions & 4 deletions lib/Properties.php
Original file line number Diff line number Diff line change
Expand Up @@ -203,15 +203,15 @@ public function getArray()
/**
* @inheritdoc
*/
public function offsetExists($offset)
public function offsetExists(mixed $offset):bool
{
return isset($this->data[$offset]);
}

/**
* @inheritdoc
*/
public function offsetGet($offset)
public function offsetGet(mixed $offset):mixed
{
if (isset($this->data[$offset])) {
return $this->data[$offset];
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
43 changes: 34 additions & 9 deletions lib/Query/DataQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,26 +109,30 @@ 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->logException(
$exception, [ 'message' => "Could not prepare the query: " . $query ]
);
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->logException(
$exception, [ 'message' => "Could not execute the query: " . $query ]
);
return false;
}
}
Expand Down Expand Up @@ -219,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.
*
Expand Down
Loading