Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/password_policy.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorArthur Schiwon <blizzz@arthur-schiwon.de>2020-04-04 00:08:31 +0300
committerArthur Schiwon <blizzz@arthur-schiwon.de>2020-04-07 23:49:52 +0300
commit3832ad7e40a6814c11be0776898fcf133be0482f (patch)
treebc59edb7330e563faa49c275e0c2c628478c4b2f /lib
parentac332c3c491e813199fb0a3a58a1ceb446cedb04 (diff)
password expiration support
Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
Diffstat (limited to 'lib')
-rw-r--r--lib/AppInfo/Application.php26
-rw-r--r--lib/Compliance/Expiration.php113
-rw-r--r--lib/Compliance/HistoryCompliance.php (renamed from lib/HistoryCompliance.php)5
-rw-r--r--lib/Compliance/IAuditor.php31
-rw-r--r--lib/Compliance/IEntryControl.php36
-rw-r--r--lib/Compliance/IUpdatable.php31
-rw-r--r--lib/ComplianceService.php120
-rw-r--r--lib/PasswordPolicyConfig.php8
-rw-r--r--lib/Settings.php1
9 files changed, 362 insertions, 9 deletions
diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php
index 8606177..b3e6557 100644
--- a/lib/AppInfo/Application.php
+++ b/lib/AppInfo/Application.php
@@ -25,8 +25,8 @@ declare(strict_types=1);
namespace OCA\Password_Policy\AppInfo;
use OCA\Password_Policy\Capabilities;
+use OCA\Password_Policy\ComplianceService;
use OCA\Password_Policy\Generator;
-use OCA\Password_Policy\HistoryCompliance;
use OCA\Password_Policy\PasswordValidator;
use OCP\AppFramework\App;
use OCP\EventDispatcher\Event;
@@ -36,6 +36,7 @@ use OCP\Security\Events\GenerateSecurePasswordEvent;
use OCP\Security\Events\ValidatePasswordPolicyEvent;
use OCP\User\Events\BeforePasswordUpdatedEvent;
use OCP\User\Events\PasswordUpdatedEvent;
+use OCP\User\Events\PostLoginEvent;
use Symfony\Component\EventDispatcher\GenericEvent;
class Application extends App {
@@ -80,9 +81,9 @@ class Application extends App {
if(!($event instanceof BeforePasswordUpdatedEvent)) {
return;
}
- /** @var HistoryCompliance $historyCompliance */
- $historyCompliance = $container->query(HistoryCompliance::class);
- $historyCompliance->audit($event->getUser(), $event->getPassword());
+ /** @var ComplianceService $complianceUpdater */
+ $complianceUpdater = $container->query(ComplianceService::class);
+ $complianceUpdater->audit($event->getUser(), $event->getPassword());
}
);
$eventDispatcher->addListener(
@@ -91,9 +92,20 @@ class Application extends App {
if(!($event instanceof PasswordUpdatedEvent)) {
return;
}
- /** @var HistoryCompliance $historyCompliance */
- $historyCompliance = $container->query(HistoryCompliance::class);
- $historyCompliance->update($event->getUser(), $event->getPassword());
+ /** @var ComplianceService $complianceUpdater */
+ $complianceUpdater = $container->query(ComplianceService::class);
+ $complianceUpdater->update($event->getUser(), $event->getPassword());
+ }
+ );
+ $eventDispatcher->addListener(
+ PostLoginEvent::class,
+ function (Event $event) use ($container) {
+ if(!$event instanceof PostLoginEvent) {
+ return;
+ }
+ /** @var ComplianceService $complianceUpdater */
+ $complianceUpdater = $container->query(ComplianceService::class);
+ $complianceUpdater->entryControl($event->getUser(), $event->getPassword(), $event->isTokenLogin());
}
);
diff --git a/lib/Compliance/Expiration.php b/lib/Compliance/Expiration.php
new file mode 100644
index 0000000..ff50edb
--- /dev/null
+++ b/lib/Compliance/Expiration.php
@@ -0,0 +1,113 @@
+<?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->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, bool $isTokenLogin): void {
+ if($this->policyConfig->getExpiryInDays() !== 0
+ && !$isTokenLogin
+ && $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();
+ }
+
+}
diff --git a/lib/HistoryCompliance.php b/lib/Compliance/HistoryCompliance.php
index 413f0b1..cea7b99 100644
--- a/lib/HistoryCompliance.php
+++ b/lib/Compliance/HistoryCompliance.php
@@ -22,9 +22,10 @@ declare(strict_types=1);
*
*/
-namespace OCA\Password_Policy;
+namespace OCA\Password_Policy\Compliance;
use OC\HintException;
+use OCA\Password_Policy\PasswordPolicyConfig;
use OCP\IConfig;
use OCP\IL10N;
use OCP\ILogger;
@@ -33,7 +34,7 @@ use OCP\IUserSession;
use OCP\PreConditionNotMetException;
use OCP\Security\IHasher;
-class HistoryCompliance {
+class HistoryCompliance implements IAuditor, IUpdatable {
/** @var string */
protected $uid;
diff --git a/lib/Compliance/IAuditor.php b/lib/Compliance/IAuditor.php
new file mode 100644
index 0000000..c11eb15
--- /dev/null
+++ b/lib/Compliance/IAuditor.php
@@ -0,0 +1,31 @@
+<?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;
+}
diff --git a/lib/Compliance/IEntryControl.php b/lib/Compliance/IEntryControl.php
new file mode 100644
index 0000000..febe775
--- /dev/null
+++ b/lib/Compliance/IEntryControl.php
@@ -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, bool $isTokenLogin): void;
+}
diff --git a/lib/Compliance/IUpdatable.php b/lib/Compliance/IUpdatable.php
new file mode 100644
index 0000000..9c64ce5
--- /dev/null
+++ b/lib/Compliance/IUpdatable.php
@@ -0,0 +1,31 @@
+<?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;
+}
diff --git a/lib/ComplianceService.php b/lib/ComplianceService.php
new file mode 100644
index 0000000..bd96f71
--- /dev/null
+++ b/lib/ComplianceService.php
@@ -0,0 +1,120 @@
+<?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) {
+ foreach ($this->getInstance(IEntryControl::class) as $instance) {
+ try {
+ $instance->entryControl($user, $password, $isTokenLogin);
+ } catch (HintException $e) {
+ if($this->userSession->isLoggedIn()) {
+ $this->userSession->logout();
+ }
+ 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;
+ }
+ }
+}
diff --git a/lib/PasswordPolicyConfig.php b/lib/PasswordPolicyConfig.php
index ffd4f75..80188b9 100644
--- a/lib/PasswordPolicyConfig.php
+++ b/lib/PasswordPolicyConfig.php
@@ -185,4 +185,12 @@ class PasswordPolicyConfig {
);
}
+ public function getExpiryInDays(): int {
+ return (int)$this->config->getAppValue(
+ 'password_policy',
+ 'expiration',
+ 0
+ );
+ }
+
}
diff --git a/lib/Settings.php b/lib/Settings.php
index b87dded..60a6a4f 100644
--- a/lib/Settings.php
+++ b/lib/Settings.php
@@ -46,6 +46,7 @@ class Settings implements ISettings {
'enforceSpecialCharacters' => $this->config->getEnforceSpecialCharacters(),
'enforceHaveIBeenPwned' => $this->config->getEnforceHaveIBeenPwned(),
'historySize' => $this->config->getHistorySize(),
+ 'expiration' => $this->config->getExpiryInDays(),
]);
return $response;