An error occurred while loading the file. Please try again.
-
Côme Chilliet authored
This replaces save_object and execute methods by 3 methods: readPost - Reads POST data update - Update object state render - Render HTML UI The point is to avoid reading POST and rendering HTML when this is not needed (when doing stuff through the webservice for instance). It’s also more consisent across FD with all classes handling some kind of dialog implementing the new interface FusionDirectoryDialog which makes sure these 3 methods are implemented. issue #6072
Unverified94eaa6ba
<?php
/*
This code is part of FusionDirectory (http://www.fusiondirectory.org/)
Copyright (C) 2003-2010 Cajus Pollmeier
Copyright (C) 2011-2018 FusionDirectory
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, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
class passwordRecovery extends standAlonePage
{
protected $loginAttribute;
protected $login;
protected $email_address;
protected $message;
protected $step;
/* Salt needed to mask the uniq id in the ldap */
protected $salt;
/* Uniq ID recovered from email */
protected $uniq;
/* Delay allowed for the user to change his password (minutes) */
protected $delay_allowed;
/* Sender */
protected $from_mail;
protected $mail_body;
protected $mail_subject;
protected $mail2_body;
protected $mail2_subject;
protected $usealternates;
function init ()
{
parent::init();
$this->step = 1;
$this->message = [];
if (isset($_GET['email_address']) && ($_GET['email_address'] != '')) {
$this->email_address = validate($_GET['email_address']);
} elseif (isset($_POST['email_address'])) {
$this->email_address = validate($_POST['email_address']);
}
/* Check for selected user... */
if (isset($_GET['login']) && $_GET['login'] != '') {
$this->login = validate($_GET['login']);
} elseif (isset($_POST['login'])) {
$this->login = validate($_POST['login']);
} else {
$this->login = '';
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
}
}
function readPost ()
{
if (!$this->activated) {
return;
}
/* Got a formular answer, validate and try to log in */
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if (session::is_set('_LAST_PAGE_REQUEST')) {
session::set('_LAST_PAGE_REQUEST', time());
}
if (isset($_POST['change'])) {
$this->step4();
} elseif (isset($_POST['apply'])) {
if ($_POST['email_address'] == '') {
$this->message[] = new FusionDirectoryError(msgPool::required(_('Email address')));
return;
}
$this->email_address = $_POST['email_address'];
$this->step2();
if ($this->step == 2) { /* No errors */
$this->step3();
}
}
} elseif ($_SERVER['REQUEST_METHOD'] == 'GET') {
if (isset($_GET['uniq'])) {
$this->step4();
}
}
}
function execute ()
{
$this->readPost();
/* Do we need to show error messages? */
if (count($this->message) != 0) {
/* Show error message and continue editing */
msg_dialog::displayChecks($this->message);
}
logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->step, "Step");
$smarty = get_smarty();
$this->assignSmartyVars();
$smarty->append('js_files', 'include/pwdStrength.js');
$smarty->append('css_files', get_template_path('login.css'));
$smarty->assign('title', _('Password recovery'));
$smarty->display(get_template_path('headers.tpl'));
$smarty->assign('step', $this->step);
$smarty->assign('delay_allowed', $this->delay_allowed);
$smarty->assign('activated', $this->activated);
$smarty->assign('email_address', $this->email_address);
$smarty->display(get_template_path('recovery.tpl'));
exit();
}
/* Check that password recovery is activated, read config in ldap
* Returns a boolean saying if password recovery is activated
*/
protected function readLdapConfig (): bool
{
global $config;
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
$this->salt = $config->get_cfg_value('passwordRecoverySalt');
$this->delay_allowed = $config->get_cfg_value('passwordRecoveryValidity');
$this->mail_subject = $config->get_cfg_value('passwordRecoveryMailSubject');
$this->mail_body = $config->get_cfg_value('passwordRecoveryMailBody');
$this->mail2_subject = $config->get_cfg_value('passwordRecoveryMail2Subject');
$this->mail2_body = $config->get_cfg_value('passwordRecoveryMail2Body');
$this->from_mail = $config->get_cfg_value('passwordRecoveryEmail');
$this->usealternates = $config->get_cfg_value('passwordRecoveryUseAlternate');
$this->loginAttribute = $config->get_cfg_value('passwordRecoveryLoginAttribute', 'uid');
logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $config->get_cfg_value('passwordRecoveryActivated'), "passwordRecoveryActivated");
return ($config->get_cfg_value('passwordRecoveryActivated') == "TRUE");
}
function storeToken ($temp_password)
{
global $config;
/* Store it in ldap with the salt */
$salt_temp_password = $this->salt.$temp_password.$this->salt;
$sha1_temp_password = "{SHA}".base64_encode(pack("H*", sha1($salt_temp_password)));
$ldap = $config->get_ldap_link();
// Check if token branch is here
$token = get_ou('recoveryTokenRDN').get_ou('fusiondirectoryRDN').$config->current['BASE'];
$ldap->cat($token, ['dn']);
if (!$ldap->count()) {
/* It's not, let's create it */
$ldap->cd($config->current['BASE']);
try {
$ldap->create_missing_trees($token);
} catch (FusionDirectoryError $error) {
return $error;
}
fusiondirectory_log("Created token branch ".$token);
}
$dn = 'ou='.$this->login.','.$token;
$ldap->cat($dn, ['dn']);
$add = ($ldap->count() == 0);
/* We store the token and its validity due date */
$attrs = [
'objectClass' => ['organizationalUnit'],
'ou' => $this->login,
'userPassword' => $sha1_temp_password,
'description' => time() + $this->delay_allowed * 60,
];
$ldap->cd($dn);
if ($add) {
$ldap->add($attrs);
} else {
$ldap->modify($attrs);
}
if (!$ldap->success()) {
return new SimplePluginLdapError(
NULL,
$dn,
($add ? LDAP_ADD : LDAP_MOD),
$ldap->get_error(),
$ldap->get_errno()
);
}
/* Everything went well */
return NULL;
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
}
function checkToken ($token)
{
global $config;
$salt_token = $this->salt.$token.$this->salt;
$sha1_token = "{SHA}".base64_encode(pack("H*", sha1($salt_token)));
/* Retrieve hash from the ldap */
$ldap = $config->get_ldap_link();
$token = get_ou('recoveryTokenRDN').get_ou('fusiondirectoryRDN').$config->current['BASE'];
$dn = 'ou='.$this->login.','.$token;
$ldap->cat($dn);
$attrs = $ldap->fetch();
$ldap_token = $attrs['userPassword'][0];
$last_time_recovery = $attrs['description'][0];
/* Return TRUE if the token match and is still valid */
return ($last_time_recovery >= time()) &&
($ldap_token == $sha1_token);
}
function getUserDn ()
{
global $config;
/* Retrieve dn from the ldap */
$ldap = $config->get_ldap_link();
$objectClasses = ['gosaMailAccount'];
if (class_available('personalInfo') && ($config->get_cfg_value('privateEmailPasswordRecovery', 'FALSE') == 'TRUE')) {
$objectClasses[] = 'fdPersonalInfo';
}
if (class_available('supannAccount') && ($config->get_cfg_value('supannPasswordRecovery', 'TRUE') == 'TRUE')) {
$objectClasses[] = 'supannPerson';
}
$filter = '(&(|(objectClass='.join(')(objectClass=', $objectClasses).'))('.$this->loginAttribute.'='.ldap_escape_f($this->login).'))';
$ldap->cd($config->current['BASE']);
$ldap->search($filter, ['dn']);
if ($ldap->count() < 1) {
$this->message[] = new FusionDirectoryError(htmlescape(sprintf(_('Did not find an account with login "%s"'), $this->login)));
return;
} elseif ($ldap->count() > 1) {
$this->message[] = new FusionDirectoryError(htmlescape(sprintf(_('Found multiple accounts with login "%s"'), $this->login)));
return;
}
$attrs = $ldap->fetch();
return $attrs['dn'];
}
/* Find the login of for the given email address */
function step2 ($email = NULL)
{
global $config;
if ($email !== NULL) {
/* Special case when recovery is called from webservice */
$this->email_address = $email;
}
/* Search login corresponding to the mail */
$address_escaped = ldap_escape_f($this->email_address);
if ($this->usealternates) {
$filter = '(&(objectClass=gosaMailAccount)(|(mail='.$address_escaped.')(gosaMailAlternateAddress='.$address_escaped.')))';
} else {
$filter = '(&(objectClass=gosaMailAccount)(mail='.$address_escaped.'))';
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
}
if (class_available('personalInfo') && ($config->get_cfg_value('privateEmailPasswordRecovery', 'FALSE') == 'TRUE')) {
$filter = '(|'.$filter.'(&(objectClass=fdPersonalInfo)(fdPrivateMail='.$address_escaped.')))';
}
if (class_available('supannAccount') && ($config->get_cfg_value('supannPasswordRecovery', 'TRUE') == 'TRUE')) {
$filter = '(|'.$filter.'(&(objectClass=supannPerson)(supannMailPerso='.$address_escaped.')))';
}
$ldap = $config->get_ldap_link();
$ldap->cd($config->current['BASE']);
$ldap->search($filter, ['dn', 'userPassword', $this->loginAttribute]);
/* Only one ldap node should be found */
if ($ldap->count() < 1) {
$this->message[] = new FusionDirectoryError(htmlescape(sprintf(_('There is no account using email "%s"'), $this->email_address)));
return FALSE;
} elseif ($ldap->count() > 1) {
$this->message[] = new FusionDirectoryError(htmlescape(sprintf(_('There are several accounts using email "%s"'), $this->email_address)));
return FALSE;
}
$attrs = $ldap->fetch();
$method = passwordMethod::get_method($attrs['userPassword'][0], $attrs['dn']);
if (is_object($method) && $method->is_locked($attrs['dn'])) {
$this->message[] = new FusionDirectoryError(htmlescape(sprintf(_('The user using email "%s" is locked. Please contact your administrator.'), $this->email_address)));
return FALSE;
}
$this->login = $attrs[$this->loginAttribute][0];
$this->step = 2;
if ($this->interactive) {
$smarty = get_smarty();
$smarty->assign('login', $this->login);
$smarty->assign('email_address', $this->email_address);
$params = $this->encodeParams(['login', 'directory', 'email_address']);
$smarty->assign('params', $params);
}
return $attrs['dn'];
}
function generateAndStoreToken ()
{
$activatecode = static::generateRandomHash();
$error = $this->storeToken($activatecode);
if (!empty($error)) {
$this->message[] = $error;
return FALSE;
}
return $activatecode;
}
/* generate a token and send it by email */
function step3 ()
{
/* Send a mail, save information in session and create a very random unique id */
$token = $this->generateAndStoreToken();
if ($token === FALSE) {
return;
}
$reinit_link = URL::getPageURL();
$reinit_link .= '?uniq='.urlencode($token);
$reinit_link .= '&login='.urlencode($this->login);
$reinit_link .= '&email_address='.urlencode($this->email_address);
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $reinit_link, 'Setting link to');
/* Send the mail */
$body = sprintf($this->mail_body, $this->login, $reinit_link);
if (mail_utf8($this->email_address, FALSE, $this->from_mail, $this->mail_subject, $body)) {
$this->step = 3;
} else {
$this->message[] = new FusionDirectoryError(htmlescape(_('Contact your administrator, there was a problem with mail server')));
}
$smarty = get_smarty();
$smarty->assign('login', $this->login);
}
/* check if the given token is the good one */
function step4 ()
{
$uniq_id_from_mail = validate($_GET['uniq']);
if (!$this->checkToken($uniq_id_from_mail)) {
$this->message[] = new FusionDirectoryError(htmlescape(_('This token is invalid')));
return;
}
$smarty = get_smarty();
$smarty->assign('uniq', $uniq_id_from_mail);
$this->uniq = $uniq_id_from_mail;
$this->step = 4;
$smarty->assign('login', $this->login);
$params = $this->encodeParams(['login', 'directory', 'email_address', 'uniq']);
$smarty->assign('params', $params);
if (isset($_POST['change'])) {
$this->step5();
}
}
function changeUserPassword ($new_password, $new_password_repeated)
{
$dn = $this->getUserDn();
if (!$dn) {
return FALSE;
}
$userTabs = objects::open($dn, 'user');
$userTab = $userTabs->getBaseObject();
$userTab->userPassword = [
'',
$new_password,
$new_password_repeated,
$userTab->userPassword,
$userTab->attributesAccess['userPassword']->isLocked()
];
/* Is there any problem with entered passwords? */
$userTabs->update();
$errors = $userTabs->save();
if (!empty($errors)) {
$this->message = $errors;
return;
}
fusiondirectory_log('User '.$this->login.' password has been changed');
return TRUE;
}
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
/* change the password and send confirmation email */
function step5 ()
{
$success = $this->changeUserPassword($_POST['new_password'], $_POST['new_password_repeated']);
if (!$success) {
return;
}
/* Send the mail */
$body = sprintf($this->mail2_body, $this->login);
if (mail_utf8($this->email_address, FALSE, $this->from_mail, $this->mail2_subject, $body)) {
$smarty = get_smarty();
$this->step = 5;
$smarty->assign('changed', TRUE);
} else {
$this->message[] = new FusionDirectoryError(htmlescape(_('There was a problem with mail server, confirmation email not sent')));
}
}
function getErrorMessages (): array
{
return $this->message;
}
function setLogin (string $login)
{
$this->login = $login;
}
function getLogin ()
{
return $this->login;
}
}