annotate core/lib/Drupal/Core/EventSubscriber/AuthenticationSubscriber.php @ 17:129ea1e6d783

Update, including to Drupal core 8.6.10
author Chris Cannam
date Thu, 28 Feb 2019 13:21:36 +0000
parents 4c8ae668cc8c
children af1871eacc83
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\Core\EventSubscriber;
Chris@0 4
Chris@0 5 use Drupal\Core\Authentication\AuthenticationProviderFilterInterface;
Chris@0 6 use Drupal\Core\Authentication\AuthenticationProviderChallengeInterface;
Chris@0 7 use Drupal\Core\Authentication\AuthenticationProviderInterface;
Chris@0 8 use Drupal\Core\Session\AccountProxyInterface;
Chris@0 9 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
Chris@0 10 use Symfony\Component\HttpKernel\Event\GetResponseEvent;
Chris@0 11 use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
Chris@0 12 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
Chris@0 13 use Symfony\Component\HttpKernel\HttpKernelInterface;
Chris@0 14 use Symfony\Component\HttpKernel\KernelEvents;
Chris@0 15
Chris@0 16 /**
Chris@0 17 * Authentication subscriber.
Chris@0 18 *
Chris@0 19 * Trigger authentication during the request.
Chris@0 20 */
Chris@0 21 class AuthenticationSubscriber implements EventSubscriberInterface {
Chris@0 22
Chris@0 23 /**
Chris@0 24 * Authentication provider.
Chris@0 25 *
Chris@0 26 * @var \Drupal\Core\Authentication\AuthenticationProviderInterface
Chris@0 27 */
Chris@0 28 protected $authenticationProvider;
Chris@0 29
Chris@0 30 /**
Chris@0 31 * Authentication provider filter.
Chris@0 32 *
Chris@0 33 * @var \Drupal\Core\Authentication\AuthenticationProviderFilterInterface|null
Chris@0 34 */
Chris@0 35 protected $filter;
Chris@0 36
Chris@0 37 /**
Chris@0 38 * Authentication challenge provider.
Chris@0 39 *
Chris@0 40 * @var \Drupal\Core\Authentication\AuthenticationProviderChallengeInterface|null
Chris@0 41 */
Chris@0 42 protected $challengeProvider;
Chris@0 43
Chris@0 44 /**
Chris@0 45 * Account proxy.
Chris@0 46 *
Chris@0 47 * @var \Drupal\Core\Session\AccountProxyInterface
Chris@0 48 */
Chris@0 49 protected $accountProxy;
Chris@0 50
Chris@0 51 /**
Chris@0 52 * Constructs an authentication subscriber.
Chris@0 53 *
Chris@0 54 * @param \Drupal\Core\Authentication\AuthenticationProviderInterface $authentication_provider
Chris@0 55 * An authentication provider.
Chris@0 56 * @param \Drupal\Core\Session\AccountProxyInterface $account_proxy
Chris@0 57 * Account proxy.
Chris@0 58 */
Chris@0 59 public function __construct(AuthenticationProviderInterface $authentication_provider, AccountProxyInterface $account_proxy) {
Chris@0 60 $this->authenticationProvider = $authentication_provider;
Chris@0 61 $this->filter = ($authentication_provider instanceof AuthenticationProviderFilterInterface) ? $authentication_provider : NULL;
Chris@0 62 $this->challengeProvider = ($authentication_provider instanceof AuthenticationProviderChallengeInterface) ? $authentication_provider : NULL;
Chris@0 63 $this->accountProxy = $account_proxy;
Chris@0 64 }
Chris@0 65
Chris@0 66 /**
Chris@0 67 * Authenticates user on request.
Chris@0 68 *
Chris@0 69 * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
Chris@0 70 * The request event.
Chris@0 71 *
Chris@0 72 * @see \Drupal\Core\Authentication\AuthenticationProviderInterface::authenticate()
Chris@0 73 */
Chris@0 74 public function onKernelRequestAuthenticate(GetResponseEvent $event) {
Chris@0 75 if ($event->getRequestType() === HttpKernelInterface::MASTER_REQUEST) {
Chris@0 76 $request = $event->getRequest();
Chris@0 77 if ($this->authenticationProvider->applies($request)) {
Chris@0 78 $account = $this->authenticationProvider->authenticate($request);
Chris@0 79 if ($account) {
Chris@0 80 $this->accountProxy->setAccount($account);
Chris@0 81 return;
Chris@0 82 }
Chris@0 83 }
Chris@0 84 // No account has been set explicitly, initialize the timezone here.
Chris@0 85 date_default_timezone_set(drupal_get_user_timezone());
Chris@0 86 }
Chris@0 87 }
Chris@0 88
Chris@0 89 /**
Chris@0 90 * Denies access if authentication provider is not allowed on this route.
Chris@0 91 *
Chris@0 92 * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
Chris@0 93 * The request event.
Chris@0 94 */
Chris@0 95 public function onKernelRequestFilterProvider(GetResponseEvent $event) {
Chris@0 96 if (isset($this->filter) && $event->getRequestType() === HttpKernelInterface::MASTER_REQUEST) {
Chris@0 97 $request = $event->getRequest();
Chris@0 98 if ($this->authenticationProvider->applies($request) && !$this->filter->appliesToRoutedRequest($request, TRUE)) {
Chris@0 99 throw new AccessDeniedHttpException('The used authentication method is not allowed on this route.');
Chris@0 100 }
Chris@0 101 }
Chris@0 102 }
Chris@0 103
Chris@0 104 /**
Chris@0 105 * Respond with a challenge on access denied exceptions if appropriate.
Chris@0 106 *
Chris@0 107 * On a 403 (access denied), if there are no credentials on the request, some
Chris@0 108 * authentication methods (e.g. basic auth) require that a challenge is sent
Chris@0 109 * to the client.
Chris@0 110 *
Chris@0 111 * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
Chris@0 112 * The exception event.
Chris@0 113 */
Chris@0 114 public function onExceptionSendChallenge(GetResponseForExceptionEvent $event) {
Chris@0 115 if (isset($this->challengeProvider) && $event->getRequestType() === HttpKernelInterface::MASTER_REQUEST) {
Chris@0 116 $request = $event->getRequest();
Chris@0 117 $exception = $event->getException();
Chris@0 118 if ($exception instanceof AccessDeniedHttpException && !$this->authenticationProvider->applies($request) && (!isset($this->filter) || $this->filter->appliesToRoutedRequest($request, FALSE))) {
Chris@0 119 $challenge_exception = $this->challengeProvider->challengeException($request, $exception);
Chris@0 120 if ($challenge_exception) {
Chris@0 121 $event->setException($challenge_exception);
Chris@0 122 }
Chris@0 123 }
Chris@0 124 }
Chris@0 125 }
Chris@0 126
Chris@0 127 /**
Chris@17 128 * Detect disallowed authentication methods on access denied exceptions.
Chris@17 129 *
Chris@17 130 * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
Chris@17 131 */
Chris@17 132 public function _onExceptionAccessDenied(GetResponseForExceptionEvent $event) {
Chris@17 133 if (isset($this->filter) && $event->isMasterRequest()) {
Chris@17 134 $request = $event->getRequest();
Chris@17 135 $exception = $event->getException();
Chris@17 136 if ($exception instanceof AccessDeniedHttpException && $this->authenticationProvider->applies($request) && !$this->filter->appliesToRoutedRequest($request, TRUE)) {
Chris@17 137 $event->setException(new AccessDeniedHttpException('The used authentication method is not allowed on this route.', $exception));
Chris@17 138 }
Chris@17 139 }
Chris@17 140 }
Chris@17 141
Chris@17 142 /**
Chris@0 143 * {@inheritdoc}
Chris@0 144 */
Chris@0 145 public static function getSubscribedEvents() {
Chris@0 146 // The priority for authentication must be higher than the highest event
Chris@0 147 // subscriber accessing the current user. Especially it must be higher than
Chris@0 148 // LanguageRequestSubscriber as LanguageManager accesses the current user if
Chris@0 149 // the language module is enabled.
Chris@0 150 $events[KernelEvents::REQUEST][] = ['onKernelRequestAuthenticate', 300];
Chris@0 151
Chris@0 152 // Access check must be performed after routing.
Chris@0 153 $events[KernelEvents::REQUEST][] = ['onKernelRequestFilterProvider', 31];
Chris@0 154 $events[KernelEvents::EXCEPTION][] = ['onExceptionSendChallenge', 75];
Chris@17 155 $events[KernelEvents::EXCEPTION][] = ['_onExceptionAccessDenied', 80];
Chris@0 156 return $events;
Chris@0 157 }
Chris@0 158
Chris@0 159 }