Issue #1998198 by pwolanin, splatio, Albert Volkman, tim.plunkett, andypost, disasm, Les Lim, tkuldeep17: Convert user_pass_reset to a new-style Form object.
parent
291b99816d
commit
f4751717cc
|
@ -11,12 +11,121 @@ use Drupal\Component\Utility\Xss;
|
|||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\user\UserInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
use Drupal\Core\Datetime\Date;
|
||||
use Drupal\user\UserStorageInterface;
|
||||
|
||||
/**
|
||||
* Controller routines for user routes.
|
||||
*/
|
||||
class UserController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* The date formatting service.
|
||||
*
|
||||
* @var \Drupal\Core\Datetime\Date
|
||||
*/
|
||||
protected $date;
|
||||
|
||||
/**
|
||||
* The user storage.
|
||||
*
|
||||
* @var \Drupal\user\UserStorageInterface
|
||||
*/
|
||||
protected $userStorage;
|
||||
|
||||
/**
|
||||
* Constructs a UserController object.
|
||||
*
|
||||
* @param \Drupal\Core\Datetime\Date $date
|
||||
* The date formatting service.
|
||||
* @param \Drupal\user\UserStorageInterface $user_storage
|
||||
* The user storage.
|
||||
*/
|
||||
public function __construct(Date $date, UserStorageInterface $user_storage) {
|
||||
$this->date = $date;
|
||||
$this->userStorage = $user_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('date'),
|
||||
$container->get('entity.manager')->getStorage('user')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user password reset page.
|
||||
*
|
||||
* @param int $uid
|
||||
* UID of user requesting reset.
|
||||
* @param int $timestamp
|
||||
* The current timestamp.
|
||||
* @param string $hash
|
||||
* Login link hash.
|
||||
*
|
||||
* @return array|\Symfony\Component\HttpFoundation\RedirectResponse
|
||||
* The form structure or a redirect response.
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
|
||||
* If the login link is for a blocked user or invalid user ID.
|
||||
*/
|
||||
public function resetPass($uid, $timestamp, $hash) {
|
||||
$account = $this->currentUser();
|
||||
$config = $this->config('user.settings');
|
||||
// When processing the one-time login link, we have to make sure that a user
|
||||
// isn't already logged in.
|
||||
if ($account->isAuthenticated()) {
|
||||
// The current user is already logged in.
|
||||
if ($account->id() == $uid) {
|
||||
drupal_set_message($this->t('You are logged in as %user. <a href="!user_edit">Change your password.</a>', array('%user' => $account->getUsername(), '!user_edit' => $this->url('user.edit', array('user' => $account->id())))));
|
||||
}
|
||||
// A different user is already logged in on the computer.
|
||||
else {
|
||||
if ($reset_link_user = $this->userStorage->load($uid)) {
|
||||
drupal_set_message($this->t('Another user (%other_user) is already logged into the site on this computer, but you tried to use a one-time link for user %resetting_user. Please <a href="!logout">logout</a> and try using the link again.',
|
||||
array('%other_user' => $account->getUsername(), '%resetting_user' => $reset_link_user->getUsername(), '!logout' => $this->url('user.logout'))));
|
||||
}
|
||||
else {
|
||||
// Invalid one-time link specifies an unknown user.
|
||||
drupal_set_message($this->t('The one-time login link you clicked is invalid.'));
|
||||
}
|
||||
}
|
||||
return $this->redirect('<front>');
|
||||
}
|
||||
else {
|
||||
// The current user is not logged in, so check the parameters.
|
||||
// Time out, in seconds, until login URL expires.
|
||||
$timeout = $config->get('password_reset_timeout');
|
||||
$current = REQUEST_TIME;
|
||||
/* @var \Drupal\user\UserInterface $user */
|
||||
$user = $this->userStorage->load($uid);
|
||||
// Verify that the user exists and is active.
|
||||
if ($user && $user->isActive()) {
|
||||
// No time out for first time login.
|
||||
if ($user->getLastLoginTime() && $current - $timestamp > $timeout) {
|
||||
drupal_set_message($this->t('You have tried to use a one-time login link that has expired. Please request a new one using the form below.'));
|
||||
return $this->redirect('user.pass');
|
||||
}
|
||||
elseif ($user->isAuthenticated() && ($timestamp >= $user->getLastLoginTime()) && ($timestamp <= $current) && ($hash === user_pass_rehash($user->getPassword(), $timestamp, $user->getLastLoginTime()))) {
|
||||
$expiration_date = $user->getLastLoginTime() ? $this->date->format($timestamp + $timeout) : NULL;
|
||||
return $this->formBuilder()->getForm('Drupal\user\Form\UserPasswordResetForm', $user, $expiration_date, $timestamp, $hash);
|
||||
}
|
||||
else {
|
||||
drupal_set_message($this->t('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.'));
|
||||
return $this->redirect('user.pass');
|
||||
}
|
||||
}
|
||||
}
|
||||
// Blocked or invalid user ID, so deny access. The parameters will be in the
|
||||
// watchdog's URL for the administrator to check.
|
||||
throw new AccessDeniedHttpException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user page.
|
||||
*
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\user\Form\UserForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\user\Form;
|
||||
|
||||
/**
|
||||
* Temporary form controller for user module.
|
||||
*/
|
||||
class UserForm {
|
||||
|
||||
/**
|
||||
* Wraps user_pass_reset().
|
||||
*
|
||||
* @todo Remove user_pass_reset().
|
||||
*/
|
||||
public function resetPass($uid, $timestamp, $hash, $operation) {
|
||||
module_load_include('pages.inc', 'user');
|
||||
return \Drupal::formBuilder()->getForm('user_pass_reset', $uid, $timestamp, $hash, $operation);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\user\Form\UserPasswordResetForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\user\Form;
|
||||
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Form controller for the user password forms.
|
||||
*/
|
||||
class UserPasswordResetForm extends FormBase {
|
||||
|
||||
/**
|
||||
* A logger instance.
|
||||
*
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* Constructs a new UserPasswordResetForm.
|
||||
*
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
* A logger instance.
|
||||
*/
|
||||
public function __construct(LoggerInterface $logger) {
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('logger.factory')->get('user')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormID() {
|
||||
return 'user_pass_reset';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param array $form_state
|
||||
* An associative array containing the current state of the form.
|
||||
* @param \Drupal\Core\Session\AccountInterface $user
|
||||
* User requesting reset.
|
||||
* @param string $expiration_date
|
||||
* Formatted expiration date for the login link, or NULL if the link does
|
||||
* not expire.
|
||||
* @param int $timestamp
|
||||
* The current timestamp.
|
||||
* @param string $hash
|
||||
* Login link hash.
|
||||
*/
|
||||
public function buildForm(array $form, array &$form_state, AccountInterface $user = NULL, $expiration_date = NULL, $timestamp = NULL, $hash = NULL) {
|
||||
if ($expiration_date) {
|
||||
$form['message'] = array('#markup' => $this->t('<p>This is a one-time login for %user_name and will expire on %expiration_date.</p><p>Click on this button to log in to the site and change your password.</p>', array('%user_name' => $user->getUsername(), '%expiration_date' => $expiration_date)));
|
||||
}
|
||||
else {
|
||||
// No expiration for first time login.
|
||||
$form['message'] = array('#markup' => $this->t('<p>This is a one-time login for %user_name.</p><p>Click on this button to log in to the site and change your password.</p>', array('%user_name' => $user->getUsername())));
|
||||
}
|
||||
|
||||
$form['#title'] = 'Reset Password';
|
||||
$form['user'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $user,
|
||||
);
|
||||
$form['timestamp'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $timestamp,
|
||||
);
|
||||
$form['help'] = array('#markup' => '<p>' . $this->t('This login can be used only once.') . '</p>');
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Log in'),
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, array &$form_state) {
|
||||
/** @var $user \Drupal\user\UserInterface */
|
||||
$user = $form_state['values']['user'];
|
||||
user_login_finalize($user);
|
||||
$this->logger->notice('User %name used one-time login link at time %timestamp.', array('%name' => $user->getUsername(), '%timestamp' => $form_state['values']['timestamp']));
|
||||
drupal_set_message($this->t('You have just used your one-time login link. It is no longer necessary to use this link to log in. Please change your password.'));
|
||||
// Let the user's password be changed without the current password check.
|
||||
$token = Crypt::randomBytesBase64(55);
|
||||
$_SESSION['pass_reset_' . $user->id()] = $token;
|
||||
$form_state['redirect_route']['route_name'] = 'user.edit';
|
||||
$form_state['redirect_route']['route_parameters'] = array('user' => $user->id());
|
||||
$form_state['redirect_route']['options'] = array(
|
||||
'query' => array('pass-reset-token' => $token),
|
||||
'absolute' => TRUE,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -106,6 +106,13 @@ class UserPasswordResetTest extends WebTestBase {
|
|||
$_uid = $this->account->id();
|
||||
$this->drupalGet("user/reset/$_uid/$bogus_timestamp/" . user_pass_rehash($this->account->getPassword(), $bogus_timestamp, $this->account->getLastLoginTime()));
|
||||
$this->assertText(t('You have tried to use a one-time login link that has expired. Please request a new one using the form below.'), 'Expired password reset request rejected.');
|
||||
|
||||
// Create a user, block the account, and verify that a login link is denied.
|
||||
$timestamp = REQUEST_TIME - 1;
|
||||
$blocked_account = $this->drupalCreateUser()->block();
|
||||
$blocked_account->save();
|
||||
$this->drupalGet("user/reset/" . $blocked_account->id() . "/$timestamp/" . user_pass_rehash($blocked_account->getPassword(), $timestamp, $blocked_account->getLastLoginTime()));
|
||||
$this->assertResponse(403);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,97 +6,8 @@
|
|||
*/
|
||||
|
||||
use Drupal\Core\Render\Element;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
|
||||
/**
|
||||
* Menu callback; process one time login link and redirects to the user page on success.
|
||||
*
|
||||
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
|
||||
* Use \Drupal\user\Form\UserForm::resetPass().
|
||||
*/
|
||||
function user_pass_reset($form, &$form_state, $uid, $timestamp, $hashed_pass, $action = NULL) {
|
||||
global $user;
|
||||
|
||||
// When processing the one-time login link, we have to make sure that a user
|
||||
// isn't already logged in.
|
||||
if ($user->isAuthenticated()) {
|
||||
// The existing user is already logged in.
|
||||
if ($user->id() == $uid) {
|
||||
drupal_set_message(t('You are logged in as %user. <a href="!user_edit">Change your password.</a>', array('%user' => $user->getUsername(), '!user_edit' => url("user/" . $user->id() . "/edit"))));
|
||||
}
|
||||
// A different user is already logged in on the computer.
|
||||
else {
|
||||
$reset_link_account = user_load($uid);
|
||||
if (!empty($reset_link_account)) {
|
||||
drupal_set_message(t('Another user (%other_user) is already logged into the site on this computer, but you tried to use a one-time link for user %resetting_user. Please <a href="!logout">logout</a> and try using the link again.',
|
||||
array('%other_user' => $user->getUsername(), '%resetting_user' => $reset_link_account->getUsername(), '!logout' => url('user/logout'))));
|
||||
} else {
|
||||
// Invalid one-time link specifies an unknown user.
|
||||
drupal_set_message(t('The one-time login link you clicked is invalid.'));
|
||||
}
|
||||
}
|
||||
return new RedirectResponse(url('<front>', array('absolute' => TRUE)));
|
||||
}
|
||||
else {
|
||||
// Time out, in seconds, until login URL expires.
|
||||
$timeout = \Drupal::config('user.settings')->get('password_reset_timeout');
|
||||
$current = REQUEST_TIME;
|
||||
$account = user_load($uid);
|
||||
// Verify that the user exists and is active.
|
||||
if ($timestamp <= $current && $account && $account->isActive()) {
|
||||
// No time out for first time login.
|
||||
if ($account->getLastLoginTime() && $current - $timestamp > $timeout) {
|
||||
drupal_set_message(t('You have tried to use a one-time login link that has expired. Please request a new one using the form below.'));
|
||||
return new RedirectResponse(url('user/password', array('absolute' => TRUE)));
|
||||
}
|
||||
elseif ($account->isAuthenticated() && $timestamp >= $account->getLastLoginTime() && $timestamp <= $current && $hashed_pass == user_pass_rehash($account->getPassword(), $timestamp, $account->getLastLoginTime())) {
|
||||
// First stage is a confirmation form, then login
|
||||
if ($action == 'login') {
|
||||
// Set the new user.
|
||||
// user_login_finalize() also updates the login timestamp of the
|
||||
// user, which invalidates further use of the one-time login link.
|
||||
user_login_finalize($account);
|
||||
\Drupal::logger('user')->notice('User %name used one-time login link at time %timestamp.', array('%name' => $account->getUsername(), '%timestamp' => $timestamp));
|
||||
drupal_set_message(t('You have just used your one-time login link. It is no longer necessary to use this link to log in. Please change your password.'));
|
||||
// Let the user's password be changed without the current password check.
|
||||
$token = Crypt::randomBytesBase64(55);
|
||||
$_SESSION['pass_reset_' . $user->id()] = $token;
|
||||
return new RedirectResponse(url('user/' . $user->id() . '/edit', array(
|
||||
'query' => array('pass-reset-token' => $token),
|
||||
'absolute' => TRUE,
|
||||
)));
|
||||
}
|
||||
else {
|
||||
if (!$account->getLastLoginTime()) {
|
||||
// No expiration for first time login.
|
||||
$form['message'] = array('#markup' => t('<p>This is a one-time login for %user_name.</p><p>Click on this button to log in to the site and change your password.</p>', array('%user_name' => $account->getUsername())));
|
||||
}
|
||||
else {
|
||||
$form['message'] = array('#markup' => t('<p>This is a one-time login for %user_name and will expire on %expiration_date.</p><p>Click on this button to log in to the site and change your password.</p>', array('%user_name' => $account->getUsername(), '%expiration_date' => format_date($timestamp + $timeout))));
|
||||
}
|
||||
$form['help'] = array('#markup' => '<p>' . t('This login can be used only once.') . '</p>');
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Log in'));
|
||||
$form['#action'] = url("user/reset/$uid/$timestamp/$hashed_pass/login");
|
||||
return $form;
|
||||
}
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.'));
|
||||
return new RedirectResponse(url('user/password', array('absolute' => TRUE)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Deny access, no more clues.
|
||||
// Everything will be in the watchdog's URL for the administrator to check.
|
||||
throw new AccessDeniedHttpException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares variables for user templates.
|
||||
|
|
|
@ -182,11 +182,10 @@ user.cancel_confirm:
|
|||
_entity_access: 'user.delete'
|
||||
|
||||
user.reset:
|
||||
path: '/user/reset/{uid}/{timestamp}/{hash}/{operation}'
|
||||
path: '/user/reset/{uid}/{timestamp}/{hash}'
|
||||
defaults:
|
||||
_content: '\Drupal\user\Form\UserForm::resetPass'
|
||||
_content: '\Drupal\user\Controller\UserController::resetPass'
|
||||
_title: 'Reset password'
|
||||
operation: NULL
|
||||
requirements:
|
||||
_access: 'TRUE'
|
||||
options:
|
||||
|
|
Loading…
Reference in New Issue