Skip to content

Commit

Permalink
add force password change
Browse files Browse the repository at this point in the history
Signed-off-by: Jörn Friedrich Dreyer <[email protected]>
  • Loading branch information
butonic authored and DeepDiver1975 committed Jul 4, 2018
1 parent 2880459 commit 2d4057e
Show file tree
Hide file tree
Showing 36 changed files with 2,442 additions and 696 deletions.
51 changes: 23 additions & 28 deletions appinfo/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,46 +19,41 @@
*
*/

use Symfony\Component\EventDispatcher\GenericEvent;
use OCA\PasswordPolicy\HooksHandler;

OCP\Util::connectHook('\OC\Share', 'verifyExpirationDate', \OCA\PasswordPolicy\HooksHandler::class, 'updateLinkExpiry');
$handler = new HooksHandler();

OCP\Util::connectHook(
'\OC\Share', 'verifyExpirationDate',
$handler, 'updateLinkExpiry'
);

$eventDispatcher = \OC::$server->getEventDispatcher();
$eventDispatcher->addListener(
'OCP\User::validatePassword',
function($event) {
if ($event instanceof GenericEvent) {
HooksHandler::verifyPassword(
$event->getArguments()['password'],
$event->getArguments()['uid']
);
}
}
[$handler, 'verifyPassword']
);
$eventDispatcher->addListener(
'OCP\Share::validatePassword',
function($event) {
if ($event instanceof GenericEvent) {
HooksHandler::verifyPassword($event->getArguments()['password']);
}
}
[$handler, 'verifyPassword']
);
$eventDispatcher->addListener(
'OCP\User::createPassword',
function($event) {
if ($event instanceof GenericEvent) {
$event['password'] = HooksHandler::generatePassword();
$event->stopPropagation();
}
}
[$handler, 'generatePassword']
);
\OC::$server->getEventDispatcher()->addListener(
$eventDispatcher->addListener(
'user.aftersetpassword',
function (GenericEvent $event) {
HooksHandler::saveOldPassword(
$event->getArgument('user'),
$event->getArgument('password')
);
}
[$handler, 'saveOldPassword']
);
$eventDispatcher->addListener(
'user.afterlogin',
[$handler, 'checkPasswordExpired']
);
$eventDispatcher->addListener(
'user.afterlogin',
[$handler, 'checkAdminRequestedPasswordChange']
);
$eventDispatcher->addListener(
\OCP\IUser::class . '::firstLogin',
[$handler, 'checkForcePasswordChangeOnFirstLogin']
);
7 changes: 7 additions & 0 deletions appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ The definition of certain password rules support administrators in the task of e
<admin>OCA\PasswordPolicy\Controller\SettingsController</admin>
</settings>
<category>security</category>
<commands>
<command>OCA\PasswordPolicy\Command\ExpirePassword</command>
</commands>
<account-modules>
<module>OCA\PasswordPolicy\Authentication\AccountModule</module>
</account-modules>

<screenshot>https://raw.githubusercontent.com/owncloud/screenshots/master/password_policy/owncloud-app-password_policy.jpg</screenshot>
<screenshot>https://raw.githubusercontent.com/owncloud/screenshots/master/password_policy/owncloud-app-password_policy2.jpg</screenshot>
<use-migrations>true</use-migrations>
Expand Down
2 changes: 2 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@

return ['routes' => [
['name' => 'Settings#updatePolicy', 'url' => '/update_policy', 'verb' => 'POST'],
['name' => 'password#show', 'url' => '/update_password', 'verb' => 'GET'],
['name' => 'password#update', 'url' => '/update_password', 'verb' => 'POST'],
]];
5 changes: 5 additions & 0 deletions css/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,8 @@
#password_policy .indented {
margin-left: 24px;
}

#new_password.password-mismatch,
#confirm_password.password-mismatch {
color: red;
}
38 changes: 38 additions & 0 deletions js/password.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* @author Jörn Friedrich Dreyer <[email protected]>
*
* @copyright Copyright (c) 2018, ownCloud GmbH
* @license GPL-2.0
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
$(document).ready(function() {
var $newPassword = $("#password_policy #new_password"),
$confirmPassword = $("#password_policy #confirm_password"),
$submit = $("#password_policy #submit"),
check = function() {
if ($confirmPassword.val() !== '' && $newPassword.val() !== $confirmPassword.val()) {
$newPassword.addClass('password-mismatch');
$confirmPassword.addClass('password-mismatch');
$submit.attr('disabled', 'disabled');
} else {
$newPassword.removeClass('password-mismatch');
$confirmPassword.removeClass('password-mismatch');
$submit.removeAttr('disabled');
}
};
$newPassword.keyup(check);
$confirmPassword.keyup(check);
});
75 changes: 75 additions & 0 deletions lib/Authentication/AccountModule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php
/**
* @author Jörn Friedrich Dreyer <[email protected]>
*
* @copyright Copyright (c) 2018, ownCloud GmbH
* @license GPL-2.0
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

namespace OCA\PasswordPolicy\Authentication;

use OCP\Authentication\Exceptions\AccountCheckException;
use OCP\AppFramework\Http\RedirectResponse;
use OCP\Authentication\IAccountModule;
use OCP\IRequest;
use OCP\ISession;
use OCP\IURLGenerator;
use OCP\IUser;

class AccountModule implements IAccountModule {

/**
* @var ISession
*/
private $session;
/**
* @var IRequest
*/
private $request;
/**
* @var IURLGenerator
*/
private $urlGenerator;

public function __construct(ISession $session, IRequest $request, IURLGenerator $urlGenerator) {
$this->session = $session;
$this->request = $request;
$this->urlGenerator = $urlGenerator;
}

/**
* Checks a session variable to not interfere with existing sessions
*
* @param IUser $user
* @throws AccountCheckException
*/
public function check(IUser $user) {
$forcePasswordChange = $this->session->get('password_policy.forcePasswordChange');
if ($forcePasswordChange) {
$redirectUrl = $this->request->getRequestUri();
$response = new RedirectResponse(
$this->urlGenerator->linkToRoute( 'password_policy.password.show', [
'redirect_url' => $redirectUrl
])
);
// full list of error codes https://msdn.microsoft.com/en-us/library/windows/desktop/ms681386(v=vs.85).aspx
// ldap relevant: https://github.com/rancher/rancher/issues/1941#issue-104315860
// we use 'user must reset password', not 'password expired' because the letter indicates password cannot be changed
throw new AccountCheckException($response, 'The user must reset their password.', 0x773);
}
}
}
108 changes: 108 additions & 0 deletions lib/Command/ExpirePassword.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?php
/**
* @author Jörn Friedrich Dreyer <[email protected]>
*
* @copyright Copyright (c) 2018, ownCloud GmbH
* @license GPL-2.0
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

namespace OCA\PasswordPolicy\Command;

use OCP\IConfig;
use OCP\IUserManager;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;


class ExpirePassword extends Command {
/** @var \OCP\IConfig */
private $config;

/** @var \OCP\IUserManager */
protected $userManager;

/**
* @param IConfig $config
* @param IUserManager $userManager
*/
public function __construct(IConfig $config, IUserManager $userManager) {
parent::__construct();
$this->config = $config;
$this->userManager = $userManager;
}

protected function configure() {
$this
->setName('user:expire-password')
->setDescription('Expire a user\'s password')
->addArgument(
'uid',
InputArgument::REQUIRED,
'The user\'s ownCloud uid (username).'
)
->addArgument(
'expiredate',
InputArgument::OPTIONAL,
'The date and time when a password expires, e.g. "2019-01-01 14:00:00 CET".',
'-1 days' // base.php sets timezone to utc, so
// make sure date is in the past
)
;
}

/**
* sets a user value as a flag in the user config that will be checked by
* the AccountModule on login.
*
* @param InputInterface $input
* @param OutputInterface $output
* @return int
* @throws \OCP\PreConditionNotMetException
*/
protected function execute(InputInterface $input, OutputInterface $output) {
$uid = $input->getArgument('uid');

$exists = $this->userManager->userExists($uid);
if($exists === false) {
$output->writeln("<error>Unknown user: $uid</error>");
/**
* return EX_NOUSER from /usr/include/sysexits.h
* @see http://tldp.org/LDP/abs/html/exitcodes.html#FTN.AEN23647
*/
return 67;
}

$date = new \DateTime($input->getArgument('expiredate'));
$date->setTimezone(new \DateTimeZone('UTC'));
$value = $date->format('Y-m-d\TH:i:s\Z'); // ISO8601 with Zulu = UTC

$this->config->setUserValue(
$uid,
'password_policy',
'forcePasswordChange',
$value
);

// show expire date if it was given
if ($input->hasArgument('expiredate')) {
$output->writeln("The password for $uid is set to expire on ". $date->format('Y-m-d H:i:s T').'.');
}
return 0;
}
}
Loading

0 comments on commit 2d4057e

Please sign in to comment.