Issue #1793520 by disasm, klausi, tnightingale, Crell: Add access control mechanism for new router system.
parent
cf860477f4
commit
c37973bb27
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains Drupal\Core\Access\AccessCheckInterface.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\Core\Access;
|
||||||
|
|
||||||
|
use Symfony\Component\Routing\Route;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An access check service determines access rules for particular routes.
|
||||||
|
*/
|
||||||
|
interface AccessCheckInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Declares whether the access check applies to a specific route or not.
|
||||||
|
*
|
||||||
|
* @param \Symfony\Component\Routing\Route $route
|
||||||
|
* The route to consider attaching to.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* TRUE if the check applies to the passed route, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
public function applies(Route $route);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks for access to route.
|
||||||
|
*
|
||||||
|
* @param \Symfony\Component\Routing\Route $route
|
||||||
|
* The route to check against.
|
||||||
|
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||||
|
* The request object.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
* TRUE if access is allowed.
|
||||||
|
* FALSE if not.
|
||||||
|
* NULL if no opinion.
|
||||||
|
*/
|
||||||
|
public function access(Route $route, Request $request);
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains Drupal\Core\Access\AccessManager.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\Core\Access;
|
||||||
|
|
||||||
|
use Symfony\Component\Routing\RouteCollection;
|
||||||
|
use Symfony\Component\Routing\Route;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerAware;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches access check services to routes and runs them on request.
|
||||||
|
*/
|
||||||
|
class AccessManager extends ContainerAware {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of registered access check service ids.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $checkIds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of access check objects keyed by service id.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $checks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The request object.
|
||||||
|
*
|
||||||
|
* @var \Symfony\Component\HttpFoundation\Request
|
||||||
|
*/
|
||||||
|
protected $request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new AccessManager.
|
||||||
|
*
|
||||||
|
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||||
|
* The request object.
|
||||||
|
*/
|
||||||
|
public function __construct(Request $request) {
|
||||||
|
$this->request = $request;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a new AccessCheck by service ID.
|
||||||
|
*
|
||||||
|
* @param string $service_id
|
||||||
|
* The ID of the service in the Container that provides a check.
|
||||||
|
*/
|
||||||
|
public function addCheckService($service_id) {
|
||||||
|
$this->checkIds[] = $service_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For each route, saves a list of applicable access checks to the route.
|
||||||
|
*
|
||||||
|
* @param \Symfony\Component\Routing\RouteCollection $routes
|
||||||
|
* A collection of routes to apply checks to.
|
||||||
|
*/
|
||||||
|
public function setChecks(RouteCollection $routes) {
|
||||||
|
foreach ($routes as $route) {
|
||||||
|
$checks = $this->applies($route);
|
||||||
|
if (!empty($checks)) {
|
||||||
|
$route->setOption('_access_checks', $checks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine which registered access checks apply to a route.
|
||||||
|
*
|
||||||
|
* @param \Symfony\Component\Routing\Route $route
|
||||||
|
* The route to get list of access checks for.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* An array of service ids for the access checks that apply to passed
|
||||||
|
* route.
|
||||||
|
*/
|
||||||
|
protected function applies(Route $route) {
|
||||||
|
$checks = array();
|
||||||
|
|
||||||
|
foreach ($this->checkIds as $service_id) {
|
||||||
|
if (empty($this->checks[$service_id])) {
|
||||||
|
$this->loadCheck($service_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->checks[$service_id]->applies($route)) {
|
||||||
|
$checks[] = $service_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $checks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks a route against applicable access check services.
|
||||||
|
*
|
||||||
|
* Determines whether the route is accessible or not.
|
||||||
|
*
|
||||||
|
* @param \Symfony\Component\Routing\Route $route
|
||||||
|
* The route to check access to.
|
||||||
|
*
|
||||||
|
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
|
||||||
|
* If any access check denies access or none explicitly approve.
|
||||||
|
*/
|
||||||
|
public function check(Route $route) {
|
||||||
|
$access = FALSE;
|
||||||
|
$checks = $route->getOption('_access_checks') ?: array();
|
||||||
|
|
||||||
|
// No checks == deny by default.
|
||||||
|
foreach ($checks as $service_id) {
|
||||||
|
if (empty($this->checks[$service_id])) {
|
||||||
|
$this->loadCheck($service_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$access = $this->checks[$service_id]->access($route, $this->request);
|
||||||
|
if ($access === FALSE) {
|
||||||
|
// A check has denied access, no need to continue checking.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Access has been denied or not explicily approved.
|
||||||
|
if (!$access) {
|
||||||
|
throw new AccessDeniedHttpException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lazy-loads access check services.
|
||||||
|
*
|
||||||
|
* @param string $service_id
|
||||||
|
* The service id of the access check service to load.
|
||||||
|
*/
|
||||||
|
protected function loadCheck($service_id) {
|
||||||
|
if (!in_array($service_id, $this->checkIds)) {
|
||||||
|
throw new \InvalidArgumentException(sprintf('No check has been registered for %s', $service_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->checks[$service_id] = $this->container->get($service_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains Drupal\Core\Access\DefaultAccessCheck.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\Core\Access;
|
||||||
|
|
||||||
|
use Symfony\Component\Routing\Route;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows access to routes to be controlled by an '_access' boolean parameter.
|
||||||
|
*/
|
||||||
|
class DefaultAccessCheck implements AccessCheckInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements AccessCheckInterface::applies().
|
||||||
|
*/
|
||||||
|
public function applies(Route $route) {
|
||||||
|
return array_key_exists('_access', $route->getRequirements());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements AccessCheckInterface::access().
|
||||||
|
*/
|
||||||
|
public function access(Route $route, Request $request) {
|
||||||
|
return $route->getRequirement('_access');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains Drupal\Core\Access\PermissionAccessCheck.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\Core\Access;
|
||||||
|
|
||||||
|
use Symfony\Component\Routing\Route;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines access to routes based on permissions defined via hook_permission().
|
||||||
|
*/
|
||||||
|
class PermissionAccessCheck implements AccessCheckInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements AccessCheckInterface::applies().
|
||||||
|
*/
|
||||||
|
public function applies(Route $route) {
|
||||||
|
return array_key_exists('_permission', $route->getRequirements());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements AccessCheckInterface::access().
|
||||||
|
*/
|
||||||
|
public function access(Route $route, Request $request) {
|
||||||
|
$permission = $route->getRequirement('_permission');
|
||||||
|
// @todo Replace user_access() with a correctly injected and session-using
|
||||||
|
// alternative.
|
||||||
|
// If user_access() fails, return NULL to give other checks a chance.
|
||||||
|
return user_access($permission) ? TRUE : NULL;
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@
|
||||||
namespace Drupal\Core;
|
namespace Drupal\Core;
|
||||||
|
|
||||||
use Drupal\Core\DependencyInjection\Compiler\RegisterKernelListenersPass;
|
use Drupal\Core\DependencyInjection\Compiler\RegisterKernelListenersPass;
|
||||||
|
use Drupal\Core\DependencyInjection\Compiler\RegisterAccessChecksPass;
|
||||||
use Drupal\Core\DependencyInjection\Compiler\RegisterMatchersPass;
|
use Drupal\Core\DependencyInjection\Compiler\RegisterMatchersPass;
|
||||||
use Drupal\Core\DependencyInjection\Compiler\RegisterNestedMatchersPass;
|
use Drupal\Core\DependencyInjection\Compiler\RegisterNestedMatchersPass;
|
||||||
use Drupal\Core\DependencyInjection\Compiler\RegisterSerializationClassesPass;
|
use Drupal\Core\DependencyInjection\Compiler\RegisterSerializationClassesPass;
|
||||||
|
@ -179,8 +180,18 @@ class CoreBundle extends Bundle {
|
||||||
$container->register('view_subscriber', 'Drupal\Core\EventSubscriber\ViewSubscriber')
|
$container->register('view_subscriber', 'Drupal\Core\EventSubscriber\ViewSubscriber')
|
||||||
->addArgument(new Reference('content_negotiation'))
|
->addArgument(new Reference('content_negotiation'))
|
||||||
->addTag('event_subscriber');
|
->addTag('event_subscriber');
|
||||||
$container->register('access_subscriber', 'Drupal\Core\EventSubscriber\AccessSubscriber')
|
$container->register('legacy_access_subscriber', 'Drupal\Core\EventSubscriber\LegacyAccessSubscriber')
|
||||||
->addTag('event_subscriber');
|
->addTag('event_subscriber');
|
||||||
|
$container->register('access_manager', 'Drupal\Core\Access\AccessManager')
|
||||||
|
->addArgument(new Reference('request'))
|
||||||
|
->addMethodCall('setContainer', array(new Reference('service_container')));
|
||||||
|
$container->register('access_subscriber', 'Drupal\Core\EventSubscriber\AccessSubscriber')
|
||||||
|
->addArgument(new Reference('access_manager'))
|
||||||
|
->addTag('event_subscriber');
|
||||||
|
$container->register('access_check.default', 'Drupal\Core\Access\DefaultAccessCheck')
|
||||||
|
->addTag('access_check');
|
||||||
|
$container->register('access_check.permission', 'Drupal\Core\Access\PermissionAccessCheck')
|
||||||
|
->addTag('access_check');
|
||||||
$container->register('maintenance_mode_subscriber', 'Drupal\Core\EventSubscriber\MaintenanceModeSubscriber')
|
$container->register('maintenance_mode_subscriber', 'Drupal\Core\EventSubscriber\MaintenanceModeSubscriber')
|
||||||
->addTag('event_subscriber');
|
->addTag('event_subscriber');
|
||||||
$container->register('path_subscriber', 'Drupal\Core\EventSubscriber\PathSubscriber')
|
$container->register('path_subscriber', 'Drupal\Core\EventSubscriber\PathSubscriber')
|
||||||
|
@ -221,6 +232,9 @@ class CoreBundle extends Bundle {
|
||||||
$container->addCompilerPass(new RegisterKernelListenersPass(), PassConfig::TYPE_AFTER_REMOVING);
|
$container->addCompilerPass(new RegisterKernelListenersPass(), PassConfig::TYPE_AFTER_REMOVING);
|
||||||
// Add a compiler pass for adding Normalizers and Encoders to Serializer.
|
// Add a compiler pass for adding Normalizers and Encoders to Serializer.
|
||||||
$container->addCompilerPass(new RegisterSerializationClassesPass());
|
$container->addCompilerPass(new RegisterSerializationClassesPass());
|
||||||
|
// Add a compiler pass for registering event subscribers.
|
||||||
|
$container->addCompilerPass(new RegisterKernelListenersPass(), PassConfig::TYPE_AFTER_REMOVING);
|
||||||
|
$container->addCompilerPass(new RegisterAccessChecksPass());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains Drupal\Core\DependencyInjection\Compiler\RegisterAccessChecksPass.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\Core\DependencyInjection\Compiler;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds services tagged 'access_check' to the access_manager service.
|
||||||
|
*/
|
||||||
|
class RegisterAccessChecksPass implements CompilerPassInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements CompilerPassInterface::process().
|
||||||
|
*
|
||||||
|
* Adds services tagged 'access_check' to the access_manager service.
|
||||||
|
*/
|
||||||
|
public function process(ContainerBuilder $container) {
|
||||||
|
if (!$container->hasDefinition('access_manager')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$access_manager = $container->getDefinition('access_manager');
|
||||||
|
foreach ($container->findTaggedServiceIds('access_check') as $id => $attributes) {
|
||||||
|
$access_manager->addMethodCall('addCheckService', array($id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,37 +2,59 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file
|
* @file
|
||||||
* Definition of Drupal\Core\EventSubscriber\AccessSubscriber.
|
* Contains Drupal\Core\EventSubscriber\AccessSubscriber.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Drupal\Core\EventSubscriber;
|
namespace Drupal\Core\EventSubscriber;
|
||||||
|
|
||||||
use Symfony\Component\HttpKernel\KernelEvents;
|
use Symfony\Component\HttpKernel\KernelEvents;
|
||||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
|
||||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||||
|
use Drupal\Core\Routing\RoutingEvents;
|
||||||
|
use Drupal\Core\Access\AccessManager;
|
||||||
|
use Drupal\Core\Routing\RouteBuildEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Access subscriber for controller requests.
|
* Access subscriber for controller requests.
|
||||||
*/
|
*/
|
||||||
class AccessSubscriber implements EventSubscriberInterface {
|
class AccessSubscriber implements EventSubscriberInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new AccessSubscriber.
|
||||||
|
*
|
||||||
|
* @param \Drupal\Core\Access\AccessManager $access_manager
|
||||||
|
* The access check manager that will be responsible for applying
|
||||||
|
* AccessCheckers against routes.
|
||||||
|
*/
|
||||||
|
public function __construct(AccessManager $access_manager) {
|
||||||
|
$this->accessManager = $access_manager;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies that the current user can access the requested path.
|
* Verifies that the current user can access the requested path.
|
||||||
*
|
*
|
||||||
* @todo This is a total hack to keep our current access system working. It
|
* @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
|
||||||
* should be replaced with something robust and injected at some point.
|
|
||||||
*
|
|
||||||
* @param Symfony\Component\HttpKernel\Event\GetResponseEvent $event
|
|
||||||
* The Event to process.
|
* The Event to process.
|
||||||
*/
|
*/
|
||||||
public function onKernelRequestAccessCheck(GetResponseEvent $event) {
|
public function onKernelRequestAccessCheck(GetResponseEvent $event) {
|
||||||
|
$request = $event->getRequest();
|
||||||
$router_item = $event->getRequest()->attributes->get('drupal_menu_item');
|
if (!$request->attributes->has('_route')) {
|
||||||
|
// If no Route is available it is likely a static resource and access is
|
||||||
if (isset($router_item['access']) && !$router_item['access']) {
|
// handled elsewhere.
|
||||||
throw new AccessDeniedHttpException();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->accessManager->check($request->attributes->get('_route'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply access checks to routes.
|
||||||
|
*
|
||||||
|
* @param \Drupal\Core\Routing\RouteBuildEvent $event
|
||||||
|
* The event to process.
|
||||||
|
*/
|
||||||
|
public function onRoutingRouteAlterSetAccessCheck(RouteBuildEvent $event) {
|
||||||
|
$this->accessManager->setChecks($event->getRouteCollection());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,6 +65,8 @@ class AccessSubscriber implements EventSubscriberInterface {
|
||||||
*/
|
*/
|
||||||
static function getSubscribedEvents() {
|
static function getSubscribedEvents() {
|
||||||
$events[KernelEvents::REQUEST][] = array('onKernelRequestAccessCheck', 30);
|
$events[KernelEvents::REQUEST][] = array('onKernelRequestAccessCheck', 30);
|
||||||
|
// Setting very low priority to ensure access checks are run after alters.
|
||||||
|
$events[RoutingEvents::ALTER][] = array('onRoutingRouteAlterSetAccessCheck', 0);
|
||||||
|
|
||||||
return $events;
|
return $events;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains Drupal\Core\EventSubscriber\LegacyAccessSubscriber.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\Core\EventSubscriber;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpKernel\KernelEvents;
|
||||||
|
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||||
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access subscriber for legacy controller requests.
|
||||||
|
*/
|
||||||
|
class LegacyAccessSubscriber implements EventSubscriberInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that the current user can access the requested path.
|
||||||
|
*
|
||||||
|
* @todo This is a total hack to keep our current access system working. It
|
||||||
|
* should be replaced with something robust and injected at some point.
|
||||||
|
*
|
||||||
|
* @param Symfony\Component\HttpKernel\Event\GetResponseEvent $event
|
||||||
|
* The Event to process.
|
||||||
|
*/
|
||||||
|
public function onKernelRequestAccessCheck(GetResponseEvent $event) {
|
||||||
|
|
||||||
|
$router_item = $event->getRequest()->attributes->get('drupal_menu_item');
|
||||||
|
|
||||||
|
if (isset($router_item['access']) && !$router_item['access']) {
|
||||||
|
throw new AccessDeniedHttpException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the methods in this class that should be listeners.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* An array of event listener definitions.
|
||||||
|
*/
|
||||||
|
static function getSubscribedEvents() {
|
||||||
|
$events[KernelEvents::REQUEST][] = array('onKernelRequestAccessCheck', 30);
|
||||||
|
|
||||||
|
return $events;
|
||||||
|
}
|
||||||
|
}
|
|
@ -53,7 +53,8 @@ class FirstEntryFinalMatcher implements FinalMatcherInterface {
|
||||||
|
|
||||||
preg_match($compiled->getRegex(), $path, $matches);
|
preg_match($compiled->getRegex(), $path, $matches);
|
||||||
|
|
||||||
return array_merge($this->mergeDefaults($matches, $route->getDefaults()), array('_route' => $name));
|
$route->setOption('_name', $name);
|
||||||
|
return array_merge($this->mergeDefaults($matches, $route->getDefaults()), array('_route' => $route));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@ class RouteSubscriber implements EventSubscriberInterface {
|
||||||
|
|
||||||
// @todo Switch to ->addCollection() once http://drupal.org/node/1819018 is resolved.
|
// @todo Switch to ->addCollection() once http://drupal.org/node/1819018 is resolved.
|
||||||
foreach ($plugin->routes() as $name => $route) {
|
foreach ($plugin->routes() as $name => $route) {
|
||||||
|
$route->setRequirement('_access', 'TRUE');
|
||||||
$collection->add("rest.$name", $route);
|
$collection->add("rest.$name", $route);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains Drupal\system\Access\CronAccessCheck.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\system\Access;
|
||||||
|
|
||||||
|
use Drupal\Core\Access\AccessCheckInterface;
|
||||||
|
use Symfony\Component\Routing\Route;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access check for cron routes.
|
||||||
|
*/
|
||||||
|
class CronAccessCheck implements AccessCheckInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements AccessCheckInterface::applies().
|
||||||
|
*/
|
||||||
|
public function applies(Route $route) {
|
||||||
|
return array_key_exists('_access_system_cron', $route->getRequirements());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements AccessCheckInterface::access().
|
||||||
|
*/
|
||||||
|
public function access(Route $route, Request $request) {
|
||||||
|
$key = $request->attributes->get('key');
|
||||||
|
if ($key != state()->get('system.cron_key')) {
|
||||||
|
watchdog('cron', 'Cron could not run because an invalid key was used.', array(), WATCHDOG_NOTICE);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
elseif (config('system.maintenance')->get('enabled')) {
|
||||||
|
watchdog('cron', 'Cron could not run because the site is in maintenance mode.', array(), WATCHDOG_NOTICE);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,10 +8,9 @@
|
||||||
namespace Drupal\system;
|
namespace Drupal\system;
|
||||||
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controllers for Cron handling.
|
* Controller for Cron handling.
|
||||||
*/
|
*/
|
||||||
class CronController {
|
class CronController {
|
||||||
|
|
||||||
|
@ -21,35 +20,11 @@ class CronController {
|
||||||
* @return Symfony\Component\HttpFoundation\Response
|
* @return Symfony\Component\HttpFoundation\Response
|
||||||
* A Symfony response object.
|
* A Symfony response object.
|
||||||
*/
|
*/
|
||||||
public function run($key) {
|
public function run() {
|
||||||
if (!$this->access($key)) {
|
|
||||||
throw new AccessDeniedHttpException();
|
|
||||||
}
|
|
||||||
|
|
||||||
// @todo Make this an injected object.
|
// @todo Make this an injected object.
|
||||||
drupal_cron_run();
|
drupal_cron_run();
|
||||||
|
|
||||||
// HTTP 204 is "No content", meaning "I did what you asked and we're done."
|
// HTTP 204 is "No content", meaning "I did what you asked and we're done."
|
||||||
return new Response('', 204);
|
return new Response('', 204);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines if the user has access to run cron.
|
|
||||||
*
|
|
||||||
* @todo Eliminate this method in favor of a new-style access checker once
|
|
||||||
* http://drupal.org/node/1793520 gets in.
|
|
||||||
*/
|
|
||||||
function access($key) {
|
|
||||||
if ($key != state()->get('system.cron_key')) {
|
|
||||||
watchdog('cron', 'Cron could not run because an invalid key was used.', array(), WATCHDOG_NOTICE);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
elseif (config('system.maintenance')->get('enabled')) {
|
|
||||||
watchdog('cron', 'Cron could not run because the site is in maintenance mode.', array(), WATCHDOG_NOTICE);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains Drupal\system\SystemBundle.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\system;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* System dependency injection container.
|
||||||
|
*/
|
||||||
|
class SystemBundle extends Bundle {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides Bundle::build().
|
||||||
|
*/
|
||||||
|
public function build(ContainerBuilder $container) {
|
||||||
|
$container->register('access_check.cron', 'Drupal\system\Access\CronAccessCheck')
|
||||||
|
->addTag('access_check');
|
||||||
|
}
|
||||||
|
}
|
|
@ -61,7 +61,7 @@ class FirstEntryFinalMatcherTest extends UnitTestBase {
|
||||||
$matcher->setCollection($collection);
|
$matcher->setCollection($collection);
|
||||||
$attributes = $matcher->matchRequest($request);
|
$attributes = $matcher->matchRequest($request);
|
||||||
|
|
||||||
$this->assertEqual($attributes['_route'], 'route_a', 'The correct matching route was found.');
|
$this->assertEqual($attributes['_route']->getOption('_name'), 'route_a', 'The correct matching route was found.');
|
||||||
$this->assertEqual($attributes['_controller'], 'foo', 'The correct controller was found.');
|
$this->assertEqual($attributes['_controller'], 'foo', 'The correct controller was found.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ class FirstEntryFinalMatcherTest extends UnitTestBase {
|
||||||
$matcher->setCollection($collection);
|
$matcher->setCollection($collection);
|
||||||
$attributes = $matcher->matchRequest($request);
|
$attributes = $matcher->matchRequest($request);
|
||||||
|
|
||||||
$this->assertEqual($attributes['_route'], 'route_a', 'The correct matching route was found.');
|
$this->assertEqual($attributes['_route']->getOption('_name'), 'route_a', 'The correct matching route was found.');
|
||||||
$this->assertEqual($attributes['_controller'], 'foo', 'The correct controller was found.');
|
$this->assertEqual($attributes['_controller'], 'foo', 'The correct controller was found.');
|
||||||
$this->assertEqual($attributes['value'], 'narf', 'Required placeholder value found.');
|
$this->assertEqual($attributes['value'], 'narf', 'Required placeholder value found.');
|
||||||
}
|
}
|
||||||
|
@ -105,7 +105,7 @@ class FirstEntryFinalMatcherTest extends UnitTestBase {
|
||||||
$matcher->setCollection($collection);
|
$matcher->setCollection($collection);
|
||||||
$attributes = $matcher->matchRequest($request);
|
$attributes = $matcher->matchRequest($request);
|
||||||
|
|
||||||
$this->assertEqual($attributes['_route'], 'route_a', 'The correct matching route was found.');
|
$this->assertEqual($attributes['_route']->getOption('_name'), 'route_a', 'The correct matching route was found.');
|
||||||
$this->assertEqual($attributes['_controller'], 'foo', 'The correct controller was found.');
|
$this->assertEqual($attributes['_controller'], 'foo', 'The correct controller was found.');
|
||||||
$this->assertEqual($attributes['value'], 'poink', 'Optional placeholder value used default.');
|
$this->assertEqual($attributes['value'], 'poink', 'Optional placeholder value used default.');
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ class HttpMethodMatcherTest extends UnitTestBase {
|
||||||
|
|
||||||
$this->fixtures = new RoutingFixtures();
|
$this->fixtures = new RoutingFixtures();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Confirms that the HttpMethod matcher matches properly.
|
* Confirms that the HttpMethod matcher matches properly.
|
||||||
*/
|
*/
|
||||||
|
@ -78,7 +78,7 @@ class HttpMethodMatcherTest extends UnitTestBase {
|
||||||
|
|
||||||
$attributes = $matcher->matchRequest($request);
|
$attributes = $matcher->matchRequest($request);
|
||||||
|
|
||||||
$this->assertEqual($attributes['_route'], 'route_a', 'The correct matching route was found.');
|
$this->assertEqual($attributes['_route']->getOption('_name'), 'route_a', 'The correct matching route was found.');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -93,7 +93,7 @@ class MimeTypeMatcherTest extends UnitTestBase {
|
||||||
$request->headers->set('Accept', 'text/html, text/xml;q=0.9');
|
$request->headers->set('Accept', 'text/html, text/xml;q=0.9');
|
||||||
|
|
||||||
$attributes = $matcher->matchRequest($request);
|
$attributes = $matcher->matchRequest($request);
|
||||||
$this->assertEqual($attributes['_route'], 'route_e', 'The correct matching route was found.');
|
$this->assertEqual($attributes['_route']->getOption('_name'), 'route_e', 'The correct matching route was found.');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -60,6 +60,6 @@ class NestedMatcherTest extends UnitTestBase {
|
||||||
|
|
||||||
$attributes = $matcher->matchRequest($request);
|
$attributes = $matcher->matchRequest($request);
|
||||||
|
|
||||||
$this->assertEqual($attributes['_route'], 'route_a', 'The correct matching route was found.');
|
$this->assertEqual($attributes['_route']->getOption('_name'), 'route_a', 'The correct matching route was found.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains Drupal\system\Tests\Routing\RouterPermissionTest.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\system\Tests\Routing;
|
||||||
|
|
||||||
|
use Drupal\simpletest\WebTestBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic tests for access permissions in routes.
|
||||||
|
*/
|
||||||
|
class RouterPermissionTest extends WebTestBase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modules to enable.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public static $modules = array('router_test');
|
||||||
|
|
||||||
|
public static function getInfo() {
|
||||||
|
return array(
|
||||||
|
'name' => 'Router Permission tests',
|
||||||
|
'description' => 'Function Tests for the routing permission system.',
|
||||||
|
'group' => 'Routing',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests permission requirements on routes.
|
||||||
|
*/
|
||||||
|
public function testPermissionAccess() {
|
||||||
|
|
||||||
|
$this->drupalGet('router_test/test7');
|
||||||
|
$this->assertResponse(403, "Access denied for a route where we don't have a permission");
|
||||||
|
|
||||||
|
$this->drupalGet('router_test/test8');
|
||||||
|
$this->assertResponse(403, 'Access denied by default if no access specified');
|
||||||
|
|
||||||
|
$user = $this->drupalCreateUser(array('access test7'));
|
||||||
|
$this->drupalLogin($user);
|
||||||
|
$this->drupalGet('router_test/test7');
|
||||||
|
$this->assertResponse(200);
|
||||||
|
$this->assertNoRaw('Access denied');
|
||||||
|
$this->assertRaw('test7text', 'The correct string was returned because the route was successful.');
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
cron:
|
system.cron:
|
||||||
pattern: '/cron/{key}'
|
pattern: '/cron/{key}'
|
||||||
defaults:
|
defaults:
|
||||||
_controller: '\Drupal\system\CronController::run'
|
_controller: '\Drupal\system\CronController::run'
|
||||||
|
requirements:
|
||||||
|
_access_system_cron: 'TRUE'
|
||||||
|
|
|
@ -35,6 +35,8 @@ class RouteTestSubscriber implements EventSubscriberInterface {
|
||||||
$collection = $event->getRouteCollection();
|
$collection = $event->getRouteCollection();
|
||||||
$route = new Route('/router_test/test5', array(
|
$route = new Route('/router_test/test5', array(
|
||||||
'_content' => '\Drupal\router_test\TestControllers::test5'
|
'_content' => '\Drupal\router_test\TestControllers::test5'
|
||||||
|
), array(
|
||||||
|
'_access' => 'TRUE'
|
||||||
));
|
));
|
||||||
$collection->add('router_test_5', $route);
|
$collection->add('router_test_5', $route);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,4 +34,16 @@ class TestControllers {
|
||||||
return "test5";
|
return "test5";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test6() {
|
||||||
|
return new Response('test6');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test7() {
|
||||||
|
return new Response('test7text');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test8() {
|
||||||
|
return new Response('test8');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1,13 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_permission().
|
||||||
|
*/
|
||||||
|
function router_test_permission() {
|
||||||
|
return array(
|
||||||
|
'access test7' => array(
|
||||||
|
'title' => t('Access test7 route'),
|
||||||
|
'description' => t('Test permission only.'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -2,24 +2,46 @@ router_test_1:
|
||||||
pattern: '/router_test/test1'
|
pattern: '/router_test/test1'
|
||||||
defaults:
|
defaults:
|
||||||
_controller: '\Drupal\router_test\TestControllers::test1'
|
_controller: '\Drupal\router_test\TestControllers::test1'
|
||||||
|
requirements:
|
||||||
|
_access: 'TRUE'
|
||||||
|
|
||||||
router_test_2:
|
router_test_2:
|
||||||
pattern: '/router_test/test2'
|
pattern: '/router_test/test2'
|
||||||
defaults:
|
defaults:
|
||||||
_content: '\Drupal\router_test\TestControllers::test2'
|
_content: '\Drupal\router_test\TestControllers::test2'
|
||||||
|
requirements:
|
||||||
|
_access: 'TRUE'
|
||||||
|
|
||||||
router_test_3:
|
router_test_3:
|
||||||
pattern: '/router_test/test3/{value}'
|
pattern: '/router_test/test3/{value}'
|
||||||
defaults:
|
defaults:
|
||||||
_content: '\Drupal\router_test\TestControllers::test3'
|
_content: '\Drupal\router_test\TestControllers::test3'
|
||||||
|
requirements:
|
||||||
|
_access: 'TRUE'
|
||||||
|
|
||||||
router_test_4:
|
router_test_4:
|
||||||
pattern: '/router_test/test4/{value}'
|
pattern: '/router_test/test4/{value}'
|
||||||
defaults:
|
defaults:
|
||||||
_content: '\Drupal\router_test\TestControllers::test4'
|
_content: '\Drupal\router_test\TestControllers::test4'
|
||||||
value: 'narf'
|
value: 'narf'
|
||||||
|
requirements:
|
||||||
|
_access: 'TRUE'
|
||||||
|
|
||||||
router_test_6:
|
router_test_6:
|
||||||
pattern: '/router_test/test6'
|
pattern: '/router_test/test6'
|
||||||
defaults:
|
defaults:
|
||||||
_controller: '\Drupal\router_test\TestControllers::test1'
|
_controller: '\Drupal\router_test\TestControllers::test1'
|
||||||
|
requirements:
|
||||||
|
_access: 'TRUE'
|
||||||
|
|
||||||
|
router_test_7:
|
||||||
|
pattern: '/router_test/test7'
|
||||||
|
defaults:
|
||||||
|
_controller: '\Drupal\router_test\TestControllers::test7'
|
||||||
|
requirements:
|
||||||
|
_permission: 'access test7'
|
||||||
|
|
||||||
|
router_test_8:
|
||||||
|
pattern: '/router_test/test8'
|
||||||
|
defaults:
|
||||||
|
_controller: '\Drupal\router_test\TestControllers::test8'
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains Drupal\user\Access\RegisterAccessCheck.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\user\Access;
|
||||||
|
|
||||||
|
use Drupal\Core\Access\AccessCheckInterface;
|
||||||
|
use Symfony\Component\Routing\Route;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access check for user registration routes.
|
||||||
|
*/
|
||||||
|
class RegisterAccessCheck implements AccessCheckInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements AccessCheckInterface::applies().
|
||||||
|
*/
|
||||||
|
public function applies(Route $route) {
|
||||||
|
return array_key_exists('_access_user_register', $route->getRequirements());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements AccessCheckInterface::access().
|
||||||
|
*/
|
||||||
|
public function access(Route $route, Request $request) {
|
||||||
|
return user_is_anonymous() && (config('user.settings')->get('register') != USER_REGISTER_ADMINISTRATORS_ONLY);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains Drupal\system\UserBundle.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\user;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User dependency injection container.
|
||||||
|
*/
|
||||||
|
class UserBundle extends Bundle {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides Bundle::build().
|
||||||
|
*/
|
||||||
|
public function build(ContainerBuilder $container) {
|
||||||
|
$container->register('access_check.user.register', 'Drupal\user\Access\RegisterAccessCheck')
|
||||||
|
->addTag('access_check');
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,6 @@
|
||||||
namespace Drupal\user;
|
namespace Drupal\user;
|
||||||
|
|
||||||
use Symfony\Component\DependencyInjection\ContainerAware;
|
use Symfony\Component\DependencyInjection\ContainerAware;
|
||||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns responses for User module routes.
|
* Returns responses for User module routes.
|
||||||
|
@ -22,12 +21,6 @@ class UserRouteController extends ContainerAware {
|
||||||
* A renderable array containing the user registration form.
|
* A renderable array containing the user registration form.
|
||||||
*/
|
*/
|
||||||
public function register() {
|
public function register() {
|
||||||
// @todo Remove once access control is integrated with new routing system:
|
|
||||||
// http://drupal.org/node/1793520.
|
|
||||||
if (!user_register_access()) {
|
|
||||||
throw new AccessDeniedHttpException();
|
|
||||||
}
|
|
||||||
|
|
||||||
$account = entity_create('user', array());
|
$account = entity_create('user', array());
|
||||||
return entity_get_form($account, 'register');
|
return entity_get_form($account, 'register');
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,3 +2,5 @@ user_register:
|
||||||
pattern: '/user/register'
|
pattern: '/user/register'
|
||||||
defaults:
|
defaults:
|
||||||
_content: '\Drupal\user\UserRouteController::register'
|
_content: '\Drupal\user\UserRouteController::register'
|
||||||
|
requirements:
|
||||||
|
_access_user_register: 'TRUE'
|
||||||
|
|
Loading…
Reference in New Issue