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