View source
<?php
namespace Drupal\user\Controller;
use Drupal\Core\Access\CsrfTokenGenerator;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Flood\FloodInterface;
use Drupal\Core\Routing\RouteProviderInterface;
use Drupal\user\UserAuthInterface;
use Drupal\user\UserInterface;
use Drupal\user\UserStorageInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Serializer;
class UserAuthenticationController extends ControllerBase implements ContainerInjectionInterface {
const LOGGED_IN = 1;
const LOGGED_OUT = 0;
protected $flood;
protected $userStorage;
protected $csrfToken;
protected $userAuth;
protected $routeProvider;
protected $serializer;
protected $serializerFormats = [];
protected $logger;
public function __construct(FloodInterface $flood, UserStorageInterface $user_storage, CsrfTokenGenerator $csrf_token, UserAuthInterface $user_auth, RouteProviderInterface $route_provider, Serializer $serializer, array $serializer_formats, LoggerInterface $logger) {
$this->flood = $flood;
$this->userStorage = $user_storage;
$this->csrfToken = $csrf_token;
$this->userAuth = $user_auth;
$this->serializer = $serializer;
$this->serializerFormats = $serializer_formats;
$this->routeProvider = $route_provider;
$this->logger = $logger;
}
public static function create(ContainerInterface $container) {
if ($container
->hasParameter('serializer.formats') && $container
->has('serializer')) {
$serializer = $container
->get('serializer');
$formats = $container
->getParameter('serializer.formats');
}
else {
$formats = [
'json',
];
$encoders = [
new JsonEncoder(),
];
$serializer = new Serializer([], $encoders);
}
return new static($container
->get('flood'), $container
->get('entity_type.manager')
->getStorage('user'), $container
->get('csrf_token'), $container
->get('user.auth'), $container
->get('router.route_provider'), $serializer, $formats, $container
->get('logger.factory')
->get('user'));
}
public function login(Request $request) {
$format = $this
->getRequestFormat($request);
$content = $request
->getContent();
$credentials = $this->serializer
->decode($content, $format);
if (!isset($credentials['name']) && !isset($credentials['pass'])) {
throw new BadRequestHttpException('Missing credentials.');
}
if (!isset($credentials['name'])) {
throw new BadRequestHttpException('Missing credentials.name.');
}
if (!isset($credentials['pass'])) {
throw new BadRequestHttpException('Missing credentials.pass.');
}
$this
->floodControl($request, $credentials['name']);
if ($this
->userIsBlocked($credentials['name'])) {
throw new BadRequestHttpException('The user has not been activated or is blocked.');
}
if ($uid = $this->userAuth
->authenticate($credentials['name'], $credentials['pass'])) {
$this->flood
->clear('user.http_login', $this
->getLoginFloodIdentifier($request, $credentials['name']));
$user = $this->userStorage
->load($uid);
$this
->userLoginFinalize($user);
$response_data = [];
if ($user
->get('uid')
->access('view', $user)) {
$response_data['current_user']['uid'] = $user
->id();
}
if ($user
->get('roles')
->access('view', $user)) {
$response_data['current_user']['roles'] = $user
->getRoles();
}
if ($user
->get('name')
->access('view', $user)) {
$response_data['current_user']['name'] = $user
->getAccountName();
}
$response_data['csrf_token'] = $this->csrfToken
->get('rest');
$logout_route = $this->routeProvider
->getRouteByName('user.logout.http');
$logout_path = ltrim($logout_route
->getPath(), '/');
$response_data['logout_token'] = $this->csrfToken
->get($logout_path);
$encoded_response_data = $this->serializer
->encode($response_data, $format);
return new Response($encoded_response_data);
}
$flood_config = $this
->config('user.flood');
if ($identifier = $this
->getLoginFloodIdentifier($request, $credentials['name'])) {
$this->flood
->register('user.http_login', $flood_config
->get('user_window'), $identifier);
}
$this->flood
->register('user.failed_login_ip', $flood_config
->get('ip_window'));
throw new BadRequestHttpException('Sorry, unrecognized username or password.');
}
public function resetPassword(Request $request) {
$format = $this
->getRequestFormat($request);
$content = $request
->getContent();
$credentials = $this->serializer
->decode($content, $format);
if (!isset($credentials['name']) && !isset($credentials['mail'])) {
throw new BadRequestHttpException('Missing credentials.name or credentials.mail');
}
if (isset($credentials['name'])) {
$users = $this->userStorage
->loadByProperties([
'name' => trim($credentials['name']),
]);
}
elseif (isset($credentials['mail'])) {
$users = $this->userStorage
->loadByProperties([
'mail' => trim($credentials['mail']),
]);
}
$account = reset($users);
if ($account && $account
->id()) {
if ($this
->userIsBlocked($account
->getAccountName())) {
throw new BadRequestHttpException('The user has not been activated or is blocked.');
}
$mail = _user_mail_notify('password_reset', $account, $account
->getPreferredLangcode());
if (empty($mail)) {
throw new BadRequestHttpException('Unable to send email. Contact the site administrator if the problem persists.');
}
else {
$this->logger
->notice('Password reset instructions mailed to %name at %email.', [
'%name' => $account
->getAccountName(),
'%email' => $account
->getEmail(),
]);
return new Response();
}
}
throw new BadRequestHttpException('Unrecognized username or email address.');
}
protected function userIsBlocked($name) {
return user_is_blocked($name);
}
protected function userLoginFinalize(UserInterface $user) {
user_login_finalize($user);
}
public function logout() {
$this
->userLogout();
return new Response(NULL, 204);
}
protected function userLogout() {
user_logout();
}
public function loginStatus() {
if ($this
->currentUser()
->isAuthenticated()) {
$response = new Response(self::LOGGED_IN);
}
else {
$response = new Response(self::LOGGED_OUT);
}
$response->headers
->set('Content-Type', 'text/plain');
return $response;
}
protected function getRequestFormat(Request $request) {
$format = $request
->getRequestFormat();
if (!in_array($format, $this->serializerFormats)) {
throw new BadRequestHttpException("Unrecognized format: {$format}.");
}
return $format;
}
protected function floodControl(Request $request, $username) {
$flood_config = $this
->config('user.flood');
if (!$this->flood
->isAllowed('user.failed_login_ip', $flood_config
->get('ip_limit'), $flood_config
->get('ip_window'))) {
throw new AccessDeniedHttpException('Access is blocked because of IP based flood prevention.', NULL, Response::HTTP_TOO_MANY_REQUESTS);
}
if ($identifier = $this
->getLoginFloodIdentifier($request, $username)) {
if (!$this->flood
->isAllowed('user.http_login', $flood_config
->get('user_limit'), $flood_config
->get('user_window'), $identifier)) {
if ($flood_config
->get('uid_only')) {
$error_message = sprintf('There have been more than %s failed login attempts for this account. It is temporarily blocked. Try again later or request a new password.', $flood_config
->get('user_limit'));
}
else {
$error_message = 'Too many failed login attempts from your IP address. This IP address is temporarily blocked.';
}
throw new AccessDeniedHttpException($error_message, NULL, Response::HTTP_TOO_MANY_REQUESTS);
}
}
}
protected function getLoginFloodIdentifier(Request $request, $username) {
$flood_config = $this
->config('user.flood');
$accounts = $this->userStorage
->loadByProperties([
'name' => $username,
'status' => 1,
]);
if ($account = reset($accounts)) {
if ($flood_config
->get('uid_only')) {
$identifier = $account
->id();
}
else {
$identifier = $account
->id() . '-' . $request
->getClientIp();
}
return $identifier;
}
return '';
}
}