diff -u b/core/core.services.yml b/core/core.services.yml --- b/core/core.services.yml +++ b/core/core.services.yml @@ -540 +540 @@ - - { name: access_check } + - { name: access_check } \ No newline at end of file diff -u b/core/lib/Drupal/Core/Access/AuthenticationProviderAccessCheck.php b/core/lib/Drupal/Core/Access/AuthenticationProviderAccessCheck.php --- b/core/lib/Drupal/Core/Access/AuthenticationProviderAccessCheck.php +++ b/core/lib/Drupal/Core/Access/AuthenticationProviderAccessCheck.php @@ -35,12 +35,18 @@ if (!empty($auth_provider_triggered)) { $allowed_auth_providers = $route->getOption('_auth'); if (empty($allowed_auth_providers)) { - $result = NULL; + return NULL; } - else { - $result = in_array($auth_provider_triggered, $allowed_auth_providers); + $check = in_array($auth_provider_triggered, $allowed_auth_providers); + + // Set user as anonymous as he should not be logged in because he logged in + // with not allowed authentication provider. + if ($check == FALSE) { + global $user; + $user = drupal_anonymous_user(); } - return $result; + + return $check; } } } diff -u b/core/lib/Drupal/Core/Authentication/AuthenticationManager.php b/core/lib/Drupal/Core/Authentication/AuthenticationManager.php --- b/core/lib/Drupal/Core/Authentication/AuthenticationManager.php +++ b/core/lib/Drupal/Core/Authentication/AuthenticationManager.php @@ -8,6 +8,8 @@ namespace Drupal\Core\Authentication; use Symfony\Component\HttpFoundation\Request; +use Symfony\Cmf\Component\Routing\RouteObjectInterface; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; /** * Manager for authentication. @@ -66,6 +68,7 @@ * The providers priority. */ public function addProvider($provider_id, AuthenticationProviderInterface $provider, $priority = 0) { + $provider_id = substr($provider_id, strlen('authentication.')); $this->providers[$provider_id] = $provider; $this->providerPriorities[$provider_id] = $priority; } @@ -139,7 +142,7 @@ // Save the ID of the triggered provider to the request so that it can be // accessed in Drupal\Core\Access\AuthenticationProviderAccessCheck. - $request->attributes->set('_authentication_provider', substr($this->triggeredProvider, strlen('authentication.'))); + $request->attributes->set('_authentication_provider', $this->triggeredProvider); return $this->account; } @@ -163,2 +166,23 @@ } + + /** + * {@inheritdoc} + */ + public function handleException(GetResponseForExceptionEvent $event) { + $request = $event->getRequest(); + $route = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT); + if (empty($route)) { + return; + } + $allowed_auth_providers = $route->getOption('_auth'); + foreach ($this->providers as $provider_id => $provider) { + if (!in_array($provider_id, $allowed_auth_providers)) { + continue; + } + + if (NULL !== $provider->handleException($event)) { + break; + } + } + } } diff -u b/core/lib/Drupal/Core/Authentication/AuthenticationProviderInterface.php b/core/lib/Drupal/Core/Authentication/AuthenticationProviderInterface.php --- b/core/lib/Drupal/Core/Authentication/AuthenticationProviderInterface.php +++ b/core/lib/Drupal/Core/Authentication/AuthenticationProviderInterface.php @@ -8,6 +8,7 @@ namespace Drupal\Core\Authentication; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; /** * Interface for authentication providers. @@ -42,2 +43,18 @@ public function cleanup(Request $request); + + + /** + * Handle exception. + * + * In case exception has happened we allow authentication providers react. + * Used in Drupal\Core\Authentication\Provider\HttpBasic to set up headers to + * prompt login. + * + * @param GetResponseForExceptionEvent $event + * + * @return bool + * TRUE - exception handled. No need to run through other providers + * NULL - no actions have beed done. Run through other providers + */ + public function handleException(GetResponseForExceptionEvent $event); } diff -u b/core/lib/Drupal/Core/Authentication/Provider/Cookie.php b/core/lib/Drupal/Core/Authentication/Provider/Cookie.php --- b/core/lib/Drupal/Core/Authentication/Provider/Cookie.php +++ b/core/lib/Drupal/Core/Authentication/Provider/Cookie.php @@ -8,6 +8,7 @@ namespace Drupal\Core\Authentication\Provider; use Drupal\Core\Authentication\AuthenticationProviderInterface; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; use Symfony\Component\HttpFoundation\Request; /** @@ -36,2 +37,9 @@ } + + /** + * {@inheritdoc} + */ + public function handleException(GetResponseForExceptionEvent $event) { + return NULL; + } } diff -u b/core/lib/Drupal/Core/Authentication/Provider/HttpBasic.php b/core/lib/Drupal/Core/Authentication/Provider/HttpBasic.php --- b/core/lib/Drupal/Core/Authentication/Provider/HttpBasic.php +++ b/core/lib/Drupal/Core/Authentication/Provider/HttpBasic.php @@ -8,6 +8,8 @@ namespace Drupal\Core\Authentication\Provider; use Drupal\Core\Authentication\AuthenticationProviderInterface; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; use Symfony\Component\HttpFoundation\Request; /** @@ -37,2 +39,12 @@ public function cleanup(Request $request) {} + + /** + * {@inheritdoc} + */ + public function handleException(GetResponseForExceptionEvent $event) { + $exception = $event->getException(); + if (user_is_anonymous() && get_class($exception) == 'Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException') { + $event->setException(new UnauthorizedHttpException('Basic realm="Drupal 8"', 'No authentication credentials provided.', $exception)); + } + } } diff -u b/core/lib/Drupal/Core/EventSubscriber/AuthenticationSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/AuthenticationSubscriber.php --- b/core/lib/Drupal/Core/EventSubscriber/AuthenticationSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/AuthenticationSubscriber.php @@ -11,6 +11,7 @@ use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; use Symfony\Component\HttpKernel\Event\FilterResponseEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -64,6 +65,17 @@ } /** + * Pass exception handling to authentication manager. + * + * @param GetResponseForExceptionEvent $event + */ + public function onException(GetResponseForExceptionEvent $event) { + if ($event->getRequestType() == HttpKernelInterface::MASTER_REQUEST) { + $this->authenticationManager->handleException($event); + } + } + + /** * {@inheritdoc} * * The priority for request must be higher than the highest event subscriber @@ -76,6 +88,7 @@ // access global $user in case language module enabled. $events[KernelEvents::REQUEST][] = array('onKernelRequestAuthenticate', 300); $events[KernelEvents::RESPONSE][] = array('onRespond', 0); + $events[KernelEvents::EXCEPTION][] = array('onException', 0); return $events; } } diff -u b/core/modules/system/lib/Drupal/system/Tests/Authentication/HttpBasicTest.php b/core/modules/system/lib/Drupal/system/Tests/Authentication/HttpBasicTest.php --- b/core/modules/system/lib/Drupal/system/Tests/Authentication/HttpBasicTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Authentication/HttpBasicTest.php @@ -48,7 +48,7 @@ $this->curlClose(); $this->drupalGet('router_test/test11'); - $this->assertResponse('403', 'Not authenticated on the route that allows only http_basic.'); + $this->assertResponse('401', 'Not authenticated on the route that allows only http_basic. Prompt to authenticate received.'); } /**