User quota from SQL

This commit is contained in:
Marcin Łojewski
2018-07-09 19:21:53 +02:00
parent 49a9b2ed61
commit ed37c7085d
14 changed files with 178 additions and 9 deletions

View File

@@ -10,9 +10,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- WoltLab Community Framework 2.x hash algorithm - WoltLab Community Framework 2.x hash algorithm
- phpass hash implementation - phpass hash implementation
- Support for salt column - Support for salt column
- User quota synchronization
### Changed ### Changed
- Example SQL script in README file - Example SQL script in README file
- Fixed misspelling
### Fixed ### Fixed
- Table and column autocomplete in settings panel - Table and column autocomplete in settings panel

View File

@@ -51,7 +51,8 @@ Name | Description | Details
**Allow password change** | Can user change its password. The password change is propagated to the database. See [Hash algorithms](#hash-algorithms). | Optional.<br/>Default: false. **Allow password change** | Can user change its password. The password change is propagated to the database. See [Hash algorithms](#hash-algorithms). | Optional.<br/>Default: false.
**Use cache** | Use database query results cache. The cache can be cleared any time with the *Clear cache* button click. | Optional.<br/>Default: false. **Use cache** | Use database query results cache. The cache can be cleared any time with the *Clear cache* button click. | Optional.<br/>Default: false.
**Hash algorithm** | How users passwords are stored in the database. See [Hash algorithms](#hash-algorithms). | Mandatory. **Hash algorithm** | How users passwords are stored in the database. See [Hash algorithms](#hash-algorithms). | Mandatory.
**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 storage 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 storage. | Optional.<br/>Default: *None*.<br/>Requires: user *Email* column. **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. The `%u` variable is replaced with the username of the user. | Optional<br/>Default: *Default*. **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. The `%u` variable is replaced with the username of the user. | Optional<br/>Default: *Default*.
**Home Location** | User storage path for the `static` *home mode*. | Mandatory if the *Home mode* is set to `Static`. **Home Location** | User storage path for the `static` *home mode*. | Mandatory if the *Home mode* is set to `Static`.
@@ -64,6 +65,7 @@ Name | Description | Details
**Table name** | The table name. | Mandatory for user backend. **Table name** | The table name. | Mandatory for user backend.
**Username** | Username column. | Mandatory for user backend. **Username** | Username column. | Mandatory for user backend.
**Email** | E-mail column. | Mandatory for *Email sync* option. **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. **Home** | Home path column. | Mandatory for `Query` *Home sync* option.
**Password** | Password hash column. | Mandatory for user backend. **Password** | Password hash column. | Mandatory for user backend.
**Display name** | Display name column. | Optional. **Display name** | Display name column. | Optional.
@@ -111,6 +113,7 @@ CREATE TABLE sql_user
username VARCHAR(16) PRIMARY KEY, username VARCHAR(16) PRIMARY KEY,
display_name TEXT NULL, display_name TEXT NULL,
email TEXT NULL, email TEXT NULL,
quota TEXT NULL,
home TEXT NULL, home TEXT NULL,
password TEXT NOT NULL, password TEXT NOT NULL,
active TINYINT(1) NOT NULL DEFAULT '1', active TINYINT(1) NOT NULL DEFAULT '1',

View File

@@ -76,7 +76,7 @@ user_sql.adminSettingsUI = function () {
); );
autocomplete( autocomplete(
"#db-table-user-column-uid, #db-table-user-column-email, #db-table-user-column-home, #db-table-user-column-password, #db-table-user-column-name, #db-table-user-column-active, #db-table-user-column-avatar, #db-table-user-column-salt", "#db-table-user-column-uid, #db-table-user-column-email, #db-table-user-column-quota, #db-table-user-column-home, #db-table-user-column-password, #db-table-user-column-name, #db-table-user-column-active, #db-table-user-column-avatar, #db-table-user-column-salt",
"/apps/user_sql/settings/autocomplete/table/user" "/apps/user_sql/settings/autocomplete/table/user"
); );

View File

@@ -94,7 +94,7 @@ class EmailSync implements IUserAction
$result = false; $result = false;
switch ($this->properties[Opt::EMAIL_SYNC]) { switch ($this->properties[Opt::EMAIL_SYNC]) {
case App::EMAIL_INITIAL: case App::SYNC_INITIAL:
if (empty($ncMail) && !empty($user->email)) { if (empty($ncMail) && !empty($user->email)) {
$this->config->setUserValue( $this->config->setUserValue(
$user->uid, "settings", "email", $user->email $user->uid, "settings", "email", $user->email
@@ -103,7 +103,7 @@ class EmailSync implements IUserAction
$result = true; $result = true;
break; break;
case App::EMAIL_FORCE_NC: case App::SYNC_FORCE_NC:
if (!empty($ncMail) && $user->email !== $ncMail) { if (!empty($ncMail) && $user->email !== $ncMail) {
$user = $this->userRepository->findByUid($user->uid); $user = $this->userRepository->findByUid($user->uid);
if (!($user instanceof User)) { if (!($user instanceof User)) {
@@ -115,7 +115,7 @@ class EmailSync implements IUserAction
} }
break; break;
case App::EMAIL_FORCE_SQL: case App::SYNC_FORCE_SQL:
if (!empty($user->email) && $user->email !== $ncMail) { if (!empty($user->email) && $user->email !== $ncMail) {
$this->config->setUserValue( $this->config->setUserValue(
$user->uid, "settings", "email", $user->email $user->uid, "settings", "email", $user->email

137
lib/Action/QuotaSync.php Normal file
View File

@@ -0,0 +1,137 @@
<?php
/**
* Nextcloud - user_sql
*
* @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/>.
*/
namespace OCA\UserSQL\Action;
use OCA\UserSQL\Constant\App;
use OCA\UserSQL\Constant\Opt;
use OCA\UserSQL\Model\User;
use OCA\UserSQL\Properties;
use OCA\UserSQL\Repository\UserRepository;
use OCP\IConfig;
use OCP\ILogger;
/**
* Synchronizes the user quota.
*
* @author Marcin Łojewski <dev@mlojewski.me>
*/
class QuotaSync implements IUserAction
{
/**
* @var string The application name.
*/
private $appName;
/**
* @var ILogger The logger instance.
*/
private $logger;
/**
* @var Properties The properties array.
*/
private $properties;
/**
* @var IConfig The config instance.
*/
private $config;
/**
* @var UserRepository The user repository.
*/
private $userRepository;
/**
* The default constructor.
*
* @param string $appName The application name.
* @param ILogger $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,
UserRepository $userRepository
) {
$this->appName = $appName;
$this->logger = $logger;
$this->properties = $properties;
$this->config = $config;
$this->userRepository = $userRepository;
}
/**
* @inheritdoc
* @throws \OCP\PreConditionNotMetException
*/
public function doAction(User $user)
{
$this->logger->debug(
"Entering QuotaSync#doAction($user->uid)", ["app" => $this->appName]
);
$ncQuota = $this->config->getUserValue(
$user->uid, "files", "quota", ""
);
$result = false;
switch ($this->properties[Opt::QUOTA_SYNC]) {
case App::SYNC_INITIAL:
if (empty($ncQuota) && !empty($user->quota)) {
$this->config->setUserValue(
$user->uid, "files", "quota", $user->quota
);
}
$result = true;
break;
case App::SYNC_FORCE_NC:
if (!empty($ncQuota) && $user->quota !== $ncQuota) {
$user = $this->userRepository->findByUid($user->uid);
if (!($user instanceof User)) {
break;
}
$user->quota = $ncQuota;
$result = $this->userRepository->save($user);
}
break;
case App::SYNC_FORCE_SQL:
if (!empty($user->quota) && $user->quota !== $ncQuota) {
$this->config->setUserValue(
$user->uid, "files", "quota", $user->quota
);
}
$result = true;
break;
}
$this->logger->debug(
"Returning QuotaSync#doAction($user->uid): " . ($result ? "true"
: "false"),
["app" => $this->appName]
);
return $result;
}
}

View File

@@ -24,6 +24,7 @@ namespace OCA\UserSQL\Backend;
use OC\User\Backend; use OC\User\Backend;
use OCA\UserSQL\Action\EmailSync; use OCA\UserSQL\Action\EmailSync;
use OCA\UserSQL\Action\IUserAction; use OCA\UserSQL\Action\IUserAction;
use OCA\UserSQL\Action\QuotaSync;
use OCA\UserSQL\Cache; use OCA\UserSQL\Cache;
use OCA\UserSQL\Constant\App; use OCA\UserSQL\Constant\App;
use OCA\UserSQL\Constant\DB; use OCA\UserSQL\Constant\DB;
@@ -116,6 +117,14 @@ final class UserBackend extends Backend
$this->userRepository $this->userRepository
); );
} }
if (!empty($this->properties[Opt::QUOTA_SYNC])
&& !empty($this->properties[DB::USER_QUOTA_COLUMN])
) {
$this->actions[] = new QuotaSync(
$this->appName, $this->logger, $this->properties, $this->config,
$this->userRepository
);
}
} }
/** /**

View File

@@ -34,7 +34,7 @@ final class App
const HOME_QUERY = "query"; const HOME_QUERY = "query";
const HOME_STATIC = "static"; const HOME_STATIC = "static";
const EMAIL_FORCE_NC = "force_nc"; const SYNC_FORCE_NC = "force_nc";
const EMAIL_FORCE_SQL = "force_sql"; const SYNC_FORCE_SQL = "force_sql";
const EMAIL_INITIAL = "initial"; const SYNC_INITIAL = "initial";
} }

View File

@@ -51,6 +51,7 @@ final class DB
const USER_HOME_COLUMN = "db.table.user.column.home"; const USER_HOME_COLUMN = "db.table.user.column.home";
const USER_NAME_COLUMN = "db.table.user.column.name"; const USER_NAME_COLUMN = "db.table.user.column.name";
const USER_PASSWORD_COLUMN = "db.table.user.column.password"; const USER_PASSWORD_COLUMN = "db.table.user.column.password";
const USER_QUOTA_COLUMN = "db.table.user.column.quota";
const USER_SALT_COLUMN = "db.table.user.column.salt"; const USER_SALT_COLUMN = "db.table.user.column.salt";
const USER_UID_COLUMN = "db.table.user.column.uid"; const USER_UID_COLUMN = "db.table.user.column.uid";
} }

View File

@@ -34,5 +34,6 @@ final class Opt
const HOME_MODE = "opt.home_mode"; const HOME_MODE = "opt.home_mode";
const NAME_CHANGE = "opt.name_change"; const NAME_CHANGE = "opt.name_change";
const PASSWORD_CHANGE = "opt.password_change"; const PASSWORD_CHANGE = "opt.password_change";
const QUOTA_SYNC = "opt.quota_sync";
const USE_CACHE = "opt.use_cache"; const USE_CACHE = "opt.use_cache";
} }

View File

@@ -39,9 +39,11 @@ final class Query
const FIND_USERS = "find_users"; const FIND_USERS = "find_users";
const SAVE_USER = "save_user"; const SAVE_USER = "save_user";
const EMAIL_PARAM = "email";
const GID_PARAM = "gid"; const GID_PARAM = "gid";
const NAME_PARAM = "name"; const NAME_PARAM = "name";
const PASSWORD_PARAM = "password"; const PASSWORD_PARAM = "password";
const QUOTA_PARAM = "quota";
const SEARCH_PARAM = "search"; const SEARCH_PARAM = "search";
const UID_PARAM = "uid"; const UID_PARAM = "uid";
} }

View File

@@ -36,6 +36,10 @@ class User
* @var string The user's email address. * @var string The user's email address.
*/ */
public $email; public $email;
/**
* @var string The user quota.
*/
public $quota;
/** /**
* @var string The user's display name. * @var string The user's display name.
*/ */

View File

@@ -71,15 +71,18 @@ class QueryProvider implements \ArrayAccess
$uHome = $this->properties[DB::USER_HOME_COLUMN]; $uHome = $this->properties[DB::USER_HOME_COLUMN];
$uName = $this->properties[DB::USER_NAME_COLUMN]; $uName = $this->properties[DB::USER_NAME_COLUMN];
$uPassword = $this->properties[DB::USER_PASSWORD_COLUMN]; $uPassword = $this->properties[DB::USER_PASSWORD_COLUMN];
$uQuota = $this->properties[DB::USER_QUOTA_COLUMN];
$uSalt = $this->properties[DB::USER_SALT_COLUMN]; $uSalt = $this->properties[DB::USER_SALT_COLUMN];
$uUID = $this->properties[DB::USER_UID_COLUMN]; $uUID = $this->properties[DB::USER_UID_COLUMN];
$ugGID = $this->properties[DB::USER_GROUP_GID_COLUMN]; $ugGID = $this->properties[DB::USER_GROUP_GID_COLUMN];
$ugUID = $this->properties[DB::USER_GROUP_UID_COLUMN]; $ugUID = $this->properties[DB::USER_GROUP_UID_COLUMN];
$emailParam = Query::EMAIL_PARAM;
$gidParam = Query::GID_PARAM; $gidParam = Query::GID_PARAM;
$nameParam = Query::NAME_PARAM; $nameParam = Query::NAME_PARAM;
$passwordParam = Query::PASSWORD_PARAM; $passwordParam = Query::PASSWORD_PARAM;
$quotaParam = Query::QUOTA_PARAM;
$searchParam = Query::SEARCH_PARAM; $searchParam = Query::SEARCH_PARAM;
$uidParam = Query::UID_PARAM; $uidParam = Query::UID_PARAM;
@@ -91,6 +94,7 @@ class QueryProvider implements \ArrayAccess
= "$uUID AS uid, " . = "$uUID AS uid, " .
(empty($uName) ? "null" : $uName) . " AS name, " . (empty($uName) ? "null" : $uName) . " AS name, " .
(empty($uEmail) ? "null" : $uEmail) . " AS email, " . (empty($uEmail) ? "null" : $uEmail) . " AS email, " .
(empty($uQuota) ? "null" : $uQuota) . " AS quota, " .
(empty($uHome) ? "null" : $uHome) . " AS home, " . (empty($uHome) ? "null" : $uHome) . " AS home, " .
(empty($uActive) ? "true" : $uActive) . " AS active, " . (empty($uActive) ? "true" : $uActive) . " AS active, " .
(empty($uAvatar) ? "false" : $uAvatar) . " AS avatar, " . (empty($uAvatar) ? "false" : $uAvatar) . " AS avatar, " .
@@ -156,7 +160,9 @@ class QueryProvider implements \ArrayAccess
Query::SAVE_USER => Query::SAVE_USER =>
"UPDATE $user " . "UPDATE $user " .
"SET $uPassword = :$passwordParam, " . "SET $uPassword = :$passwordParam, " .
"$uName = :$nameParam " . "$uName = :$nameParam, " .
"$uEmail = :$emailParam, " .
"$uQuota = :$quotaParam " .
"WHERE $uUID = :$uidParam", "WHERE $uUID = :$uidParam",
]; ];
} }

View File

@@ -107,6 +107,8 @@ class UserRepository
Query::SAVE_USER, [ Query::SAVE_USER, [
Query::NAME_PARAM => $user->name, Query::NAME_PARAM => $user->name,
Query::PASSWORD_PARAM => $user->password, Query::PASSWORD_PARAM => $user->password,
Query::EMAIL_PARAM => $user->email,
Query::QUOTA_PARAM => $user->quota,
Query::UID_PARAM => $user->uid Query::UID_PARAM => $user->uid
] ]
); );

View File

@@ -131,6 +131,7 @@ function print_select_options(
print_select_options($l, "opt-crypto_class", "Hash algorithm", $hashes, $_['opt.crypto_class']); print_select_options($l, "opt-crypto_class", "Hash algorithm", $hashes, $_['opt.crypto_class']);
print_select_options($l, "opt-email_sync", "Email sync", ["" => "None", "initial" => "Synchronise only once", "force_nc"=>"Nextcloud always wins", "force_sql"=>"SQL always wins"], $_['opt.email_sync']); print_select_options($l, "opt-email_sync", "Email sync", ["" => "None", "initial" => "Synchronise only once", "force_nc"=>"Nextcloud always wins", "force_sql"=>"SQL always wins"], $_['opt.email_sync']);
print_select_options($l, "opt-quota_sync", "Quota sync", ["" => "None", "initial" => "Synchronise only once", "force_nc"=>"Nextcloud always wins", "force_sql"=>"SQL always wins"], $_['opt.quota_sync']);
print_select_options($l, "opt-home_mode", "Home mode", ["" => "Default", "query" => "Query", "static" => "Static"], $_['opt.home_mode']); print_select_options($l, "opt-home_mode", "Home mode", ["" => "Default", "query" => "Query", "static" => "Static"], $_['opt.home_mode']);
print_text_input($l, "opt-home_location", "Home Location", $_['opt.home_location']); ?> print_text_input($l, "opt-home_location", "Home Location", $_['opt.home_location']); ?>
</fieldset> </fieldset>
@@ -144,6 +145,7 @@ function print_select_options(
<?php <?php
print_text_input($l, "db-table-user-column-uid", "Username", $_['db.table.user.column.uid']); print_text_input($l, "db-table-user-column-uid", "Username", $_['db.table.user.column.uid']);
print_text_input($l, "db-table-user-column-email", "Email", $_['db.table.user.column.email']); print_text_input($l, "db-table-user-column-email", "Email", $_['db.table.user.column.email']);
print_text_input($l, "db-table-user-column-quota", "Quota", $_['db.table.user.column.quota']);
print_text_input($l, "db-table-user-column-home", "Home", $_['db.table.user.column.home']); print_text_input($l, "db-table-user-column-home", "Home", $_['db.table.user.column.home']);
print_text_input($l, "db-table-user-column-password", "Password", $_['db.table.user.column.password']); print_text_input($l, "db-table-user-column-password", "Password", $_['db.table.user.column.password']);
print_text_input($l, "db-table-user-column-name", "Display name", $_['db.table.user.column.name']); print_text_input($l, "db-table-user-column-name", "Display name", $_['db.table.user.column.name']);