mergre current develop

This commit is contained in:
Marcin Łojewski
2018-12-22 19:37:05 +01:00
33 changed files with 501 additions and 91 deletions

View File

@@ -4,6 +4,25 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- Reverse active column option
- Support for Nextcloud 16
## [4.2.1] - 2018-12-22
### Fixed
- SQL error when same column names given in several tables
## [4.2.0] - 2018-12-16
### Added
- Support for Nextcloud 15
- Redmine, SHA-256, SHA-512 hash algorithms
### Fixed
- Loading user list when display name is null
- Hide "password change form" when "Allow password change" not set
### Changed
- Append salt only when checked. Not by default
## [4.1.0] - 2018-10-28 ## [4.1.0] - 2018-10-28
### Added ### Added
- Whirlpool hash algorithm - Whirlpool hash algorithm
@@ -98,6 +117,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Changed ### Changed
- Supported version of ownCloud, Nextcloud: ownCloud 10, Nextcloud 12 - Supported version of ownCloud, Nextcloud: ownCloud 10, Nextcloud 12
[Unreleased]: https://github.com/nextcloud/user_sql/compare/v4.2.1...develop
[4.2.1]: https://github.com/nextcloud/user_sql/compare/v4.2.0...v4.2.1
[4.2.0]: https://github.com/nextcloud/user_sql/compare/v4.1.0...v4.2.0
[4.1.0]: https://github.com/nextcloud/user_sql/compare/v4.0.1...v4.1.0 [4.1.0]: https://github.com/nextcloud/user_sql/compare/v4.0.1...v4.1.0
[4.0.1]: https://github.com/nextcloud/user_sql/compare/v4.0.0...v4.0.1 [4.0.1]: https://github.com/nextcloud/user_sql/compare/v4.0.0...v4.0.1
[4.0.0]: https://github.com/nextcloud/user_sql/compare/v4.0.0-rc2...v4.0.0 [4.0.0]: https://github.com/nextcloud/user_sql/compare/v4.0.0-rc2...v4.0.0

View File

