Merge pull request #307 from Koumbit/102-password_recovery
Issue #102: Add password reset via one-time login link
This commit is contained in:
		
						commit
						c80814c4eb
					
				| 
						 | 
				
			
			@ -87,7 +87,8 @@ if ( empty($logo) ||  ! $logo ) {
 | 
			
		|||
	</div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="block_login_page">
 | 
			
		||||
 | 
			
		||||
        <a href="request_reset.php"><?php echo _('Request new password'); ?></a>
 | 
			
		||||
        <br />
 | 
			
		||||
        <?php __("You must accept the session cookie to log-in"); ?>
 | 
			
		||||
        <br />
 | 
			
		||||
        <?php echo _("If you want to use a different language, choose it in the list below"); ?>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -81,7 +81,9 @@ echo "<p>";
 | 
			
		|||
<input type="password" style="display: none" id="fakePassword" name="fakePassword" value="" />
 | 
			
		||||
 | 
			
		||||
<table border="1" cellspacing="0" cellpadding="4" class="tedit" >
 | 
			
		||||
<tr><th><?php __("Old password"); ?></th><td><input type="password" class="int" name="oldpass" value="<?php isset($oldpass) ? : $oldpass=""; ehe($oldpass); ?>" size="20" maxlength="128" /></td></tr>
 | 
			
		||||
<?php if ($mem->requires_old_password_for_change()): ?>
 | 
			
		||||
    <tr><th><?php __("Old password"); ?></th><td><input type="password" class="int" name="oldpass" value="<?php isset($oldpass) ? : $oldpass=""; ehe($oldpass); ?>" size="20" maxlength="128" /></td></tr>
 | 
			
		||||
<?php endif; ?>
 | 
			
		||||
<tr><th><?php __("New password"); ?> (1)</th><td><input type="password" class="int" autocomplete="off" id="newpass" name="newpass" value="<?php isset($newpass) ? : $newpass=""; ehe($newpass);  ?>" size="20" maxlength="60" /><?php display_div_generate_password(DEFAULT_PASS_SIZE,"#newpass","#newpass2",$passwd_classcount); ?></td></tr>
 | 
			
		||||
<tr><th><?php __("New password"); ?> (2)</th><td><input type="password" class="int" autocomplete="off" id="newpass2" name="newpass2" value="<?php isset($newpass2) ? : $newpass2=""; ehe($newpass2);?>" size="20" maxlength="61" /></td></tr>
 | 
			
		||||
<tr class="trbtn"><td colspan="3"><input type="submit" class="inb ok" name="submit" value="<?php __("Change my password"); ?>" /></td></tr>
 | 
			
		||||
| 
						 | 
				
			
			@ -134,8 +136,13 @@ if ($mem->user["su"]) {
 | 
			
		|||
</div> <!-- tabsmem -->
 | 
			
		||||
 | 
			
		||||
<script type="text/javascript">
 | 
			
		||||
document.forms['main'].oldpass.focus();
 | 
			
		||||
$(function() {$( "#tabsmem" ).tabs();});
 | 
			
		||||
    if (document.forms['main'].getElementsByClassName("oldpass").length > 0) {
 | 
			
		||||
        document.forms['main'].oldpass.focus();
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        document.getElementById('newpass').focus();
 | 
			
		||||
    }
 | 
			
		||||
    $(function() {$( "#tabsmem" ).tabs();});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<?php include_once("foot.php"); ?>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,72 @@
 | 
			
		|||
<?php
 | 
			
		||||
 | 
			
		||||
require_once("../class/config_nochk.php");
 | 
			
		||||
 | 
			
		||||
$request = FALSE;
 | 
			
		||||
$valid_request = TRUE;
 | 
			
		||||
if (isset($_REQUEST['name_or_email'])) {
 | 
			
		||||
    $request = TRUE;
 | 
			
		||||
    // Inserted into the global namespace by config.php
 | 
			
		||||
    $valid_request = !$fatalcsrf;
 | 
			
		||||
    if ($fatalcsrf) {
 | 
			
		||||
        $msg->raise('ERROR', _('Failed to validate CSRF token'));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Show the form if nothing was submitted, or if what was submitted is not
 | 
			
		||||
// a valid request (eg. doesn't pass CSRF).
 | 
			
		||||
$show_form =  !$request || ($request && !$valid_request);
 | 
			
		||||
 | 
			
		||||
if ($request && $valid_request) {
 | 
			
		||||
    $mem->send_reset_url($_REQUEST['name_or_email']);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if (!isset($charset) || ! $charset) {
 | 
			
		||||
    $charset="UTF-8";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@header("Content-Type: text/html; charset=$charset");
 | 
			
		||||
require_once("html-head.php");
 | 
			
		||||
?>
 | 
			
		||||
 | 
			
		||||
<body class="login_page">
 | 
			
		||||
    <div id="global">
 | 
			
		||||
        <div id="content">
 | 
			
		||||
            <?php
 | 
			
		||||
            // Getting logo. C.f. admin/index.php
 | 
			
		||||
            $logo = variable_get('logo_login', '' ,'You can specify a logo for the login page, example /images/my_logo.png .', array('desc'=>'URL','type'=>'string'));
 | 
			
		||||
            if ( empty($logo) ||  ! $logo ) {
 | 
			
		||||
                $logo = 'images/logo.png';
 | 
			
		||||
            }
 | 
			
		||||
            ?>
 | 
			
		||||
            <p id='logo'>  <img src="<?php echo $logo; ?>" border="0" height="100px" alt="<?php __("Web Hosting Control Panel"); ?>" title="<?php __("Web Hosting Control Panel"); ?>" />
 | 
			
		||||
            </p>
 | 
			
		||||
            <p> </p>
 | 
			
		||||
            <?php echo $msg->msg_html_all(); ?>
 | 
			
		||||
            <br/>
 | 
			
		||||
            <div class="block_list">
 | 
			
		||||
              <?php if ($show_form): ?>
 | 
			
		||||
                <div class="block_login_page">
 | 
			
		||||
                        <div class="menu-box">
 | 
			
		||||
                            <div class="menu-title"><?php echo _('Password reset'); ?></div>
 | 
			
		||||
                            <form action="request_reset.php" method="post" name="passwordreset">
 | 
			
		||||
                                <?php csrf_get(); ?>
 | 
			
		||||
                                <div>
 | 
			
		||||
                                    <label for="name_or_email"><?php echo _('Username or e-mail'); ?></label>
 | 
			
		||||
                                    <input type="text" class="int" name="name_or_email">
 | 
			
		||||
                                </div>
 | 
			
		||||
                                <div class="submit"><input type="submit" class="inb" name="submit"></div>
 | 
			
		||||
                            </form>
 | 
			
		||||
                        </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="block_list">
 | 
			
		||||
                  <p><?php echo _('An e-mail with instructions will be sent'); ?></p>
 | 
			
		||||
                </div>
 | 
			
		||||
              <?php else: ?>
 | 
			
		||||
                <div><p><a href="index.php"><?php __('Return to login page'); ?></a></p></div>
 | 
			
		||||
              <?php endif; ?>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</body>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,15 @@
 | 
			
		|||
<?php
 | 
			
		||||
 | 
			
		||||
require_once("../class/config_nochk.php");
 | 
			
		||||
 | 
			
		||||
if (isset($_GET['uid']) && isset($_GET['token']) && isset($_GET['timestamp'])) {
 | 
			
		||||
    // We may have received a one-time use link.
 | 
			
		||||
    $logged_in = $mem->temporary_login($_GET['uid'], $_GET['timestamp'],
 | 
			
		||||
                                       $_GET['token']);
 | 
			
		||||
    if ($logged_in) {
 | 
			
		||||
        $msg->raise('INFO', 'admin/reset', _('Please change your password'));
 | 
			
		||||
        header("Location: /mem_param.php");
 | 
			
		||||
        exit;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
header("Location: /index.php");
 | 
			
		||||
| 
						 | 
				
			
			@ -41,7 +41,6 @@ class m_mem {
 | 
			
		|||
     */
 | 
			
		||||
    var $local;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Password kind used in this class (hook for admin class)
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -401,10 +400,14 @@ class m_mem {
 | 
			
		|||
            $msg->raise("ERROR", "mem", _("You are not allowed to change your password."));
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        if (!password_verify($oldpass, $this->user['pass'])) {
 | 
			
		||||
            $msg->raise("ERROR", "mem", _("The old password is incorrect"));
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if ($this->requires_old_password_for_change()) {
 | 
			
		||||
            if (!password_verify($oldpass, $this->user['pass'])) {
 | 
			
		||||
                $msg->raise("ERROR", "mem", _("The old password is incorrect"));
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($newpass != $newpass2) {
 | 
			
		||||
            $msg->raise("ERROR", "mem", _("The new passwords are differents, please retry"));
 | 
			
		||||
            return false;
 | 
			
		||||
| 
						 | 
				
			
			@ -418,11 +421,12 @@ class m_mem {
 | 
			
		|||
        $newpass = password_hash($newpass, PASSWORD_BCRYPT);
 | 
			
		||||
        $db->query("UPDATE membres SET pass= ? WHERE uid= ?;", array($newpass, $cuid));
 | 
			
		||||
        $msg->init_msgs();
 | 
			
		||||
        setcookie('require_old_password', '', 1);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /** 
 | 
			
		||||
    /**
 | 
			
		||||
     * Change the administrator preferences of an admin account
 | 
			
		||||
     * @param integer $admlist visualisation mode of the account list (0=large 1=short)
 | 
			
		||||
     * @return boolean TRUE if the preferences has been changed, FALSE if not.
 | 
			
		||||
| 
						 | 
				
			
			@ -678,4 +682,264 @@ Cordially.
 | 
			
		|||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sends a password-reset URL.
 | 
			
		||||
     */
 | 
			
		||||
    public function send_reset_url($email_or_login) {
 | 
			
		||||
        global $msg, $L_FQDN, $L_HOSTING, $db;
 | 
			
		||||
        // Look up user by email_or_login.
 | 
			
		||||
        $db->query("SELECT * FROM membres WHERE login = ? OR mail = ? ;", array($email_or_login, $email_or_login));
 | 
			
		||||
 | 
			
		||||
        $msg->log('mem', 'send_reset_url', 'Password reset requested for: ' . $email_or_login);
 | 
			
		||||
        // Give user feedback, even if we don't have an account stored.
 | 
			
		||||
        $msg->raise('INFO', 'mem', _('An e-mail with information on how to connect has been sent to the owner of the account if one exists'));
 | 
			
		||||
 | 
			
		||||
        // It is possible here that a user could have multiple accounts for a
 | 
			
		||||
        // single e-mail since 'mail' is not a uniqe key in the membres table.
 | 
			
		||||
        // For the moment we'll just take the first account.
 | 
			
		||||
        if (!$db->num_rows()) {
 | 
			
		||||
            $msg->log('mem', 'send_reset_url', 'No member found with login or mail ' . $email_or_login);
 | 
			
		||||
            return FALSE;
 | 
			
		||||
        }
 | 
			
		||||
        if ($db->num_rows()) {
 | 
			
		||||
            $db->next_record();
 | 
			
		||||
            // Get a reset URL for the current timestamp.
 | 
			
		||||
            $url = $this->generate_reset_url($db->f('uid'));
 | 
			
		||||
            $mail = $db->f('mail');
 | 
			
		||||
        }
 | 
			
		||||
        if (!$url || !$mail) {
 | 
			
		||||
            return FALSE;
 | 
			
		||||
        }
 | 
			
		||||
        $duration = variable_get('password_reset_expiration', 86400, 'The number of seconds for which a password reset link is valid');
 | 
			
		||||
        $duration_hours = ($duration / 3600.0) . ' ' . _('hours');
 | 
			
		||||
        $message = sprintf(_('
 | 
			
		||||
Hi,
 | 
			
		||||
 | 
			
		||||
someone requested a password reset for your account at %s (%s).
 | 
			
		||||
 | 
			
		||||
You may connect to your account and change your account by clicking on the following URL or copying it into your browser :
 | 
			
		||||
 | 
			
		||||
%s
 | 
			
		||||
 | 
			
		||||
This link may only be used once. You should change your password in your account settings once connected. This link will only be valid for %s, and no changes will be made if it is not used.
 | 
			
		||||
'), $L_HOSTING, $L_FQDN, $url, $duration_hours);
 | 
			
		||||
        mail($mail, "Password reset request on {$L_HOSTING}", $message, "From: postmaster@{$L_FQDN}\nReply-to: postmaster@{$L_FQDN}");
 | 
			
		||||
        $msg->log('mem', 'send_reset_url', "Password reset e-mail sent for account {$uid} at {$mail}");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Generate a reset URL for an account given it's e-mail or login.
 | 
			
		||||
     *
 | 
			
		||||
     * @param $email_or_login
 | 
			
		||||
     *   A string with the email or login.
 | 
			
		||||
     *
 | 
			
		||||
     * @returns string|boolean
 | 
			
		||||
     *   A reset URL or FALSE in case of error.
 | 
			
		||||
     */
 | 
			
		||||
    function generate_reset_url($uid) {
 | 
			
		||||
        global $db;
 | 
			
		||||
        $db->query("SELECT * FROM membres WHERE uid = ? ;", array($uid));
 | 
			
		||||
        if (!$db->num_rows()) {
 | 
			
		||||
            return FALSE;
 | 
			
		||||
        }
 | 
			
		||||
        if ($db->num_rows()) {
 | 
			
		||||
            $db->next_record();
 | 
			
		||||
            // Get a reset URL for the current timestamp.
 | 
			
		||||
            return $this->_get_reset_url(time(), $db->f('uid'), $db->f('login'), $db->f('pass'));
 | 
			
		||||
        }
 | 
			
		||||
        return FALSE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Builds a full reset URL from the uid, login, password and timestamp.
 | 
			
		||||
     *
 | 
			
		||||
     * @returns string
 | 
			
		||||
     *   A full URL.
 | 
			
		||||
     */
 | 
			
		||||
    function _get_reset_url($timestamp, $uid, $login, $password) {
 | 
			
		||||
        global $db, $L_FQDN;
 | 
			
		||||
        $salt = variable_get('salt_password_reset', base64_encode(random_bytes(128)), 'The salt used when hasing password resets - change to invalidate all existing reset tokens') . $password;
 | 
			
		||||
        $data = $timestamp . $uid . $login;
 | 
			
		||||
        $token = hash_hmac('sha512', $data, $salt);
 | 
			
		||||
        // @TODO: Not sure where the bureau's preferred protocol is stored, but
 | 
			
		||||
        // since 3.5.0 https seems to be the default.
 | 
			
		||||
        return 'https://' . $L_FQDN . '/reset.php?' . http_build_query(array(
 | 
			
		||||
            'uid' => $uid,
 | 
			
		||||
            'timestamp' => $timestamp,
 | 
			
		||||
            'token' => $token,
 | 
			
		||||
        ));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Logs a user in from a one-time login link.
 | 
			
		||||
     */
 | 
			
		||||
    function temporary_login($uid, $timestamp, $token, $restrictip = 0, $authip_token = false) {
 | 
			
		||||
        global $db, $msg, $cuid, $authip;
 | 
			
		||||
        if (!$this->validate_reset_url($uid, $timestamp, $token)) {
 | 
			
		||||
            return FALSE;
 | 
			
		||||
        }
 | 
			
		||||
        $msg->log("mem", "temporary_login", $username);
 | 
			
		||||
        if ($msg->has_msgs("ERROR")) {
 | 
			
		||||
            return FALSE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $db->query("select * from membres where uid= ? ;", array($uid));
 | 
			
		||||
        if ($db->num_rows() == 0) {
 | 
			
		||||
            return FALSE;
 | 
			
		||||
        }
 | 
			
		||||
        $db->next_record();
 | 
			
		||||
 | 
			
		||||
        // No password verification for temporary logins, the validation
 | 
			
		||||
        // is in validate_reset_url instead.
 | 
			
		||||
        if (!$db->f("enabled")) {
 | 
			
		||||
            $msg->raise("ERROR", "mem", _("This account is locked, contact the administrator."));
 | 
			
		||||
            return FALSE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->user = $db->Record;
 | 
			
		||||
        $cuid = $db->f("uid");
 | 
			
		||||
        if (panel_islocked() && $cuid != 2000) {
 | 
			
		||||
            $msg->raise("ALERT", "mem", _("This website is currently under maintenance, login is currently disabled."));
 | 
			
		||||
            return FALSE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // AuthIP
 | 
			
		||||
        $allowed_ip = FALSE;
 | 
			
		||||
        if ($authip_token) {
 | 
			
		||||
            $allowed_ip = $this->authip_tokencheck($authip_token);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $aga = $authip->get_allowed('panel');
 | 
			
		||||
        foreach ($aga as $k => $v) {
 | 
			
		||||
            if ($authip->is_in_subnet(get_remote_ip(), $v['ip'], $v['subnet'])) {
 | 
			
		||||
                $allowed = TRUE;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Error if there is rules, the IP is not allowed and it's not in the whitelisted IP
 | 
			
		||||
        if (sizeof($aga) > 1 && !$allowed_ip && !$authip->is_wl(get_remote_ip())) {
 | 
			
		||||
            $msg->raise("ERROR", "mem", _("Your IP isn't allowed to connect"));
 | 
			
		||||
            return FALSE;
 | 
			
		||||
        }
 | 
			
		||||
        // End AuthIP
 | 
			
		||||
 | 
			
		||||
        if ($restrictip) {
 | 
			
		||||
            $ip = get_remote_ip();
 | 
			
		||||
        } else {
 | 
			
		||||
            $ip = "";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Close sessions that are more than 2 days old.
 | 
			
		||||
        $db->query("DELETE FROM sessions WHERE DATE_ADD(ts,INTERVAL 2 DAY)<NOW();");
 | 
			
		||||
 | 
			
		||||
        // Delete old impersonations.
 | 
			
		||||
        if (isset($_COOKIE["oldid"])) {
 | 
			
		||||
            setcookie('oldid', '', 0, '/');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Open the session
 | 
			
		||||
        $sess = md5(mt_rand().mt_rand().mt_rand());
 | 
			
		||||
        $_REQUEST["session"] = $sess;
 | 
			
		||||
        $db->query("insert into sessions (sid,ip,uid) values (?, ?, ?);", array($sess, $ip, $cuid));
 | 
			
		||||
        setcookie("session", $sess, 0, "/");
 | 
			
		||||
        $msg->init_msgs();
 | 
			
		||||
 | 
			
		||||
        // Fill in $local.
 | 
			
		||||
        $db->query("SELECT * FROM local WHERE uid= ? ;", array($cuid));
 | 
			
		||||
        if ($db->num_rows()) {
 | 
			
		||||
            $db->next_record();
 | 
			
		||||
            $this->local = $db->Record;
 | 
			
		||||
        }
 | 
			
		||||
        $this->resetlast();
 | 
			
		||||
 | 
			
		||||
        // Set a cookie parameter to allow password change without requiring
 | 
			
		||||
        // previous one.
 | 
			
		||||
        $db->query('select lastlogin, pass from membres where uid = ?;', array($uid));
 | 
			
		||||
        if ($db->num_rows()) {
 | 
			
		||||
            $db->next_record();
 | 
			
		||||
            $cookie_data = $cuid . $db->f('lastlogin');
 | 
			
		||||
            $salt = variable_get('salt_password_reset', base64_encode(random_bytes(128))) . $db->f('pass');
 | 
			
		||||
            $c = setcookie('require_old_password', hash_hmac('sha512', $cookie_data, $salt), 0, '/');
 | 
			
		||||
            if (!$c) {
 | 
			
		||||
                $msg->log('mem', 'temporary_login', 'Failed to set cookie require_old_password');
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return TRUE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function requires_old_password_for_change() {
 | 
			
		||||
        global $cuid, $db;
 | 
			
		||||
        $cookie = $_COOKIE['require_old_password'];
 | 
			
		||||
        if (!$cookie) {
 | 
			
		||||
            return TRUE;
 | 
			
		||||
        }
 | 
			
		||||
        $db->query('select lastlogin, pass from membres where uid = ?;', array($cuid));
 | 
			
		||||
        if ($db->num_rows()) {
 | 
			
		||||
            $db->next_record();
 | 
			
		||||
            $cookie_data = $cuid . $db->f('lastlogin');
 | 
			
		||||
            $salt = variable_get('salt_password_reset', base64_encode(random_bytes(128))) . $db->f('pass');
 | 
			
		||||
            if ($cookie == hash_hmac('sha512', $cookie_data, $salt)) {
 | 
			
		||||
                return FALSE;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return TRUE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Validates a reset URL that has been received.
 | 
			
		||||
     */
 | 
			
		||||
    function validate_reset_url($uid, $timestamp, $token) {
 | 
			
		||||
        global $cuid, $db, $msg;
 | 
			
		||||
        // Do not log a person in if they are logged in already.
 | 
			
		||||
        if ($this->checkid(false)) {
 | 
			
		||||
            $msg->raise('ERROR', 'mem', _('You are already logged in, you may not use a one-time login link'));
 | 
			
		||||
            $msg->log('mem', 'validate_reset_url', 'Refused one-time log-in since the user is already connected');
 | 
			
		||||
            return FALSE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // The timestamp is older than the age limit - invalid.
 | 
			
		||||
        $fail_message = _('The login-link has already been used or is expired');
 | 
			
		||||
        $duration = variable_get('password_reset_expiration', 86400, 'The number of seconds for which a password reset link is valid');
 | 
			
		||||
        if (time() - $timestamp >= $duration) {
 | 
			
		||||
            $msg->raise('ERROR', 'mem', $fail_message);
 | 
			
		||||
            $msg->log('mem', 'validate_reset_url', 'Refused one-time log-in since the time elapsed is greater than limit of ' . $duration);
 | 
			
		||||
            return FALSE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $db->query("SELECT * FROM membres WHERE uid = ? ;", array($uid));
 | 
			
		||||
        if (!$db->num_rows()) {
 | 
			
		||||
            $msg->raise('ERROR', 'mem', $fail_message);
 | 
			
		||||
            $msg->log('mem', 'validate_reset_url', 'Refused one-time log-in since a user with ID ' . $uid. ' does not exist');
 | 
			
		||||
            return FALSE;
 | 
			
		||||
        }
 | 
			
		||||
        $db->next_record();
 | 
			
		||||
        $last_login = strtotime($db->f('lastlogin'));
 | 
			
		||||
        // The timestamp is older than the most recent login - invalid.
 | 
			
		||||
        if ($last_login >= time() || $last_login >= $timestamp) {
 | 
			
		||||
            $msg->raise('ERROR', 'mem', $fail_message);
 | 
			
		||||
            $msg->log('mem', 'validate_reset_url', "Refused one-time log-in since the most recent login was more recent than the timestamp in the log-in link. Last: {$last_login}, Timestamp: {$timestamp}");
 | 
			
		||||
            return FALSE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // The account is locked or cannot change pass - invalid.
 | 
			
		||||
        if (!$db->f('enabled') || !$db->f('canpass')) {
 | 
			
		||||
            $msg->raise('ERROR', 'mem', $fail_message);
 | 
			
		||||
            $msg->log('mem', 'validate_reset_url', 'Refused one-time log-in since the user account is disabled or cannot change it\'s password.');
 | 
			
		||||
            return FALSE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Using the current user info and timestamp the tokens generated
 | 
			
		||||
        // do not match - invalid. (Eg. user password changed, salt changed).
 | 
			
		||||
        $salt = variable_get('salt_password_reset', base64_encode(random_bytes(128))) . $db->f('pass');
 | 
			
		||||
        $data = $timestamp . $uid . $db->f('login');
 | 
			
		||||
        $ref_token = hash_hmac('sha512', $data, $salt);
 | 
			
		||||
        if ($token != $ref_token) {
 | 
			
		||||
            $msg->raise('ERROR', 'mem', $fail_message);
 | 
			
		||||
            return FALSE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $msg->raise('INFO', 'mem', _('You have used a one-time login link. Please set a new password now.'));
 | 
			
		||||
        return TRUE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
} /* Class m_mem */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue