migrate therinaldos.com data
Build & Deploy to DigitalOcean Space / build (push) Failing after 2m38s

This commit is contained in:
2024-05-05 15:50:45 -04:00
commit ef1ff240d4
23182 changed files with 3801898 additions and 0 deletions
@@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
/**
* @copyright 2017, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy\AppInfo;
use OCA\Password_Policy\Capabilities;
use OCA\Password_Policy\Listener\BeforePasswordUpdatedEventListener;
use OCA\Password_Policy\Listener\BeforeUserLoggedInEventListener;
use OCA\Password_Policy\Listener\FailedLoginListener;
use OCA\Password_Policy\Listener\GenerateSecurePasswordEventListener;
use OCA\Password_Policy\Listener\PasswordUpdatedEventListener;
use OCA\Password_Policy\Listener\SuccesfullLoginListener;
use OCA\Password_Policy\Listener\ValidatePasswordPolicyEventListener;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\Authentication\Events\LoginFailedEvent;
use OCP\Security\Events\GenerateSecurePasswordEvent;
use OCP\Security\Events\ValidatePasswordPolicyEvent;
use OCP\User\Events\BeforePasswordUpdatedEvent;
use OCP\User\Events\BeforeUserLoggedInEvent;
use OCP\User\Events\PasswordUpdatedEvent;
use OCP\User\Events\UserLoggedInEvent;
class Application extends App implements IBootstrap {
public function __construct() {
parent::__construct('password_policy');
}
public function register(IRegistrationContext $context): void {
$context->registerCapability(Capabilities::class);
$context->registerEventListener(ValidatePasswordPolicyEvent::class, ValidatePasswordPolicyEventListener::class);
$context->registerEventListener(GenerateSecurePasswordEvent::class, GenerateSecurePasswordEventListener::class);
$context->registerEventListener(BeforePasswordUpdatedEvent::class, BeforePasswordUpdatedEventListener::class);
$context->registerEventListener(PasswordUpdatedEvent::class, PasswordUpdatedEventListener::class);
$context->registerEventListener(BeforeUserLoggedInEvent::class, BeforeUserLoggedInEventListener::class);
$context->registerEventListener(LoginFailedEvent::class, FailedLoginListener::class);
$context->registerEventListener(UserLoggedInEvent::class, SuccesfullLoginListener::class);
}
public function boot(IBootContext $context): void {
}
}
@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2017 Bjoern Schiessle <bjoern@schiessle.org>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy;
use OCP\Capabilities\ICapability;
use OCP\IURLGenerator;
class Capabilities implements ICapability {
/** @var PasswordPolicyConfig */
private $config;
/** @var IURLGenerator */
private $urlGenerator;
public function __construct(PasswordPolicyConfig $config, IURLGenerator $urlGenerator) {
$this->config = $config;
$this->urlGenerator = $urlGenerator;
}
/**
* Function an app uses to return the capabilities
*
* @return array Array containing the apps capabilities
* @since 12.0.0
*/
public function getCapabilities(): array {
return [
'password_policy' =>
[
'minLength' => $this->config->getMinLength(),
'enforceNonCommonPassword' => $this->config->getEnforceNonCommonPassword(),
'enforceNumericCharacters' => $this->config->getEnforceNumericCharacters(),
'enforceSpecialCharacters' => $this->config->getEnforceSpecialCharacters(),
'enforceUpperLowerCase' => $this->config->getEnforceUpperLowerCase(),
'api' => [
'generate' => $this->urlGenerator->linkToOCSRouteAbsolute('password_policy.API.generate'),
'validate' => $this->urlGenerator->linkToOCSRouteAbsolute('password_policy.API.validate'),
]
]
];
}
}
@@ -0,0 +1,121 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020 Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy\Compliance;
use OC\HintException;
use OCA\Password_Policy\PasswordPolicyConfig;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IUser;
use OCP\IUserManager;
use OCP\PreConditionNotMetException;
class Expiration implements IUpdatable, IEntryControl {
/** @var IConfig */
private $config;
/** @var PasswordPolicyConfig */
private $policyConfig;
/** @var IUserManager */
private $userManager;
/** @var IEventDispatcher */
private $eventDispatcher;
/** @var IL10N */
private $l;
public function __construct(
IConfig $config,
PasswordPolicyConfig $policyConfig,
IUserManager $userManager,
IEventDispatcher $eventDispatcher,
IL10N $l
) {
$this->config = $config;
$this->policyConfig = $policyConfig;
$this->userManager = $userManager;
$this->eventDispatcher = $eventDispatcher;
$this->l = $l;
}
/**
* @throws PreConditionNotMetException
*/
public function update(IUser $user, string $password): void {
if (!$this->isLocalUser($user)) {
return;
}
if ($this->policyConfig->getExpiryInDays() === 0) {
$this->config->deleteUserValue(
$user->getUID(),
'password_policy',
'pwd_last_updated'
);
return;
}
$this->config->setUserValue(
$user->getUID(),
'password_policy',
'pwd_last_updated',
time()
);
}
public function entryControl(IUser $user, ?string $password): void {
if ($this->policyConfig->getExpiryInDays() !== 0
&& $this->isLocalUser($user)
&& $this->isPasswordExpired($user)
) {
$message = 'Password is expired, please use forgot password method to reset';
$message_t = $this->l->t('Password is expired, please use forgot password method to reset');
throw new HintException($message, $message_t);
}
}
protected function isPasswordExpired(IUser $user) {
$updatedAt = (int)$this->config->getUserValue(
$user->getUID(),
'password_policy',
'pwd_last_updated',
0
);
if ($updatedAt === 0) {
$this->update($user, '');
return false;
}
$expiryInDays = $this->policyConfig->getExpiryInDays();
$expiresIn = $updatedAt + $expiryInDays * 24 * 60 * 60;
return $expiresIn <= time();
}
protected function isLocalUser(IUser $user) {
$localBackends = ['Database', 'Guests'];
return in_array($user->getBackendClassName(), $localBackends);
}
}
@@ -0,0 +1,133 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020 Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy\Compliance;
use OC\HintException;
use OCA\Password_Policy\PasswordPolicyConfig;
use OCP\IConfig;
use OCP\IL10N;
use OCP\ILogger;
use OCP\IUser;
use OCP\IUserSession;
use OCP\PreConditionNotMetException;
use OCP\Security\IHasher;
class HistoryCompliance implements IAuditor, IUpdatable {
/** @var string */
protected $uid;
/** @var PasswordPolicyConfig */
private $policyConfig;
/** @var IConfig */
private $config;
/** @var IUserSession */
private $session;
/** @var IHasher */
private $hasher;
/** @var IL10N */
private $l;
/** @var ILogger */
private $logger;
public function __construct(
PasswordPolicyConfig $policyConfig,
IConfig $config,
IUserSession $session,
IHasher $hasher,
IL10N $l,
ILogger $logger
) {
$this->policyConfig = $policyConfig;
$this->config = $config;
$this->session = $session;
$this->hasher = $hasher;
$this->l = $l;
$this->logger = $logger;
}
/**
* @throws HintException
*/
public function audit(IUser $user, string $password): void {
if ($this->policyConfig->getHistorySize() === 0) {
return;
}
$history = $this->getHistory($user);
foreach ($history as $hash) {
if ($this->hasher->verify($password, $hash)) {
$message = 'Password must not have been used recently before.';
$message_t = $this->l->t(
'Password must not have been used recently before.'
);
throw new HintException($message, $message_t);
}
}
}
/**
* @throws PreConditionNotMetException
*/
public function update(IUser $user, string $password): void {
$historySize = $this->policyConfig->getHistorySize();
if ($historySize === 0) {
$this->config->deleteUserValue($user->getUID(), 'password_policy', 'passwordHistory');
return;
}
$history = $this->getHistory($user);
array_unshift($history, $this->hasher->hash($password));
$history = \array_slice($history, 0, $historySize);
$this->config->setUserValue(
$user->getUID(),
'password_policy',
'passwordHistory',
\json_encode($history)
);
}
protected function getHistory(IUser $user): array {
$history = $this->config->getUserValue(
$user->getUID(),
'password_policy',
'passwordHistory',
'[]'
);
$history = \json_decode($history, true);
if (!is_array($history)) {
$this->logger->warning(
'Received password history of {uid} had the unexpected value of {history}, resetting.',
['app' => 'password_policy', 'uid' => $user->getUID(), 'history' => $history]
);
$history = [];
}
$history = \array_slice($history, 0, $this->policyConfig->getHistorySize());
return $history;
}
}
@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020 Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy\Compliance;
use OCP\IUser;
interface IAuditor {
public function audit(IUser $user, string $password): void;
}
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020 Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy\Compliance;
use OC\HintException;
use OCP\IUser;
interface IEntryControl {
/**
* @throws HintException
*/
public function entryControl(IUser $user, ?string $password): void;
}
@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020 Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy\Compliance;
use OCP\IUser;
interface IUpdatable {
public function update(IUser $user, string $password): void;
}
@@ -0,0 +1,129 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020 Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy;
use OC\HintException;
use OC\User\LoginException;
use OCA\Password_Policy\Compliance\Expiration;
use OCA\Password_Policy\Compliance\HistoryCompliance;
use OCA\Password_Policy\Compliance\IAuditor;
use OCA\Password_Policy\Compliance\IEntryControl;
use OCA\Password_Policy\Compliance\IUpdatable;
use OCP\AppFramework\IAppContainer;
use OCP\AppFramework\QueryException;
use OCP\IConfig;
use OCP\ILogger;
use OCP\ISession;
use OCP\IUser;
use OCP\IUserSession;
class ComplianceService {
/** @var IAppContainer */
protected $container;
/** @var ILogger */
protected $logger;
protected const COMPLIANCERS = [
HistoryCompliance::class,
Expiration::class,
];
/** @var IUserSession */
private $userSession;
/** @var IConfig */
private $config;
/** @var ISession */
private $session;
public function __construct(
IAppContainer $container,
ILogger $logger,
IUserSession $userSession,
IConfig $config,
ISession $session
) {
$this->container = $container;
$this->logger = $logger;
$this->userSession = $userSession;
$this->config = $config;
$this->session = $session;
}
public function update(IUser $user, string $password) {
foreach ($this->getInstance(IUpdatable::class) as $instance) {
$instance->update($user, $password);
}
}
public function audit(IUser $user, string $password) {
foreach ($this->getInstance(IAuditor::class) as $instance) {
$instance->audit($user, $password);
}
}
/**
* @throws LoginException
*/
//public function entryControl(IUser $user, string $password, bool $isTokenLogin) {
public function entryControl(string $loginName, ?string $password) {
$uid = $loginName;
\OCP\Util::emitHook('\OCA\Files_Sharing\API\Server2Server', 'preLoginNameUsedAsUserName', ['uid' => &$uid]);
/** @var IEntryControl $instance */
foreach ($this->getInstance(IEntryControl::class) as $instance) {
try {
$user = \OC::$server->getUserManager()->get($uid);
if (!($user instanceof IUser)) {
break;
}
$instance->entryControl($user, $password);
} catch (HintException $e) {
throw new LoginException($e->getHint());
}
}
}
/**
* @returns Iterable
*/
protected function getInstance($interface) {
foreach (self::COMPLIANCERS as $compliance) {
try {
$instance = $this->container->query($compliance);
if (!$instance instanceof $interface) {
continue;
}
} catch (QueryException $e) {
//ignore and continue
$this->logger->logException($e, ['level' => ILogger::INFO]);
continue;
}
yield $instance;
}
}
}
@@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy\Controller;
use OC\HintException;
use OCA\Password_Policy\Generator;
use OCA\Password_Policy\PasswordValidator;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\IRequest;
class APIController extends OCSController {
/** @var PasswordValidator */
private $validator;
/** @var Generator */
private $generator;
public function __construct(string $appName, IRequest $request, PasswordValidator $validator, Generator $generator) {
parent::__construct($appName, $request);
$this->validator = $validator;
$this->generator = $generator;
}
/**
* @NoAdminRequired
*
* @param string $password
* @return DataResponse
*/
public function validate(string $password): DataResponse {
try {
$this->validator->validate($password);
} catch (HintException $e) {
return new DataResponse([
'passed' => false,
'reason' => $e->getHint(),
]);
}
return new DataResponse([
'passed' => true,
]);
}
/**
* @NoAdminRequired
*
* @return DataResponse
*/
public function generate(): DataResponse {
try {
$password = $this->generator->generate();
} catch (HintException $e) {
return new DataResponse([], Http::STATUS_CONFLICT);
}
return new DataResponse([
'password' => $password,
]);
}
}
@@ -0,0 +1,94 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy;
use OCP\IConfig;
use OCP\IUser;
use OCP\IUserManager;
class FailedLoginCompliance {
/** @var IConfig */
private $config;
/** @var IUserManager */
private $userManager;
/** @var PasswordPolicyConfig */
private $passwordPolicyConfig;
public function __construct(
IConfig $config,
IUserManager $userManager,
PasswordPolicyConfig $passwordPolicyConfig) {
$this->config = $config;
$this->userManager = $userManager;
$this->passwordPolicyConfig = $passwordPolicyConfig;
}
public function onFailedLogin(string $uid) {
$user = $this->userManager->get($uid);
if (!($user instanceof IUser)) {
return;
}
if ($user->isEnabled() === false) {
// Just ignore this user then
return;
}
$allowedAttempts = $this->passwordPolicyConfig->getMaximumLoginAttempts();
if ($allowedAttempts === 0) {
// 0 is the max
return;
}
$attempts = $this->getAttempts($uid);
$attempts++;
if ($attempts >= $allowedAttempts) {
$this->setAttempts($uid, 0);
$user->setEnabled(false);
return;
}
$this->setAttempts($uid, $attempts);
}
public function onSucessfullLogin(IUser $user) {
$this->setAttempts($user->getUID(), 0);
}
private function getAttempts(string $uid): int {
return (int)$this->config->getUserValue($uid, 'password_policy', 'failedLoginAttempts', 0);
}
private function setAttempts(string $uid, int $attempts): void {
$this->config->setUserValue($uid, 'password_policy', 'failedLoginAttempts', $attempts);
}
}
@@ -0,0 +1,116 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2018, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy;
use OC\HintException;
use OCP\Security\ISecureRandom;
class Generator {
/** @var PasswordPolicyConfig */
private $config;
/** @var PasswordValidator */
private $validator;
/** @var ISecureRandom */
private $random;
public function __construct(PasswordPolicyConfig $config,
PasswordValidator $validator,
ISecureRandom $random) {
$this->config = $config;
$this->validator = $validator;
$this->random = $random;
}
/**
* @return string
* @throws HintException
*/
public function generate(): string {
$minLength = max($this->config->getMinLength(), 8);
$length = $minLength;
$password = '';
$chars = '';
$found = false;
for ($i = 0; $i < 10; $i++) {
if ($this->config->getEnforceUpperLowerCase()) {
$password .= $this->random->generate(1, ISecureRandom::CHAR_UPPER);
$password .= $this->random->generate(1, ISecureRandom::CHAR_LOWER);
$length -= 2;
$chars .= ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER;
}
if ($this->config->getEnforceNumericCharacters()) {
$password .= $this->random->generate(1, ISecureRandom::CHAR_DIGITS);
$length -= 1;
$chars .= ISecureRandom::CHAR_DIGITS;
}
if ($this->config->getEnforceSpecialCharacters()) {
$password .= $this->random->generate(1, ISecureRandom::CHAR_SYMBOLS);
$length -= 1;
$chars .= ISecureRandom::CHAR_SYMBOLS;
}
if ($chars === '') {
$chars = ISecureRandom::CHAR_HUMAN_READABLE;
}
$password .= $chars = $this->random->generate($length, $chars);
// Shuffle string so the order is random
$password = str_shuffle($password);
try {
$this->validator->validate($password);
if ($password === null || $password === '') {
// something went wrong
break;
}
$found = true;
break;
} catch (HintException $e) {
/*
* Invalid so lets go for another round
* Reset the length so we don't run below zero
*/
$length = $minLength;
}
}
if ($found === false) {
throw new HintException('Could not generate a valid password');
}
return $password;
}
}
@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020 Morris Jobke <hey@morrisjobke.de>
*
* @author Morris Jobke <hey@morrisjobke.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy\Listener;
use OCA\Password_Policy\ComplianceService;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\User\Events\BeforePasswordUpdatedEvent;
class BeforePasswordUpdatedEventListener implements IEventListener {
/** @var ComplianceService */
private $complianceUpdater;
public function __construct(ComplianceService $complianceUpdater) {
$this->complianceUpdater = $complianceUpdater;
}
public function handle(Event $event): void {
if (!($event instanceof BeforePasswordUpdatedEvent)) {
return;
}
$this->complianceUpdater->audit($event->getUser(), $event->getPassword());
}
}
@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020 Morris Jobke <hey@morrisjobke.de>
*
* @author Morris Jobke <hey@morrisjobke.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy\Listener;
use OCA\Password_Policy\ComplianceService;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\User\Events\BeforeUserLoggedInEvent;
class BeforeUserLoggedInEventListener implements IEventListener {
/** @var ComplianceService */
private $complianceUpdater;
public function __construct(ComplianceService $complianceUpdater) {
$this->complianceUpdater = $complianceUpdater;
}
public function handle(Event $event): void {
if (!$event instanceof BeforeUserLoggedInEvent) {
return;
}
$this->complianceUpdater->entryControl($event->getUsername(), $event->getPassword());
}
}
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy\Listener;
use OCA\Password_Policy\FailedLoginCompliance;
use OCP\Authentication\Events\LoginFailedEvent;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
class FailedLoginListener implements IEventListener {
/** @var FailedLoginCompliance */
private $compliance;
public function __construct(FailedLoginCompliance $compliance) {
$this->compliance = $compliance;
}
public function handle(Event $event): void {
if (!($event instanceof LoginFailedEvent)) {
return;
}
$this->compliance->onFailedLogin($event->getUid());
}
}
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020 Morris Jobke <hey@morrisjobke.de>
*
* @author Morris Jobke <hey@morrisjobke.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy\Listener;
use OCA\Password_Policy\Generator;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Security\Events\GenerateSecurePasswordEvent;
class GenerateSecurePasswordEventListener implements IEventListener {
/** @var Generator */
private $generator;
public function __construct(Generator $generator) {
$this->generator = $generator;
}
public function handle(Event $event): void {
if (!($event instanceof GenerateSecurePasswordEvent)) {
return;
}
$event->setPassword($this->generator->generate());
}
}
@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020 Morris Jobke <hey@morrisjobke.de>
*
* @author Morris Jobke <hey@morrisjobke.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy\Listener;
use OCA\Password_Policy\ComplianceService;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\User\Events\PasswordUpdatedEvent;
class PasswordUpdatedEventListener implements IEventListener {
/** @var ComplianceService */
private $complianceUpdater;
public function __construct(ComplianceService $complianceUpdater) {
$this->complianceUpdater = $complianceUpdater;
}
public function handle(Event $event): void {
if (!($event instanceof PasswordUpdatedEvent)) {
return;
}
$this->complianceUpdater->update($event->getUser(), $event->getPassword());
}
}
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy\Listener;
use OCA\Password_Policy\FailedLoginCompliance;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\User\Events\UserLoggedInEvent;
class SuccesfullLoginListener implements IEventListener {
/** @var FailedLoginCompliance */
private $compliance;
public function __construct(FailedLoginCompliance $compliance) {
$this->compliance = $compliance;
}
public function handle(Event $event): void {
if (!($event instanceof UserLoggedInEvent)) {
return;
}
$this->compliance->onSucessfullLogin($event->getUser());
}
}
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020 Morris Jobke <hey@morrisjobke.de>
*
* @author Morris Jobke <hey@morrisjobke.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy\Listener;
use OCA\Password_Policy\PasswordValidator;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Security\Events\ValidatePasswordPolicyEvent;
class ValidatePasswordPolicyEventListener implements IEventListener {
/** @var PasswordValidator */
private $passwordValidator;
public function __construct(PasswordValidator $passwordValidator) {
$this->passwordValidator = $passwordValidator;
}
public function handle(Event $event): void {
if (!($event instanceof ValidatePasswordPolicyEvent)) {
return;
}
$this->passwordValidator->validate($event->getPassword());
}
}
@@ -0,0 +1,210 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy;
use OCP\IConfig;
/**
* Class Config
*
* read/write config of the password policy
*
* @package OCA\Password_Policy
*/
class PasswordPolicyConfig {
/** @var IConfig */
private $config;
/**
* Config constructor.
*
* @param IConfig $config
*/
public function __construct(IConfig $config) {
$this->config = $config;
}
/**
* get the enforced minimum length of passwords
*
* @return int
*/
public function getMinLength(): int {
return (int)$this->config->getAppValue('password_policy', 'minLength', '10');
}
/**
* Whether non-common passwords should be enforced
*
* @return bool
*/
public function getEnforceNonCommonPassword(): bool {
$enforceNonCommonPasswords = $this->config->getAppValue(
'password_policy',
'enforceNonCommonPassword',
'1'
);
return $enforceNonCommonPasswords === '1';
}
/**
* does the password need to contain upper and lower case characters
*
* @return bool
*/
public function getEnforceUpperLowerCase(): bool {
$enforceUpperLowerCase = $this->config->getAppValue(
'password_policy',
'enforceUpperLowerCase',
'0'
);
return $enforceUpperLowerCase === '1';
}
/**
* does the password need to contain numeric characters
*
* @return bool
*/
public function getEnforceNumericCharacters(): bool {
$enforceNumericCharacters = $this->config->getAppValue(
'password_policy',
'enforceNumericCharacters',
'0'
);
return $enforceNumericCharacters === '1';
}
/**
* does the password need to contain special characters
*
* @return bool
*/
public function getEnforceSpecialCharacters(): bool {
$enforceSpecialCharacters = $this->config->getAppValue(
'password_policy',
'enforceSpecialCharacters',
'0'
);
return $enforceSpecialCharacters === '1';
}
/**
* set minimal length of passwords
*
* @param int $minLength
*/
public function setMinLength(int $minLength) {
$this->config->setAppValue('password_policy', 'minLength', $minLength);
}
/**
* enforce upper and lower case characters
*
* @param bool $enforceUpperLowerCase
*/
public function setEnforceUpperLowerCase(bool $enforceUpperLowerCase) {
$value = $enforceUpperLowerCase === true ? '1' : '0';
$this->config->setAppValue('password_policy', 'enforceUpperLowerCase', $value);
}
/**
* enforce numeric characters
*
* @param bool $enforceNumericCharacters
*/
public function setEnforceNumericCharacters(bool $enforceNumericCharacters) {
$value = $enforceNumericCharacters === true ? '1' : '0';
$this->config->setAppValue('password_policy', 'enforceNumericCharacters', $value);
}
/**
* enforce special characters
*
* @param bool $enforceSpecialCharacters
*/
public function setEnforceSpecialCharacters(bool $enforceSpecialCharacters) {
$value = $enforceSpecialCharacters === true ? '1' : '0';
$this->config->setAppValue('password_policy', 'enforceSpecialCharacters', $value);
}
/**
* Do we check against the HaveIBeenPwned passwords
*
* @return bool
*/
public function getEnforceHaveIBeenPwned(): bool {
$hasInternetConnection = $this->config->getSystemValue('has_internet_connection', true);
if (!$hasInternetConnection) {
return false;
}
return $this->config->getAppValue(
'password_policy',
'enforceHaveIBeenPwned',
'1'
) === '1';
}
/**
* Enforce checking against haveibeenpwned.com
*
* @param bool $enforceHaveIBeenPwned
*/
public function setEnforceHaveIBeenPwned(bool $enforceHaveIBeenPwned) {
$this->config->setAppValue('password_policy', 'enforceHaveIBeenPwned', $enforceHaveIBeenPwned ? '1' : '0');
}
public function getHistorySize(): int {
return (int)$this->config->getAppValue(
'password_policy',
'historySize',
0
);
}
public function getExpiryInDays(): int {
return (int)$this->config->getAppValue(
'password_policy',
'expiration',
0
);
}
/**
* @return int if 0 then there is no limit
*/
public function getMaximumLoginAttempts(): int {
return (int)$this->config->getAppValue(
'password_policy',
'maximumLoginAttempts',
0
);
}
}
@@ -0,0 +1,93 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy;
use OC\HintException;
use OCA\Password_Policy\Validator\CommonPasswordsValidator;
use OCA\Password_Policy\Validator\HIBPValidator;
use OCA\Password_Policy\Validator\IValidator;
use OCA\Password_Policy\Validator\LengthValidator;
use OCA\Password_Policy\Validator\NumericCharacterValidator;
use OCA\Password_Policy\Validator\SpecialCharactersValidator;
use OCA\Password_Policy\Validator\UpperCaseLoweCaseValidator;
use OCP\AppFramework\IAppContainer;
use OCP\AppFramework\QueryException;
use OCP\ILogger;
class PasswordValidator {
/** @var IAppContainer */
private $container;
/** @var ILogger */
private $logger;
public function __construct(IAppContainer $container, ILogger $logger) {
$this->container = $container;
$this->logger = $logger;
}
/**
* check if the given password matches the conditions defined by the admin
*
* @throws HintException
*/
public function validate(string $password): void {
$validators = [
CommonPasswordsValidator::class,
LengthValidator::class,
NumericCharacterValidator::class,
UpperCaseLoweCaseValidator::class,
SpecialCharactersValidator::class,
HIBPValidator::class,
];
$errors = [];
$hints = [];
foreach ($validators as $validator) {
try {
/** @var IValidator $instance */
$instance = $this->container->query($validator);
} catch (QueryException $e) {
//ignore and continue
$this->logger->logException($e, ['level' => ILogger::INFO]);
continue;
}
try {
$instance->validate($password);
} catch (HintException $e) {
$errors[] = $e->getMessage();
$hints[] = $e->getHint();
}
}
if (!empty($errors)) {
throw new HintException(
implode(' ', $errors),
implode(' ', $hints)
);
}
}
}
@@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
/**
* @copyright 2017, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\IInitialStateService;
use OCP\Settings\ISettings;
use OCP\Util;
class Settings implements ISettings {
private $appName;
/** @var PasswordPolicyConfig */
private $config;
/** @var IInitialStateService */
private $initialStateService;
public function __construct(string $appName,
PasswordPolicyConfig $config,
IInitialStateService $initialStateService) {
$this->appName = $appName;
$this->config = $config;
$this->initialStateService = $initialStateService;
}
public function getForm(): TemplateResponse {
Util::addScript($this->appName, 'password_policy-settings');
$this->initialStateService->provideInitialState($this->appName, 'config', [
'minLength' => $this->config->getMinLength(),
'enforceNonCommonPassword' => $this->config->getEnforceNonCommonPassword(),
'enforceUpperLowerCase' => $this->config->getEnforceUpperLowerCase(),
'enforceNumericCharacters' => $this->config->getEnforceNumericCharacters(),
'enforceSpecialCharacters' => $this->config->getEnforceSpecialCharacters(),
'enforceHaveIBeenPwned' => $this->config->getEnforceHaveIBeenPwned(),
'historySize' => $this->config->getHistorySize(),
'expiration' => $this->config->getExpiryInDays(),
'maximumLoginAttempts' => $this->config->getMaximumLoginAttempts(),
]);
return new TemplateResponse($this->appName, 'settings');
}
public function getSection(): string {
return 'security';
}
public function getPriority(): int {
return 50;
}
}
@@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy\Validator;
use OC\HintException;
use OCA\Password_Policy\PasswordPolicyConfig;
use OCP\IL10N;
class CommonPasswordsValidator implements IValidator {
/** @var PasswordPolicyConfig */
private $config;
/** @var IL10N */
private $l;
public function __construct(PasswordPolicyConfig $config, IL10N $l) {
$this->config = $config;
$this->l = $l;
}
public function validate(string $password): void {
$enforceNonCommonPassword = $this->config->getEnforceNonCommonPassword();
$passwordFile = __DIR__ . '/../../lists/list-'.strlen($password).'.php';
if ($enforceNonCommonPassword && file_exists($passwordFile)) {
$commonPasswords = require_once $passwordFile;
if (isset($commonPasswords[strtolower($password)])) {
$message = 'Password is among the 1,000,000 most common ones. Please make it unique.';
$message_t = $this->l->t(
'Password is among the 1,000,000 most common ones. Please make it unique.'
);
throw new HintException($message, $message_t);
}
}
}
}
@@ -0,0 +1,90 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy\Validator;
use OC\HintException;
use OCA\Password_Policy\PasswordPolicyConfig;
use OCP\Http\Client\IClientService;
use OCP\IL10N;
use OCP\ILogger;
class HIBPValidator implements IValidator {
/** @var PasswordPolicyConfig */
private $config;
/** @var IL10N */
private $l;
/** @var IClientService */
private $clientService;
/** @var ILogger */
private $logger;
public function __construct(PasswordPolicyConfig $config,
IL10N $l,
IClientService $clientService,
ILogger $logger) {
$this->config = $config;
$this->l = $l;
$this->clientService = $clientService;
$this->logger = $logger;
}
public function validate(string $password): void {
if ($this->config->getEnforceHaveIBeenPwned()) {
$hash = sha1($password);
$range = substr($hash, 0, 5);
$needle = strtoupper(substr($hash, 5));
$client = $this->clientService->newClient();
try {
$response = $client->get(
'https://api.pwnedpasswords.com/range/' . $range,
[
'timeout' => 5,
'headers' => [
'Add-Padding' => 'true'
]
]
);
} catch (\Exception $e) {
$this->logger->logException($e, ['level' => ILogger::INFO]);
return;
}
$result = $response->getBody();
$result = preg_replace('/^([0-9A-Z]+:0)$/m', '', $result);
if (strpos($result, $needle) !== false) {
$message = 'Password is present in compromised password list. Please choose a different password.';
$message_t = $this->l->t(
'Password is present in compromised password list. Please choose a different password.'
);
throw new HintException($message, $message_t);
}
}
}
}
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy\Validator;
use OC\HintException;
interface IValidator {
/**
* @throws HintException
*/
public function validate(string $password): void;
}
@@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy\Validator;
use OC\HintException;
use OCA\Password_Policy\PasswordPolicyConfig;
use OCP\IL10N;
class LengthValidator implements IValidator {
/** @var PasswordPolicyConfig */
private $config;
/** @var IL10N */
private $l;
public function __construct(PasswordPolicyConfig $config, IL10N $l) {
$this->config = $config;
$this->l = $l;
}
public function validate(string $password): void {
$minLength = $this->config->getMinLength();
if (strlen($password) < $minLength) {
$message = 'Password needs to be at least ' . $minLength . ' characters long.';
$message_t = $this->l->t(
'Password needs to be at least %s characters long.', [$minLength]
);
throw new HintException($message, $message_t);
}
}
}
@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy\Validator;
use OC\HintException;
use OCA\Password_Policy\PasswordPolicyConfig;
use OCP\IL10N;
class NumericCharacterValidator implements IValidator {
/** @var PasswordPolicyConfig */
private $config;
/** @var IL10N */
private $l;
public function __construct(PasswordPolicyConfig $config, IL10N $l) {
$this->config = $config;
$this->l = $l;
}
public function validate(string $password): void {
$enforceNumericCharacters = $this->config->getEnforceNumericCharacters();
if ($enforceNumericCharacters) {
if (preg_match('/^(?=.*\d).+$/', $password) !== 1) {
$message = 'Password needs to contain at least one numeric character.';
$message_t = $this->l->t(
'Password needs to contain at least one numeric character.'
);
throw new HintException($message, $message_t);
}
}
}
}
@@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy\Validator;
use OC\HintException;
use OCA\Password_Policy\PasswordPolicyConfig;
use OCP\IL10N;
class SpecialCharactersValidator implements IValidator {
/** @var PasswordPolicyConfig */
private $config;
/** @var IL10N */
private $l;
public function __construct(PasswordPolicyConfig $config, IL10N $l) {
$this->config = $config;
$this->l = $l;
}
public function validate(string $password): void {
$enforceSpecialCharacters = $this->config->getEnforceSpecialCharacters();
if ($enforceSpecialCharacters && ctype_alnum($password)) {
$message = 'Password needs to contain at least one special character.';
$message_t = $this->l->t(
'Password needs to contain at least one special character.'
);
throw new HintException($message, $message_t);
}
}
}
@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Password_Policy\Validator;
use OC\HintException;
use OCA\Password_Policy\PasswordPolicyConfig;
use OCP\IL10N;
class UpperCaseLoweCaseValidator implements IValidator {
/** @var PasswordPolicyConfig */
private $config;
/** @var IL10N */
private $l;
public function __construct(PasswordPolicyConfig $config, IL10N $l) {
$this->config = $config;
$this->l = $l;
}
public function validate(string $password): void {
$enforceUpperLowerCase = $this->config->getEnforceUpperLowerCase();
if ($enforceUpperLowerCase) {
if (preg_match('/^(?=.*[a-z])(?=.*[A-Z]).+$/', $password) !== 1) {
$message = 'Password needs to contain at least one lower and one upper case character.';
$message_t = $this->l->t(
'Password needs to contain at least one lower and one upper case character.'
);
throw new HintException($message, $message_t);
}
}
}
}