<?php/** * Pimcore * * This source file is available under two different licenses: * - GNU General Public License version 3 (GPLv3) * - Pimcore Enterprise License (PEL) * Full copyright and license information is available in * LICENSE.md which is distributed with this source code. * * @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org) * @license http://www.pimcore.org/license GPLv3 and PEL */namespace Pimcore\Bundle\AdminBundle\EventListener;use Pimcore\Bundle\AdminBundle\Controller\DoubleAuthenticationControllerInterface;use Pimcore\Bundle\AdminBundle\EventListener\Traits\ControllerTypeTrait;use Pimcore\Bundle\AdminBundle\Security\User\TokenStorageUserResolver;use Pimcore\Http\RequestMatcherFactory;use Pimcore\Tool\Authentication;use Symfony\Component\EventDispatcher\EventSubscriberInterface;use Symfony\Component\HttpFoundation\Request;use Symfony\Component\HttpFoundation\RequestMatcherInterface;use Symfony\Component\HttpKernel\Event\FilterControllerEvent;use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;use Symfony\Component\HttpKernel\KernelEvents;/** * Handles double authentication check for pimcore controllers after the firewall did to make sure the admin interface is * not accessible on configuration errors. Unauthenticated routes are not double-checked (e.g. login). * * TODO: the double authentication check is currently running for every DoubleAuthenticationControllerInterface, independent * of the request context, to ensure third party bundles using the AdminController handle authentication as well. Should we * do this on the pimcore context instead? */class AdminAuthenticationDoubleCheckListener implements EventSubscriberInterface{ use ControllerTypeTrait; /** * @var RequestMatcherFactory */ protected $requestMatcherFactory; /** * @var array */ protected $unauthenticatedRoutes; /** * @var RequestMatcherInterface[] */ protected $unauthenticatedMatchers; /** * @var TokenStorageUserResolver */ protected $tokenResolver; /** * @param RequestMatcherFactory $factory * @param TokenStorageUserResolver $tokenResolver * @param array $unauthenticatedRoutes */ public function __construct( RequestMatcherFactory $factory, TokenStorageUserResolver $tokenResolver, array $unauthenticatedRoutes ) { $this->requestMatcherFactory = $factory; $this->tokenResolver = $tokenResolver; $this->unauthenticatedRoutes = $unauthenticatedRoutes; } /** * @inheritDoc */ public static function getSubscribedEvents() { return [ KernelEvents::CONTROLLER => 'onKernelController' ]; } public function onKernelController(FilterControllerEvent $event) { if (!$this->isControllerType($event, DoubleAuthenticationControllerInterface::class)) { return; } $request = $event->getRequest(); /** @var DoubleAuthenticationControllerInterface $controller */ $controller = $this->getControllerType($event, DoubleAuthenticationControllerInterface::class); // double check we have a valid user to make sure there is no invalid security config // opening admin interface to the public if ($this->requestNeedsAuthentication($request)) { if ($controller->needsSessionDoubleAuthenticationCheck()) { $this->checkSessionUser(); } if ($controller->needsStorageDoubleAuthenticationCheck()) { $this->checkTokenStorageUser(); } } } /** * Check if the current request needs double authentication * * @param Request $request * * @return bool */ protected function requestNeedsAuthentication(Request $request) { foreach ($this->getUnauthenticatedMatchers() as $matcher) { if ($matcher->matches($request)) { return false; } } return true; } /** * Get list of paths which don't need double authentication check * * @return RequestMatcherInterface[] */ protected function getUnauthenticatedMatchers() { if (null === $this->unauthenticatedMatchers) { $this->unauthenticatedMatchers = $this->requestMatcherFactory->buildRequestMatchers($this->unauthenticatedRoutes); } return $this->unauthenticatedMatchers; } /** * @throws AccessDeniedHttpException * if there's no current user in the session */ protected function checkSessionUser() { $user = Authentication::authenticateSession(); if (null === $user) { throw new AccessDeniedHttpException('User is invalid.'); } } /** * @throws AccessDeniedHttpException * if there's no current user in the token storage */ protected function checkTokenStorageUser() { $user = $this->tokenResolver->getUser(); if (null === $user || !Authentication::isValidUser($user)) { throw new AccessDeniedHttpException('User is invalid.'); } }}