@@ -50,6 +50,7 @@ Name | Description | Details
**Allow display name change** | With this option enabled user can change its display name. The display name change is propagated to the database. | Optional.<br/>Default: false.<br/>Requires: user *Display name* column. **Allow display name change** | With this option enabled user can change its display name. The display name change is propagated to the database. | Optional.<br/>Default: false.<br/>Requires: user *Display name* column.
**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.
**Case-insensitive username** | Whether user query should be case-sensitive or case-insensitive. | Optional.<br/>Default: false. **Case-insensitive username** | Whether user query should be case-sensitive or case-insensitive. | Optional.<br/>Default: false.
**Reverse active column** | Reverse value of active column in user table. | 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 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. **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.
@@ -73,7 +74,8 @@ Name | Description | Details
**Active** | Flag indicating if user can log in. | Optional.<br/>Default: true. **Active** | Flag indicating if user can log in. | Optional.<br/>Default: true.
**Provide avatar** | Flag indicating if user can change its avatar. | Optional.<br/>Default: false. **Provide avatar** | Flag indicating if user can change its avatar. | Optional.<br/>Default: false.
**Salt** | Salt which is appended to password when checking or changing the password. | Optional. **Salt** | Salt which is appended to password when checking or changing the password. | Optional.
**Prepend salt** | Prepend a salt to the password instead of appending it. | Optional.<br/>Default: false. **Append salt** | Append a salt to the password. | Optional.<br/>Default: false.
**Prepend salt** | Prepend a salt to the password. | Optional.<br/>Default: false.
#### Group table #### Group table
@@ -119,7 +121,8 @@ CREATE TABLE sql_user
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',
provide_avatar BOOLEAN NOT NULL DEFAULT FALSE provide_avatar BOOLEAN NOT NULL DEFAULT FALSE,
salt TEXT NULL
); );
CREATE TABLE sql_group CREATE TABLE sql_group
@@ -195,8 +198,11 @@ Drupal 7 | See [phpass](http://www.openwall.com/phpass/). | $S$DC7eCpJQ3SUQtW4Bp
Joomla MD5 Encryption | Generates 32 chars salt. | 14d21b49b0f13e2acba962b6b0039edd:haJK0yTvBXTNMh76xwEw5RYEVpJsN8us Joomla MD5 Encryption | Generates 32 chars salt. | 14d21b49b0f13e2acba962b6b0039edd:haJK0yTvBXTNMh76xwEw5RYEVpJsN8us
MD5 | No salt supported. | 5f4dcc3b5aa765d61d8327deb882cf99 MD5 | No salt supported. | 5f4dcc3b5aa765d61d8327deb882cf99
Portable PHP password | See [phpass](http://www.openwall.com/phpass/). | $P$BxrwraqNTi4as0EI.IpiA/K.muk9ke/ Portable PHP password | See [phpass](http://www.openwall.com/phpass/). | $P$BxrwraqNTi4as0EI.IpiA/K.muk9ke/
SHA1 | No salt supported. | 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8 Redmine | Requires salt. Salt value for hash in the next column is 'salt'. | 48b75edeffd8e413341d7734f0f3391e7a5da994
SHA512 Whirlpool | No salt supported. | a96b16ebb691dbe968b0d66d0d924cff5cf5de5e0885181d00761d87f295b2bf3d3c66187c050fc01c196ff3acaa48d3561ffd170413346e934a32280d632f2e SHA-1 | No salt supported. | 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8
SHA-256 | No salt supported. | 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
SHA-512 | No salt supported. | b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86
SHA-512 Whirlpool | No salt supported. | a96b16ebb691dbe968b0d66d0d924cff5cf5de5e0885181d00761d87f295b2bf3d3c66187c050fc01c196ff3acaa48d3561ffd170413346e934a32280d632f2e
SSHA256 | Generates 32 chars salt. | {SSHA256}+WxTB3JxprNteeovsuSYtgI+UkVPA9lfwGoYkz3Ff7hjd1FSdmlTMkNsSExyR21KM3NvNTZ5V0p4WXJMUjFzUg== SSHA256 | Generates 32 chars salt. | {SSHA256}+WxTB3JxprNteeovsuSYtgI+UkVPA9lfwGoYkz3Ff7hjd1FSdmlTMkNsSExyR21KM3NvNTZ5V0p4WXJMUjFzUg==
SSHA512 | Generates 32 chars salt. | {SSHA512}It+v1kAEUBbhMJYJ2swAtz+RLE6ispv/FB6G/ALhK/YWwEmrloY+0jzrWIfmu+rWUXp8u0Tg4jLXypC5oXAW00IyYnRVdEZJbE9wak96bkNRVWFCYmlJNWxrdTA0QmhL SSHA512 | Generates 32 chars salt. | {SSHA512}It+v1kAEUBbhMJYJ2swAtz+RLE6ispv/FB6G/ALhK/YWwEmrloY+0jzrWIfmu+rWUXp8u0Tg4jLXypC5oXAW00IyYnRVdEZJbE9wak96bkNRVWFCYmlJNWxrdTA0QmhL
WoltLab Community Framework 2.x | Double salted bcrypt. | $2a$08$XEQDKNU/Vbootwxv5Gp7gujxFX/RUFsZLvQPYM435Dd3/p17fto02 WoltLab Community Framework 2.x | Double salted bcrypt. | $2a$08$XEQDKNU/Vbootwxv5Gp7gujxFX/RUFsZLvQPYM435Dd3/p17fto02
@@ -204,6 +210,10 @@ Whirlpool | | 74dfc2b27acfa364da55f93a5caee29ccad3557247eda238831b3e9bd931b01d77
## Development ## Development
#### Testing environment
There is a [vagrant](https://github.com/mlojewski-me/user_sql-vagrant) box which you can use at development stage.
#### New database driver support #### New database driver support
Add a new class in the `OCA\UserSQL\Platform` namespace which extends the `AbstractPlatform` class. Add a new class in the `OCA\UserSQL\Platform` namespace which extends the `AbstractPlatform` class.

View File

@@ -8,7 +8,7 @@
Retrieve the users and groups info. Allow the users to change their passwords. Retrieve the users and groups info. Allow the users to change their passwords.
Sync the users' email addresses with the addresses stored by Nextcloud. Sync the users' email addresses with the addresses stored by Nextcloud.
</description> </description>
<version>4.1.0</version> <version>4.3.0-dev</version>
<licence>agpl</licence> <licence>agpl</licence>
<author>Marcin Łojewski</author> <author>Marcin Łojewski</author>
<author>Andreas Böhler</author> <author>Andreas Böhler</author>
@@ -22,7 +22,7 @@
<category>auth</category> <category>auth</category>
<dependencies> <dependencies>
<php min-version="7.0"/> <php min-version="7.0"/>
<nextcloud min-version="14" max-version="14"/> <nextcloud min-version="14" max-version="16"/>
</dependencies> </dependencies>
<settings> <settings>
<admin>\OCA\UserSQL\Settings\Admin</admin> <admin>\OCA\UserSQL\Settings\Admin</admin>

View File

@@ -21,6 +21,7 @@
namespace OCA\UserSQL\Backend; namespace OCA\UserSQL\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\Action\QuotaSync;
@@ -263,6 +264,10 @@ final class UserBackend extends ABackend implements
return false; return false;
} }
if (is_null($user->name)) {
return false;
}
$name = $user->name; $name = $user->name;
$this->logger->debug( $this->logger->debug(
"Returning getDisplayName($uid): $name", "Returning getDisplayName($uid): $name",
@@ -302,7 +307,7 @@ final class UserBackend extends ABackend implements
$password = $this->addSalt($user, $password); $password = $this->addSalt($user, $password);
$isCorrect = $passwordAlgorithm->checkPassword( $isCorrect = $passwordAlgorithm->checkPassword(
$password, $user->password $password, $user->password, $user->salt
); );
if ($user->active == false) { if ($user->active == false) {
@@ -361,9 +366,9 @@ final class UserBackend extends ABackend implements
private function addSalt(User $user, string $password): string private function addSalt(User $user, string $password): string
{ {
if ($user->salt !== null) { if ($user->salt !== null) {
if (empty($this->properties[Opt::PREPEND_SALT])) { if (!empty($this->properties[Opt::APPEND_SALT])) {
return $password . $user->salt; return $password . $user->salt;
} else { } elseif (!empty($this->properties[Opt::PREPEND_SALT])) {
return $user->salt . $password; return $user->salt . $password;
} }
} }
@@ -389,7 +394,9 @@ final class UserBackend extends ABackend implements
$names = []; $names = [];
foreach ($users as $user) { foreach ($users as $user) {
$names[$user] = $user->name; if (!is_null($user->name)) {
$names[$user] = $user->name;
}
} }
$this->logger->debug( $this->logger->debug(
@@ -636,4 +643,16 @@ final class UserBackend extends ABackend implements
{ {
return false; return false;
} }
/**
* @inheritdoc
*/
public function implementsActions($actions): bool
{
if ($actions & Backend::SET_PASSWORD) {
return !empty($this->properties[Opt::PASSWORD_CHANGE]);
}
return parent::implementsActions($actions);
}
} }

View File

@@ -28,6 +28,7 @@ namespace OCA\UserSQL\Constant;
*/ */
final class Opt final class Opt
{ {
const APPEND_SALT = "opt.append_salt";
const CASE_INSENSITIVE_USERNAME = "opt.case_insensitive_username"; const CASE_INSENSITIVE_USERNAME = "opt.case_insensitive_username";
const CRYPTO_CLASS = "opt.crypto_class"; const CRYPTO_CLASS = "opt.crypto_class";
const EMAIL_SYNC = "opt.email_sync"; const EMAIL_SYNC = "opt.email_sync";
@@ -37,5 +38,6 @@ final class Opt
const PASSWORD_CHANGE = "opt.password_change"; const PASSWORD_CHANGE = "opt.password_change";
const PREPEND_SALT = "opt.prepend_salt"; const PREPEND_SALT = "opt.prepend_salt";
const QUOTA_SYNC = "opt.quota_sync"; const QUOTA_SYNC = "opt.quota_sync";
const REVERSE_ACTIVE = "opt.reverse_active";
const USE_CACHE = "opt.use_cache"; const USE_CACHE = "opt.use_cache";
} }

View File

@@ -65,15 +65,15 @@ abstract class AbstractAlgorithm implements IPasswordAlgorithm
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function checkPassword($password, $dbHash) public function checkPassword($password, $dbHash, $salt = null)
{ {
return hash_equals($dbHash, $this->getPasswordHash($password)); return hash_equals($dbHash, $this->getPasswordHash($password, $salt));
} }
/** /**
* @inheritdoc * @inheritdoc
*/ */
public abstract function getPasswordHash($password); public abstract function getPasswordHash($password, $salt = null);
/** /**
* @inheritdoc * @inheritdoc

View File

@@ -38,7 +38,7 @@ abstract class AbstractCrypt extends AbstractAlgorithm
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function checkPassword($password, $dbHash) public function checkPassword($password, $dbHash, $salt = null)
{ {
return hash_equals($dbHash, crypt($password, $dbHash)); return hash_equals($dbHash, crypt($password, $dbHash));
} }
@@ -46,7 +46,7 @@ abstract class AbstractCrypt extends AbstractAlgorithm
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function getPasswordHash($password) public function getPasswordHash($password, $salt = null)
{ {
return crypt($password, $this->getSalt()); return crypt($password, $this->getSalt());
} }

View File

@@ -43,7 +43,7 @@ class Cleartext extends AbstractAlgorithm
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function getPasswordHash($password) public function getPasswordHash($password, $salt = null)
{ {
return $password; return $password;
} }

View File

@@ -43,7 +43,7 @@ class CourierMD5 extends AbstractAlgorithm
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function getPasswordHash($password) public function getPasswordHash($password, $salt = null)
{ {
return '{MD5}' . Utils::hexToBase64(md5($password)); return '{MD5}' . Utils::hexToBase64(md5($password));
} }

View File

@@ -43,7 +43,7 @@ class CourierMD5Raw extends AbstractAlgorithm
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function getPasswordHash($password) public function getPasswordHash($password, $salt = null)
{ {
return '{MD5RAW}' . md5($password); return '{MD5RAW}' . md5($password);
} }

View File

@@ -43,7 +43,7 @@ class CourierSHA1 extends AbstractAlgorithm
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function getPasswordHash($password) public function getPasswordHash($password, $salt = null)
{ {
return '{SHA}' . Utils::hexToBase64(sha1($password)); return '{SHA}' . Utils::hexToBase64(sha1($password));
} }

View File

@@ -43,7 +43,7 @@ class CourierSHA256 extends AbstractAlgorithm
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function getPasswordHash($password) public function getPasswordHash($password, $salt = null)
{ {
return '{SHA256}' . Utils::hexToBase64(hash('sha256', $password)); return '{SHA256}' . Utils::hexToBase64(hash('sha256', $password));
} }

View File

@@ -44,7 +44,7 @@ class Crypt extends AbstractCrypt
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function getPasswordHash($password) public function getPasswordHash($password, $salt = null)
{ {
return password_hash($password, PASSWORD_DEFAULT); return password_hash($password, PASSWORD_DEFAULT);
} }

View File

@@ -81,7 +81,7 @@ class CryptArgon2 extends AbstractAlgorithm
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function checkPassword($password, $dbHash) public function checkPassword($password, $dbHash, $salt = null)
{ {
return password_verify($password, $dbHash); return password_verify($password, $dbHash);
} }
@@ -89,7 +89,7 @@ class CryptArgon2 extends AbstractAlgorithm
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function getPasswordHash($password) public function getPasswordHash($password, $salt = null)
{ {
return password_hash( return password_hash(
$password, PASSWORD_ARGON2I, [ $password, PASSWORD_ARGON2I, [

View File

@@ -52,7 +52,7 @@ class CryptBlowfish extends AbstractAlgorithm
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function checkPassword($password, $dbHash) public function checkPassword($password, $dbHash, $salt = null)
{ {
return password_verify($password, $dbHash); return password_verify($password, $dbHash);
} }
@@ -60,7 +60,7 @@ class CryptBlowfish extends AbstractAlgorithm
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function getPasswordHash($password) public function getPasswordHash($password, $salt = null)
{ {
return password_hash( return password_hash(
$password, PASSWORD_BCRYPT, ["cost" => $this->cost] $password, PASSWORD_BCRYPT, ["cost" => $this->cost]

View File

@@ -42,20 +42,22 @@ interface IPasswordAlgorithm
* This value is stored in the database, when the password is changed. * This value is stored in the database, when the password is changed.
* *
* @param String $password The new password. * @param String $password The new password.
* @param String $salt Optional. Salt value.
* *
* @return boolean True if the password was hashed successfully, false otherwise. * @return boolean True if the password was hashed successfully, false otherwise.
*/ */
public function getPasswordHash($password); public function getPasswordHash($password, $salt = null);
/** /**
* Check password given by the user against hash stored in the database. * Check password given by the user against hash stored in the database.
* *
* @param String $password Password given by the user. * @param String $password Password given by the user.
* @param String $dbHash Password hash stored in the database. * @param String $dbHash Password hash stored in the database.
* @param String $salt Optional. Salt value.
* *
* @return boolean True if the password is correct, false otherwise. * @return boolean True if the password is correct, false otherwise.
*/ */
public function checkPassword($password, $dbHash); public function checkPassword($password, $dbHash, $salt = null);
/** /**
* Configuration for the algorithm. * Configuration for the algorithm.

View File

@@ -43,7 +43,7 @@ class Joomla extends AbstractAlgorithm
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function getPasswordHash($password) public function getPasswordHash($password, $salt = null)
{ {
$salt = Utils::randomString( $salt = Utils::randomString(
32, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 32, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
@@ -55,7 +55,7 @@ class Joomla extends AbstractAlgorithm
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function checkPassword($password, $dbHash) public function checkPassword($password, $dbHash, $salt = null)
{ {
return hash_equals($dbHash, self::generateHash($password, $dbHash)); return hash_equals($dbHash, self::generateHash($password, $dbHash));
} }

View File

@@ -43,7 +43,7 @@ class MD5 extends AbstractAlgorithm
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function getPasswordHash($password) public function getPasswordHash($password, $salt = null)
{ {
return md5($password); return md5($password);
} }

View File

@@ -50,7 +50,7 @@ class Phpass extends AbstractAlgorithm
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function checkPassword($password, $dbHash) public function checkPassword($password, $dbHash, $salt = null)
{ {
return hash_equals($dbHash, $this->crypt($password, $dbHash)); return hash_equals($dbHash, $this->crypt($password, $dbHash));
} }
@@ -136,7 +136,7 @@ class Phpass extends AbstractAlgorithm
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function getPasswordHash($password) public function getPasswordHash($password, $salt = null)
{ {
return $this->crypt($password, $this->genSalt()); return $this->crypt($password, $this->genSalt());
} }

50
lib/Crypto/Redmine.php Normal file
View File

@@ -0,0 +1,50 @@
<?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\Crypto;
/**
* Redmine MD5 hash implementation.
*
* @author Marcin Łojewski <dev@mlojewski.me>
*/
class Redmine extends AbstractAlgorithm
{
/**
* @inheritdoc
*/
public function getPasswordHash($password, $salt = null)
{
if (is_null($salt)) {
return false;
}
return sha1($salt . sha1($password));
}
/**
* @inheritdoc
*/
protected function getAlgorithmName()
{
return "Redmine";
}
}

View File

@@ -24,7 +24,7 @@ namespace OCA\UserSQL\Crypto;
use OCP\IL10N; use OCP\IL10N;
/** /**
* SHA1 hash implementation. * SHA-1 hash implementation.
* *
* @author Marcin Łojewski <dev@mlojewski.me> * @author Marcin Łojewski <dev@mlojewski.me>
*/ */
@@ -43,7 +43,7 @@ class SHA1 extends AbstractAlgorithm
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function getPasswordHash($password) public function getPasswordHash($password, $salt = null)
{ {
return sha1($password); return sha1($password);
} }
@@ -53,6 +53,6 @@ class SHA1 extends AbstractAlgorithm
*/ */
protected function getAlgorithmName() protected function getAlgorithmName()
{ {
return "SHA1"; return "SHA-1";
} }
} }

58
lib/Crypto/SHA256.php Normal file
View File

@@ -0,0 +1,58 @@
<?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\Crypto;
use OCP\IL10N;
/**
* SHA-256 hash implementation.
*
* @author Marcin Łojewski <dev@mlojewski.me>
*/
class SHA256 extends AbstractAlgorithm
{
/**
* The class constructor.
*
* @param IL10N $localization The localization service.
*/
public function __construct(IL10N $localization)
{
parent::__construct($localization);
}
/**
* @inheritdoc
*/
public function getPasswordHash($password, $salt = null)
{
return hash('sha256', $password);
}
/**
* @inheritdoc
*/
protected function getAlgorithmName()
{
return "SHA-256";
}
}

58
lib/Crypto/SHA512.php Normal file
View File

@@ -0,0 +1,58 @@
<?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\Crypto;
use OCP\IL10N;
/**
* SHA-512 hash implementation.
*
* @author Marcin Łojewski <dev@mlojewski.me>
*/
class SHA512 extends AbstractAlgorithm
{
/**
* The class constructor.
*
* @param IL10N $localization The localization service.
*/
public function __construct(IL10N $localization)
{
parent::__construct($localization);
}
/**
* @inheritdoc
*/
public function getPasswordHash($password, $salt = null)
{
return hash('sha512', $password);
}
/**
* @inheritdoc
*/
protected function getAlgorithmName()
{
return "SHA-512";
}
}

View File

@@ -24,7 +24,7 @@ namespace OCA\UserSQL\Crypto;
use OCP\IL10N; use OCP\IL10N;
/** /**
* SHA512 Whirlpool hash implementation. * SHA-512 Whirlpool hash implementation.
* *
* @author Marcin Łojewski <dev@mlojewski.me> * @author Marcin Łojewski <dev@mlojewski.me>
*/ */
@@ -43,7 +43,7 @@ class SHA512Whirlpool extends AbstractAlgorithm
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function getPasswordHash($password) public function getPasswordHash($password, $salt = null)
{ {
return hash('sha512', hash('whirlpool', $password)); return hash('sha512', hash('whirlpool', $password));
} }
@@ -53,6 +53,6 @@ class SHA512Whirlpool extends AbstractAlgorithm
*/ */
protected function getAlgorithmName() protected function getAlgorithmName()
{ {
return "SHA512 Whirlpool"; return "SHA-512 Whirlpool";
} }
} }

View File

@@ -43,7 +43,7 @@ abstract class SSHA extends AbstractAlgorithm
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function checkPassword($password, $dbHash) public function checkPassword($password, $dbHash, $salt = null)
{ {
$saltedPassword = base64_decode( $saltedPassword = base64_decode(
preg_replace("/" . $this->getPrefix() . "/i", "", $dbHash) preg_replace("/" . $this->getPrefix() . "/i", "", $dbHash)
@@ -94,7 +94,7 @@ abstract class SSHA extends AbstractAlgorithm
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function getPasswordHash($password) public function getPasswordHash($password, $salt = null)
{ {
return self::ssha( return self::ssha(
$password, Utils::randomString( $password, Utils::randomString(

View File

@@ -31,7 +31,7 @@ class WCF2 extends AbstractCrypt
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function checkPassword($password, $dbHash) public function checkPassword($password, $dbHash, $salt = null)
{ {
return hash_equals($dbHash, crypt(crypt($password, $dbHash), $dbHash)); return hash_equals($dbHash, crypt(crypt($password, $dbHash), $dbHash));
} }
@@ -39,7 +39,7 @@ class WCF2 extends AbstractCrypt
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function getPasswordHash($password) public function getPasswordHash($password, $salt = null)
{ {
$salt = $this->getSalt(); $salt = $this->getSalt();
return crypt(crypt($password, $salt), $salt); return crypt(crypt($password, $salt), $salt);

View File

@@ -43,7 +43,7 @@ class Whirlpool extends AbstractAlgorithm
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function getPasswordHash($password) public function getPasswordHash($password, $salt = null)
{ {
return hash('whirlpool', $password); return hash('whirlpool', $password);
} }

View File

@@ -22,6 +22,7 @@
namespace OCA\UserSQL\Query; namespace OCA\UserSQL\Query;
use OCA\UserSQL\Constant\DB; use OCA\UserSQL\Constant\DB;
use OCA\UserSQL\Constant\Opt;
use OCA\UserSQL\Constant\Query; use OCA\UserSQL\Constant\Query;
use OCA\UserSQL\Properties; use OCA\UserSQL\Properties;
@@ -86,81 +87,83 @@ class QueryProvider implements \ArrayAccess
$searchParam = Query::SEARCH_PARAM; $searchParam = Query::SEARCH_PARAM;
$uidParam = Query::UID_PARAM; $uidParam = Query::UID_PARAM;
$reverseActiveOpt = $this->properties[Opt::REVERSE_ACTIVE];
$groupColumns $groupColumns
= "$gGID AS gid, " . = "g.$gGID AS gid, " .
(empty($gName) ? $gGID : $gName) . " AS name, " . (empty($gName) ? "g." . $gGID : "g." . $gName) . " AS name, " .
(empty($gAdmin) ? "false" : $gAdmin) . " AS admin"; (empty($gAdmin) ? "false" : "g." . $gAdmin) . " AS admin";
$userColumns $userColumns
= "$uUID AS uid, " . = "u.$uUID AS uid, " .
(empty($uName) ? $uUID : $uName) . " AS name, " . (empty($uName) ? "u." . $uUID : "u." . $uName) . " AS name, " .
(empty($uEmail) ? "null" : $uEmail) . " AS email, " . (empty($uEmail) ? "null" : "u." . $uEmail) . " AS email, " .
(empty($uQuota) ? "null" : $uQuota) . " AS quota, " . (empty($uQuota) ? "null" : "u." . $uQuota) . " AS quota, " .
(empty($uHome) ? "null" : $uHome) . " AS home, " . (empty($uHome) ? "null" : "u." . $uHome) . " AS home, " .
(empty($uActive) ? "true" : $uActive) . " AS active, " . (empty($uActive) ? "true" : (empty($reverseActiveOpt) ? "" : "NOT ") . "u." . $uActive) . " AS active, " .
(empty($uAvatar) ? "false" : $uAvatar) . " AS avatar, " . (empty($uAvatar) ? "false" : "u." . $uAvatar) . " AS avatar, " .
(empty($uSalt) ? "null" : $uSalt) . " AS salt"; (empty($uSalt) ? "null" : "u." . $uSalt) . " AS salt";
$this->queries = [ $this->queries = [
Query::BELONGS_TO_ADMIN => Query::BELONGS_TO_ADMIN =>
"SELECT COUNT($gGID) > 0 AS admin " . "SELECT COUNT(g.$gGID) > 0 AS admin " .
"FROM $group, $userGroup " . "FROM $group g, $userGroup ug " .
"WHERE $ugGID = $gGID " . "WHERE ug.$ugGID = g.$gGID " .
"AND $ugUID = :$uidParam " . "AND ug.$ugUID = :$uidParam " .
"AND $gAdmin", "AND g.$gAdmin",
Query::COUNT_GROUPS => Query::COUNT_GROUPS =>
"SELECT COUNT($ugGID) " . "SELECT COUNT(ug.$ugGID) " .
"FROM $userGroup " . "FROM $userGroup ug " .
"WHERE $ugGID = :$gidParam " . "WHERE ug.$ugGID = :$gidParam " .
"AND $ugUID " . "AND ug.$ugUID " .
"LIKE :$searchParam", "LIKE :$searchParam",
Query::COUNT_USERS => Query::COUNT_USERS =>
"SELECT COUNT($uUID) AS count " . "SELECT COUNT(u.$uUID) AS count " .
"FROM $user " . "FROM $user u " .
"WHERE $uUID LIKE :$searchParam", "WHERE u.$uUID LIKE :$searchParam",
Query::FIND_GROUP => Query::FIND_GROUP =>
"SELECT $groupColumns " . "SELECT $groupColumns " .
"FROM $group " . "FROM $group g " .
"WHERE $gGID = :$gidParam", "WHERE g.$gGID = :$gidParam",
Query::FIND_GROUP_USERS => Query::FIND_GROUP_USERS =>
"SELECT $ugUID AS uid " . "SELECT ug.$ugUID AS uid " .
"FROM $userGroup " . "FROM $userGroup ug " .
"WHERE $ugGID = :$gidParam " . "WHERE ug.$ugGID = :$gidParam " .
"AND $ugUID " . "AND ug.$ugUID " .
"LIKE :$searchParam " . "LIKE :$searchParam " .
"ORDER BY $ugUID", "ORDER BY ug.$ugUID",
Query::FIND_GROUPS => Query::FIND_GROUPS =>
"SELECT $groupColumns " . "SELECT $groupColumns " .
"FROM $group " . "FROM $group g " .
"WHERE $gGID LIKE :$searchParam " . "WHERE g.$gGID LIKE :$searchParam " .
"ORDER BY $gGID", "ORDER BY g.$gGID",
Query::FIND_USER => Query::FIND_USER =>
"SELECT $userColumns, $uPassword AS password " . "SELECT $userColumns, u.$uPassword AS password " .
"FROM $user " . "FROM $user u " .
"WHERE $uUID = :$uidParam", "WHERE u.$uUID = :$uidParam",
Query::FIND_USER_CASE_INSENSITIVE => Query::FIND_USER_CASE_INSENSITIVE =>
"SELECT $userColumns, $uPassword AS password " . "SELECT $userColumns, u.$uPassword AS password " .
"FROM $user " . "FROM $user u " .
"WHERE lower($uUID) = lower(:$uidParam)", "WHERE lower(u.$uUID) = lower(:$uidParam)",
Query::FIND_USER_GROUPS => Query::FIND_USER_GROUPS =>
"SELECT $groupColumns " . "SELECT $groupColumns " .
"FROM $group, $userGroup " . "FROM $group g, $userGroup ug " .
"WHERE $ugGID = $gGID " . "WHERE ug.$ugGID = g.$gGID " .
"AND $ugUID = :$uidParam " . "AND ug.$ugUID = :$uidParam " .
"ORDER BY $gGID", "ORDER BY g.$gGID",
Query::FIND_USERS => Query::FIND_USERS =>
"SELECT $userColumns " . "SELECT $userColumns " .
"FROM $user " . "FROM $user u " .
"WHERE $uUID LIKE :$searchParam " . "WHERE u.$uUID LIKE :$searchParam " .
"ORDER BY $uUID", "ORDER BY u.$uUID",
Query::UPDATE_DISPLAY_NAME => Query::UPDATE_DISPLAY_NAME =>
"UPDATE $user " . "UPDATE $user " .

View File

@@ -110,7 +110,8 @@ function print_select_options(
<fieldset><?php <fieldset><?php
print_checkbox_input($l, "opt-name_change", "Allow display name change", $_["opt.name_change"]); print_checkbox_input($l, "opt-name_change", "Allow display name change", $_["opt.name_change"]);
print_checkbox_input($l, "opt-password_change", "Allow password change", $_["opt.password_change"]); print_checkbox_input($l, "opt-password_change", "Allow password change", $_["opt.password_change"]);
print_checkbox_input($l, "opt-case_insensitive_username", "Case-insensitive username", $_["opt.case_insensitive_username"]); ?> print_checkbox_input($l, "opt-case_insensitive_username", "Case-insensitive username", $_["opt.case_insensitive_username"]);
print_checkbox_input($l, "opt-reverse_active", "Reverse active column", $_["opt.reverse_active"]); ?>
<div class="button-right"><?php <div class="button-right"><?php
print_checkbox_input($l, "opt-use_cache", "Use cache", $_["opt.use_cache"], false); ?> print_checkbox_input($l, "opt-use_cache", "Use cache", $_["opt.use_cache"], false); ?>
<input type="submit" id="user_sql-clear_cache" value="<?php p($l->t("Clear cache")); ?>"> <input type="submit" id="user_sql-clear_cache" value="<?php p($l->t("Clear cache")); ?>">
@@ -153,6 +154,7 @@ function print_select_options(
print_text_input($l, "db-table-user-column-active", "Active", $_["db.table.user.column.active"]); print_text_input($l, "db-table-user-column-active", "Active", $_["db.table.user.column.active"]);
print_text_input($l, "db-table-user-column-avatar", "Provide avatar", $_["db.table.user.column.avatar"]); print_text_input($l, "db-table-user-column-avatar", "Provide avatar", $_["db.table.user.column.avatar"]);
print_text_input($l, "db-table-user-column-salt", "Salt", $_["db.table.user.column.salt"]); print_text_input($l, "db-table-user-column-salt", "Salt", $_["db.table.user.column.salt"]);
print_checkbox_input($l, "opt-append_salt", "Append salt", $_["opt.append_salt"]);
print_checkbox_input($l, "opt-prepend_salt", "Prepend salt", $_["opt.prepend_salt"]); ?> print_checkbox_input($l, "opt-prepend_salt", "Prepend salt", $_["opt.prepend_salt"]); ?>
</fieldset> </fieldset>
</div> </div>

View File

@@ -27,7 +27,7 @@ use OCP\IL10N;
use Test\TestCase; use Test\TestCase;
/** /**
* Unit tests for class <code>PhpassTest</code>. * Unit tests for class <code>Phpass</code>.
* *
* @author Marcin Łojewski <dev@mlojewski.me> * @author Marcin Łojewski <dev@mlojewski.me>
*/ */

View File

@@ -0,0 +1,61 @@
<?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 Tests\UserSQL\Crypto;
use OCA\UserSQL\Crypto\IPasswordAlgorithm;
use OCA\UserSQL\Crypto\Redmine;
use OCP\IL10N;
use Test\TestCase;
/**
* Unit tests for class <code>Redmine</code>.
*
* @author Marcin Łojewski <dev@mlojewski.me>
*/
class RedmineTest extends TestCase
{
/**
* @var IPasswordAlgorithm
*/
private $crypto;
public function testCheckPassword()
{
$this->assertTrue(
$this->crypto->checkPassword(
"password", "48b75edeffd8e413341d7734f0f3391e7a5da994", "salt"
)
);
}
public function testPasswordHash()
{
$hash = $this->crypto->getPasswordHash("password", "salt");
$this->assertTrue($this->crypto->checkPassword("password", $hash, "salt"));
}
protected function setUp()
{
parent::setUp();
$this->crypto = new Redmine($this->createMock(IL10N::class));
}
}

View File

@@ -0,0 +1,61 @@
<?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 Tests\UserSQL\Crypto;
use OCA\UserSQL\Crypto\IPasswordAlgorithm;
use OCA\UserSQL\Crypto\SHA256;
use OCP\IL10N;
use Test\TestCase;
/**
* Unit tests for class <code>SHA256</code>.
*
* @author Marcin Łojewski <dev@mlojewski.me>
*/
class SHA512Test extends TestCase
{
/**
* @var IPasswordAlgorithm
*/
private $crypto;
public function testCheckPassword()
{
$this->assertTrue(
$this->crypto->checkPassword(
"password", "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8"
)
);
}
public function testPasswordHash()
{
$hash = $this->crypto->getPasswordHash("password");
$this->assertTrue($this->crypto->checkPassword("password", $hash));
}
protected function setUp()
{
parent::setUp();
$this->crypto = new SHA256($this->createMock(IL10N::class));
}
}

View File

@@ -0,0 +1,62 @@
<?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 Tests\UserSQL\Crypto;
use OCA\UserSQL\Crypto\IPasswordAlgorithm;
use OCA\UserSQL\Crypto\SHA512;
use OCP\IL10N;
use Test\TestCase;
/**
* Unit tests for class <code>SHA512</code>.
*
* @author Marcin Łojewski <dev@mlojewski.me>
*/
class SHA512Test extends TestCase
{
/**
* @var IPasswordAlgorithm
*/
private $crypto;
public function testCheckPassword()
{
$this->assertTrue(
$this->crypto->checkPassword(
"password",
"b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86"
)
);
}
public function testPasswordHash()
{
$hash = $this->crypto->getPasswordHash("password");
$this->assertTrue($this->crypto->checkPassword("password", $hash));
}
protected function setUp()
{
parent::setUp();
$this->crypto = new SHA512($this->createMock(IL10N::class));
}
}