diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index f798392..8f8cab4 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -2490,8 +2490,8 @@ function install_configure_form_submit($form, &$form_state) {
$account->name = $form_state['values']['account']['name'];
$account->save();
// Load global $user and perform final login tasks.
- $user = user_load(1);
- user_login_finalize();
+ $account = user_load(1);
+ user_login_finalize($account);
// Record when this install ran.
variable_set('install_time', $_SERVER['REQUEST_TIME']);
diff --git a/core/modules/user/lib/Drupal/user/Controller/UserController.php b/core/modules/user/lib/Drupal/user/Controller/UserController.php
index 864e095..f663bb9 100644
--- a/core/modules/user/lib/Drupal/user/Controller/UserController.php
+++ b/core/modules/user/lib/Drupal/user/Controller/UserController.php
@@ -7,27 +7,37 @@
namespace Drupal\user\Controller;
-use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\user\Form\UserLoginForm;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
-use Drupal\Core\Controller\ControllerInterface;
/**
* Controller routines for user routes.
*/
-class UserController implements ControllerInterface {
+class UserController {
/**
- * Constructs an UserController object.
+ * Returns the user page.
+ *
+ * Displays user profile if user is logged in, or login form for anonymous
+ * users.
+ *
+ * @param \Symfony\Component\HttpFoundation\Request $request
+ * The request object.
+ *
+ * @return \Symfony\Component\HttpFoundation\RedirectResponse|array
+ * Returns either a redirect to the user page or the render
+ * array of the login form.
*/
- public function __construct() {
- }
-
- /**
- * {@inheritdoc}
- */
- public static function create(ContainerInterface $container) {
- return new static();
+ public function userPage(Request $request) {
+ global $user;
+ if ($user->uid) {
+ $response = new RedirectResponse(url('user/' . $user->uid, array('absolute' => TRUE)));
+ }
+ else {
+ $response = drupal_get_form(UserLoginForm::create(\Drupal::getContainer()), $request);
+ }
+ return $response;
}
/**
diff --git a/core/modules/user/lib/Drupal/user/Form/UserLoginForm.php b/core/modules/user/lib/Drupal/user/Form/UserLoginForm.php
new file mode 100644
index 0000000..923d983
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/Form/UserLoginForm.php
@@ -0,0 +1,221 @@
+configFactory = $config_factory;
+ $this->flood = $flood;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container) {
+ return new static(
+ $container->get('config.factory'),
+ $container->get('flood')
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getFormID() {
+ return 'user_login_form';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function buildForm(array $form, array &$form_state, Request $request = NULL) {
+ $this->request = $request;
+ // Display login form:
+ $form['name'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Username'),
+ '#size' => 60,
+ '#maxlength' => USERNAME_MAX_LENGTH,
+ '#description' => t('Enter your @s username.', array('@s' => $this->configFactory->get('system.site')->get('name'))),
+ '#required' => TRUE,
+ '#attributes' => array(
+ 'autocorrect' => 'off',
+ 'autocapitalize' => 'off',
+ 'spellcheck' => 'false',
+ 'autofocus' => 'autofocus',
+ ),
+ );
+
+ $form['pass'] = array(
+ '#type' => 'password',
+ '#title' => t('Password'),
+ '#size' => 60,
+ '#description' => t('Enter the password that accompanies your username.'),
+ '#required' => TRUE,
+ );
+
+ $form['actions'] = array('#type' => 'actions');
+ $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Log in'));
+
+ $form['#validate'][] = array($this, 'validateName');
+ $form['#validate'][] = array($this, 'validateAuthentication');
+ $form['#validate'][] = array($this, 'validateFinal');
+
+ return $form;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function validateForm(array &$form, array &$form_state) {
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function submitForm(array &$form, array &$form_state) {
+ $account = user_load($form_state['uid']);
+ $form_state['redirect'] = 'user/' . $account->uid;
+
+ user_login_finalize($account);
+ }
+
+ /**
+ * A FAPI validate handler. Sets an error if supplied username has been blocked.
+ */
+ public function validateName(array &$form, array &$form_state) {
+ if (!empty($form_state['values']['name']) && user_is_blocked($form_state['values']['name'])) {
+ // Blocked in user administration.
+ form_set_error('name', t('The username %name has not been activated or is blocked.', array('%name' => $form_state['values']['name'])));
+ }
+ }
+
+ /**
+ * A validate handler on the login form. Check supplied username/password
+ * against local users table. If successful, $form_state['uid']
+ * is set to the matching user ID.
+ */
+ public function validateAuthentication(array &$form, array &$form_state) {
+ $password = trim($form_state['values']['pass']);
+ $flood_config = $this->configFactory->get('user.flood');
+ if (!empty($form_state['values']['name']) && !empty($password)) {
+ // Do not allow any login from the current user's IP if the limit has been
+ // reached. Default is 50 failed attempts allowed in one hour. This is
+ // independent of the per-user limit to catch attempts from one IP to log
+ // in to many different user accounts. We have a reasonably high limit
+ // since there may be only one apparent IP for all users at an institution.
+ if (!$this->flood->isAllowed('user.failed_login_ip', $flood_config->get('ip_limit'), $flood_config->get('ip_window'))) {
+ $form_state['flood_control_triggered'] = 'ip';
+ return;
+ }
+ $account = db_query("SELECT * FROM {users} WHERE name = :name AND status = 1", array(':name' => $form_state['values']['name']))->fetchObject();
+ if ($account) {
+ if ($flood_config->get('uid_only')) {
+ // Register flood events based on the uid only, so they apply for any
+ // IP address. This is the most secure option.
+ $identifier = $account->uid;
+ }
+ else {
+ // The default identifier is a combination of uid and IP address. This
+ // is less secure but more resistant to denial-of-service attacks that
+ // could lock out all users with public user names.
+ $identifier = $account->uid . '-' . $this->request->getClientIP();
+ }
+ $form_state['flood_control_user_identifier'] = $identifier;
+
+ // Don't allow login if the limit for this user has been reached.
+ // Default is to allow 5 failed attempts every 6 hours.
+ if (!$this->flood->isAllowed('user.failed_login_user', $flood_config->get('user_limit'), $flood_config->get('user_window'), $identifier)) {
+ $form_state['flood_control_triggered'] = 'user';
+ return;
+ }
+ }
+ // We are not limited by flood control, so try to authenticate.
+ // Set $form_state['uid'] as a flag for user_login_final_validate().
+ $form_state['uid'] = user_authenticate($form_state['values']['name'], $password);
+ }
+ }
+
+ /**
+ * The final validation handler on the login form.
+ *
+ * Sets a form error if user has not been authenticated, or if too many
+ * logins have been attempted. This validation function should always
+ * be the last one.
+ */
+ public function validateFinal(array &$form, array &$form_state) {
+ $flood_config = $this->configFactory->get('user.flood');
+ if (empty($form_state['uid'])) {
+ // Always register an IP-based failed login event.
+ $this->flood->register('user.failed_login_ip', $flood_config->get('ip_window'));
+ // Register a per-user failed login event.
+ if (isset($form_state['flood_control_user_identifier'])) {
+ $this->flood->register('user.failed_login_user', $flood_config->get('user_window'), $form_state['flood_control_user_identifier']);
+ }
+
+ if (isset($form_state['flood_control_triggered'])) {
+ if ($form_state['flood_control_triggered'] == 'user') {
+ form_set_error('name', format_plural($flood_config->get('user_limit'), 'Sorry, there has been more than one failed login attempt for this account. It is temporarily blocked. Try again later or request a new password.', 'Sorry, there have been more than @count failed login attempts for this account. It is temporarily blocked. Try again later or request a new password.', array('@url' => url('user/password'))));
+ }
+ else {
+ // We did not find a uid, so the limit is IP-based.
+ form_set_error('name', t('Sorry, too many failed login attempts from your IP address. This IP address is temporarily blocked. Try again later or request a new password.', array('@url' => url('user/password'))));
+ }
+ }
+ else {
+ form_set_error('name', t('Sorry, unrecognized username or password. Have you forgotten your password?', array('@password' => url('user/password', array('query' => array('name' => $form_state['values']['name']))))));
+ if (user_load_by_name($form_state['values']['name'])) {
+ watchdog('user', 'Login attempt failed for %user.', array('%user' => $form_state['values']['name']));
+ }
+ else {
+ // If the username entered is not a valid user,
+ // only store the IP address.
+ watchdog('user', 'Login attempt failed from %ip.', array('%ip' => $this->request->getClientIp()));
+ }
+ }
+ }
+ elseif (isset($form_state['flood_control_user_identifier'])) {
+ // Clear past failures for this user so as not to block a user who might
+ // log in and out more than once in an hour.
+ $this->flood->clear('user.failed_login_user', $form_state['flood_control_user_identifier']);
+ }
+ }
+
+}
diff --git a/core/modules/user/lib/Drupal/user/Plugin/Block/UserLoginBlock.php b/core/modules/user/lib/Drupal/user/Plugin/Block/UserLoginBlock.php
index 0c8aa2a..b785aef 100644
--- a/core/modules/user/lib/Drupal/user/Plugin/Block/UserLoginBlock.php
+++ b/core/modules/user/lib/Drupal/user/Plugin/Block/UserLoginBlock.php
@@ -10,6 +10,7 @@
use Drupal\block\BlockBase;
use Drupal\Component\Annotation\Plugin;
use Drupal\Core\Annotation\Translation;
+use Drupal\user\Form\UserLoginForm;
/**
* Provides a 'User login' block.
@@ -33,7 +34,7 @@ public function access() {
* {@inheritdoc}
*/
public function build() {
- $form = drupal_get_form('user_login_form');
+ $form = drupal_get_form(new UserLoginForm(\Drupal::service('config.factory'), \Drupal::service('flood')));
unset($form['name']['#attributes']['autofocus']);
unset($form['name']['#description']);
unset($form['pass']['#description']);
diff --git a/core/modules/user/lib/Drupal/user/RegisterFormController.php b/core/modules/user/lib/Drupal/user/RegisterFormController.php
index ceecd65..e4524b4 100644
--- a/core/modules/user/lib/Drupal/user/RegisterFormController.php
+++ b/core/modules/user/lib/Drupal/user/RegisterFormController.php
@@ -118,8 +118,7 @@ public function save(array $form, array &$form_state) {
// No e-mail verification required; log in user immediately.
elseif (!$admin && !config('user.settings')->get('verify_mail') && $account->status) {
_user_mail_notify('register_no_approval_required', $account);
- $form_state['uid'] = $account->uid;
- user_login_form_submit(array(), $form_state);
+ user_login_finalize($account);
drupal_set_message(t('Registration successful. You are now logged in.'));
$form_state['redirect'] = '';
}
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index c60e823..19f705a 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -854,16 +854,13 @@ function user_menu() {
$items['user'] = array(
'title' => 'User account',
'title callback' => 'user_menu_title',
- 'page callback' => 'user_page',
- 'access callback' => TRUE,
- 'file' => 'user.pages.inc',
'weight' => -10,
+ 'route_name' => 'user_page',
'menu_name' => 'account',
);
$items['user/login'] = array(
'title' => 'Log in',
- 'access callback' => 'user_is_anonymous',
'type' => MENU_DEFAULT_LOCAL_TASK,
);
// Other authentication methods may add pages below user/login/.
@@ -1158,44 +1155,6 @@ function user_page_title($account) {
}
/**
- * Form builder; the main user login form.
- *
- * @ingroup forms
- */
-function user_login_form($form, &$form_state) {
- // Display login form:
- $form['name'] = array(
- '#type' => 'textfield',
- '#title' => t('Username'),
- '#size' => 60,
- '#maxlength' => USERNAME_MAX_LENGTH,
- '#description' => t('Enter your @s username.', array('@s' => config('system.site')->get('name'))),
- '#required' => TRUE,
- '#attributes' => array(
- 'autocorrect' => 'off',
- 'autocapitalize' => 'off',
- 'spellcheck' => 'false',
- 'autofocus' => 'autofocus',
- ),
- );
-
- $form['pass'] = array(
- '#type' => 'password',
- '#title' => t('Password'),
- '#size' => 60,
- '#description' => t('Enter the password that accompanies your username.'),
- '#required' => TRUE,
- );
-
- $form['actions'] = array('#type' => 'actions');
- $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Log in'));
-
- $form['#validate'] = user_login_default_validators();
-
- return $form;
-}
-
-/**
* Set up a series for validators which check for blocked users,
* then authenticate against local database, then return an error if
* authentication fails. Distributed authentication modules are welcome
@@ -1218,109 +1177,6 @@ function user_login_default_validators() {
}
/**
- * A FAPI validate handler. Sets an error if supplied username has been blocked.
- */
-function user_login_name_validate($form, &$form_state) {
- if (!empty($form_state['values']['name']) && user_is_blocked($form_state['values']['name'])) {
- // Blocked in user administration.
- form_set_error('name', t('The username %name has not been activated or is blocked.', array('%name' => $form_state['values']['name'])));
- }
-}
-
-/**
- * A validate handler on the login form. Check supplied username/password
- * against local users table. If successful, $form_state['uid']
- * is set to the matching user ID.
- */
-function user_login_authenticate_validate($form, &$form_state) {
- $password = trim($form_state['values']['pass']);
- $flood_config = config('user.flood');
- $flood = Drupal::service('flood');
- if (!empty($form_state['values']['name']) && !empty($password)) {
- // Do not allow any login from the current user's IP if the limit has been
- // reached. Default is 50 failed attempts allowed in one hour. This is
- // independent of the per-user limit to catch attempts from one IP to log
- // in to many different user accounts. We have a reasonably high limit
- // since there may be only one apparent IP for all users at an institution.
- if (!$flood->isAllowed('user.failed_login_ip', $flood_config->get('ip_limit'), $flood_config->get('ip_window'))) {
- $form_state['flood_control_triggered'] = 'ip';
- return;
- }
- $account = db_query("SELECT * FROM {users} WHERE name = :name AND status = 1", array(':name' => $form_state['values']['name']))->fetchObject();
- if ($account) {
- if ($flood_config->get('uid_only')) {
- // Register flood events based on the uid only, so they apply for any
- // IP address. This is the most secure option.
- $identifier = $account->uid;
- }
- else {
- // The default identifier is a combination of uid and IP address. This
- // is less secure but more resistant to denial-of-service attacks that
- // could lock out all users with public user names.
- $identifier = $account->uid . '-' . Drupal::request()->getClientIP();
- }
- $form_state['flood_control_user_identifier'] = $identifier;
-
- // Don't allow login if the limit for this user has been reached.
- // Default is to allow 5 failed attempts every 6 hours.
- if (!$flood->isAllowed('user.failed_login_user', $flood_config->get('user_limit'), $flood_config->get('user_window'), $identifier)) {
- $form_state['flood_control_triggered'] = 'user';
- return;
- }
- }
- // We are not limited by flood control, so try to authenticate.
- // Set $form_state['uid'] as a flag for user_login_final_validate().
- $form_state['uid'] = user_authenticate($form_state['values']['name'], $password);
- }
-}
-
-/**
- * The final validation handler on the login form.
- *
- * Sets a form error if user has not been authenticated, or if too many
- * logins have been attempted. This validation function should always
- * be the last one.
- */
-function user_login_final_validate($form, &$form_state) {
- $flood_config = config('user.flood');
- $flood = Drupal::service('flood');
- if (empty($form_state['uid'])) {
- // Always register an IP-based failed login event.
- $flood->register('user.failed_login_ip', $flood_config->get('ip_window'));
- // Register a per-user failed login event.
- if (isset($form_state['flood_control_user_identifier'])) {
- $flood->register('user.failed_login_user', $flood_config->get('user_window'), $form_state['flood_control_user_identifier']);
- }
-
- if (isset($form_state['flood_control_triggered'])) {
- if ($form_state['flood_control_triggered'] == 'user') {
- form_set_error('name', format_plural($flood_config->get('user_limit'), 'Sorry, there has been more than one failed login attempt for this account. It is temporarily blocked. Try again later or request a new password.', 'Sorry, there have been more than @count failed login attempts for this account. It is temporarily blocked. Try again later or request a new password.', array('@url' => url('user/password'))));
- }
- else {
- // We did not find a uid, so the limit is IP-based.
- form_set_error('name', t('Sorry, too many failed login attempts from your IP address. This IP address is temporarily blocked. Try again later or request a new password.', array('@url' => url('user/password'))));
- }
- }
- else {
- form_set_error('name', t('Sorry, unrecognized username or password. Have you forgotten your password?', array('@password' => url('user/password', array('query' => array('name' => $form_state['values']['name']))))));
- if (user_load_by_name($form_state['values']['name'])) {
- watchdog('user', 'Login attempt failed for %user.', array('%user' => $form_state['values']['name']));
- }
- else {
- // If the username entered is not a valid user,
- // only store the IP address.
- watchdog('user', 'Login attempt failed from %ip.', array('%ip' => Drupal::request()->getClientIp()));
- }
- }
- }
- elseif (isset($form_state['flood_control_user_identifier'])) {
- // Clear past failures for this user so as not to block a user who might
- // log in and out more than once in an hour.
- $flood->clear('user.failed_login_user', $form_state['flood_control_user_identifier']);
- }
-}
-
-/**
* Try to validate the user's login credentials locally.
*
* @param $name
@@ -1357,13 +1213,14 @@ function user_authenticate($name, $password) {
* The function records a watchdog message about the new session, saves the
* login timestamp, calls hook_user_login(), and generates a new session.
*
- * @param array $edit
- * The array of form values submitted by the user.
+ * @param \Drupal\Core\Session\AccountInterface $account
+ * The account to log in.
*
* @see hook_user_login()
*/
-function user_login_finalize(&$edit = array()) {
+function user_login_finalize(AccountInterface $account) {
global $user;
+ $user = $account;
watchdog('user', 'Session opened for %name.', array('%name' => $user->name));
// Update the user table timestamp noting user has logged in.
// This is also used to invalidate one-time login links.
@@ -1382,19 +1239,6 @@ function user_login_finalize(&$edit = array()) {
}
/**
- * Submit handler for the login form. Load $user object and perform standard login
- * tasks. The user is then redirected to the My Account page. Setting the
- * destination in the query string overrides the redirect.
- */
-function user_login_form_submit($form, &$form_state) {
- global $user;
- $user = user_load($form_state['uid']);
- $form_state['redirect'] = 'user/' . $user->uid;
-
- user_login_finalize($form_state);
-}
-
-/**
* Implements hook_user_login().
*/
function user_user_login($account) {
diff --git a/core/modules/user/user.pages.inc b/core/modules/user/user.pages.inc
index 26fb040..d98041a 100644
--- a/core/modules/user/user.pages.inc
+++ b/core/modules/user/user.pages.inc
@@ -130,10 +130,9 @@ function user_pass_reset($form, &$form_state, $uid, $timestamp, $hashed_pass, $a
// First stage is a confirmation form, then login
if ($action == 'login') {
// Set the new user.
- $user = $account;
// user_login_finalize() also updates the login timestamp of the
// user, which invalidates further use of the one-time login link.
- user_login_finalize();
+ user_login_finalize($account);
watchdog('user', 'User %name used one-time login link at time %timestamp.', array('%name' => $account->name, '%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.
@@ -398,19 +397,3 @@ function user_cancel_confirm($account, $timestamp = 0, $hashed_pass = '') {
}
throw new AccessDeniedHttpException();
}
-
-/**
- * Access callback for path /user.
- *
- * Displays user profile if user is logged in, or login form for anonymous
- * users.
- */
-function user_page() {
- global $user;
- if ($user->uid) {
- return new RedirectResponse(url('user/' . $user->uid, array('absolute' => TRUE)));
- }
- else {
- return drupal_get_form('user_login_form');
- }
-}
diff --git a/core/modules/user/user.routing.yml b/core/modules/user/user.routing.yml
index b48c808..90baba8 100644
--- a/core/modules/user/user.routing.yml
+++ b/core/modules/user/user.routing.yml
@@ -68,3 +68,17 @@ user_role_delete:
_form: '\Drupal\user\Form\UserRoleDelete'
requirements:
_entity_access: user_role.delete
+
+user_page:
+ pattern: '/user'
+ defaults:
+ _content: '\Drupal\user\Controller\UserController::userPage'
+ requirements:
+ _access: 'TRUE'
+
+user_login:
+ pattern: '/user/login'
+ defaults:
+ _form: '\Drupal\user\Form\UserLoginForm'
+ requirements:
+ _access: 'TRUE'