Commit d617be8c authored by alexpott's avatar alexpott

Issue #2331079 by znerol, dawehner: Use RouteMatch in access-checks and remove...

Issue #2331079 by znerol, dawehner: Use RouteMatch in access-checks and remove RequestHelper::duplicate().
parent 3295e62a
......@@ -673,11 +673,11 @@ services:
csrf_token:
class: Drupal\Core\Access\CsrfTokenGenerator
arguments: ['@private_key', '@session_manager.metadata_bag']
access_arguments_resolver:
class: Drupal\Core\Access\AccessArgumentsResolver
access_arguments_resolver_factory:
class: Drupal\Core\Access\AccessArgumentsResolverFactory
access_manager:
class: Drupal\Core\Access\AccessManager
arguments: ['@router.route_provider', '@url_generator', '@paramconverter_manager', '@access_arguments_resolver', '@request_stack', '@current_user']
arguments: ['@router.route_provider', '@paramconverter_manager', '@access_arguments_resolver_factory', '@current_user']
calls:
- [setContainer, ['@service_container']]
access_route_subscriber:
......@@ -704,13 +704,13 @@ services:
- { name: access_check, applies_to: _access_theme }
access_check.custom:
class: Drupal\Core\Access\CustomAccessCheck
arguments: ['@controller_resolver', '@access_arguments_resolver']
arguments: ['@controller_resolver', '@access_arguments_resolver_factory']
tags:
- { name: access_check, applies_to: _custom_access }
access_check.csrf:
class: Drupal\Core\Access\CsrfAccessCheck
tags:
- { name: access_check, applies_to: _csrf_token }
- { name: access_check, applies_to: _csrf_token, needs_incoming_request: TRUE }
arguments: ['@csrf_token']
maintenance_mode:
class: Drupal\Core\Site\MaintenanceMode
......
......@@ -2,27 +2,61 @@
/**
* @file
* Contains \Drupal\Core\Access\AccessArgumentsResolver.
* Contains \Drupal\Component\Utility\ArgumentsResolver.
*/
namespace Drupal\Core\Access;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\HttpFoundation\Request;
namespace Drupal\Component\Utility;
/**
* Resolves the arguments to pass to an access check callable.
* Resolves the arguments to pass to a callable.
*/
class AccessArgumentsResolver implements AccessArgumentsResolverInterface {
class ArgumentsResolver implements ArgumentsResolverInterface {
/**
* An associative array of parameter names to scalar candidate values.
*
* @var array
*/
protected $scalars;
/**
* An associative array of parameter names to object candidate values.
*
* @var array
*/
protected $objects;
/**
* An array object candidates tried on every parameter regardless of name.
*
* @var array
*/
protected $wildcards;
/**
* Constructs a new ArgumentsResolver.
*
* @param array $scalars
* An associative array of parameter names to scalar candidate values.
* @param object[] $objects
* An associative array of parameter names to object candidate values.
* @param object[] $wildcards
* An array object candidates tried on every parameter regardless of its
* name.
*/
public function __construct(array $scalars, array $objects, array $wildcards) {
$this->scalars = $scalars;
$this->objects = $objects;
$this->wildcards = $wildcards;
}
/**
* {@inheritdoc}
*/
public function getArguments(callable $callable, Route $route, Request $request, AccountInterface $account) {
public function getArguments(callable $callable) {
$arguments = array();
foreach ($this->getReflector($callable)->getParameters() as $parameter) {
$arguments[] = $this->getArgument($parameter, $route, $request, $account);
$arguments[] = $this->getArgument($parameter);
}
return $arguments;
}
......@@ -32,12 +66,6 @@ public function getArguments(callable $callable, Route $route, Request $request,
*
* @param \ReflectionParameter $parameter
* The parameter of a callable to get the value for.
* @param \Symfony\Component\Routing\Route $route
* The access checked route.
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
* @param \Drupal\Core\Session\AccountInterface $account
* The current user.
*
* @return mixed
* The value of the requested parameter value.
......@@ -45,39 +73,30 @@ public function getArguments(callable $callable, Route $route, Request $request,
* @throws \RuntimeException
* Thrown when there is a missing parameter.
*/
protected function getArgument(\ReflectionParameter $parameter, Route $route, Request $request, AccountInterface $account) {
$upcasted_route_arguments = $request->attributes->all();
$raw_route_arguments = isset($upcasted_route_arguments['_raw_variables']) ? $upcasted_route_arguments['_raw_variables']->all() : $upcasted_route_arguments;
protected function getArgument(\ReflectionParameter $parameter) {
$parameter_type_hint = $parameter->getClass();
$parameter_name = $parameter->getName();
// @todo Remove this once AccessManagerInterface::checkNamedRoute() is fixed
// to not leak _raw_variables from the request being duplicated.
// @see https://drupal.org/node/2265939
$raw_route_arguments += $upcasted_route_arguments;
// If the route argument exists and is NULL, return it, regardless of
// If the argument exists and is NULL, return it, regardless of
// parameter type hint.
if (!isset($upcasted_route_arguments[$parameter_name]) && array_key_exists($parameter_name, $upcasted_route_arguments)) {
if (!isset($this->objects[$parameter_name]) && array_key_exists($parameter_name, $this->objects)) {
return NULL;
}
if ($parameter_type_hint) {
// If the argument exists and complies with the type hint, return it.
if (isset($upcasted_route_arguments[$parameter_name]) && is_object($upcasted_route_arguments[$parameter_name]) && $parameter_type_hint->isInstance($upcasted_route_arguments[$parameter_name])) {
return $upcasted_route_arguments[$parameter_name];
if (isset($this->objects[$parameter_name]) && is_object($this->objects[$parameter_name]) && $parameter_type_hint->isInstance($this->objects[$parameter_name])) {
return $this->objects[$parameter_name];
}
// Otherwise, resolve $request, $route, and $account by type matching
// only. This way, the callable may rename them in case the route
// defines other parameters with these names.
foreach (array($request, $route, $account) as $special_argument) {
if ($parameter_type_hint->isInstance($special_argument)) {
return $special_argument;
// Otherwise, resolve wildcard arguments by type matching.
foreach ($this->wildcards as $wildcard) {
if ($parameter_type_hint->isInstance($wildcard)) {
return $wildcard;
}
}
}
elseif (isset($raw_route_arguments[$parameter_name])) {
return $raw_route_arguments[$parameter_name];
elseif (isset($this->scalars[$parameter_name])) {
return $this->scalars[$parameter_name];
}
// If the callable provides a default value, use it.
......@@ -125,7 +144,7 @@ protected function handleUnresolvedArgument(\ReflectionParameter $parameter) {
else {
$function_name = $function->getName();
}
throw new \RuntimeException(sprintf('Access callable "%s" requires a value for the "$%s" argument.', $function_name, $parameter->getName()));
throw new \RuntimeException(sprintf('Callable "%s" requires a value for the "$%s" argument.', $function_name, $parameter->getName()));
}
}
<?php
/**
* @file
* Contains \Drupal\Component\Utility\ArgumentsResolverInterface.
*/
namespace Drupal\Component\Utility;
/**
* Resolves the arguments to pass to a callable.
*/
interface ArgumentsResolverInterface {
/**
* Returns arguments suitable for passing to the given callable.
*
* @return array
* An array of arguments to pass to the callable.
*
* @throws \RuntimeException
* When a value for an argument given cannot be resolved.
*/
public function getArguments(callable $callable);
}
<?php
/**
* @file
* Contains \Drupal\Core\Access\AccessArgumentsResolverFactory.
*/
namespace Drupal\Core\Access;
use Drupal\Component\Utility\ArgumentsResolver;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* Resolves the arguments to pass to an access check callable.
*/
class AccessArgumentsResolverFactory implements AccessArgumentsResolverFactoryInterface {
/**
* {@inheritdoc}
*/
public function getArgumentsResolver(RouteMatchInterface $route_match, AccountInterface $account, Request $request = NULL) {
$route = $route_match->getRouteObject();
// Defaults for the parameters defined on the route object need to be added
// to the raw arguments.
$raw_route_arguments = $route_match->getRawParameters()->all() + $route->getDefaults();
$upcasted_route_arguments = $route_match->getParameters()->all();
// Parameters which are not defined on the route object, but still are
// essential for access checking are passed as wildcards to the argument
// resolver. An access-check method with a parameter of type Route,
// RouteMatchInterface, AccountInterface or Request will receive those
// arguments regardless of the parameter name.
$wildcard_arguments = [$route, $route_match, $account];
if (isset($request)) {
$wildcard_arguments[] = $request;
}
return new ArgumentsResolver($raw_route_arguments, $upcasted_route_arguments, $wildcard_arguments);
}
}
<?php
/**
* @file
* Contains \Drupal\Core\Access\AccessArgumentsResolverFactoryInterface.
*/
namespace Drupal\Core\Access;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* Constructs the arguments resolver instance to use when running access checks.
*/
interface AccessArgumentsResolverFactoryInterface {
/**
* Returns the arguments resolver to use when running access checks.
*
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match object to be checked.
* @param \Drupal\Core\Session\AccountInterface $account
* The account being checked.
* @param \Symfony\Component\HttpFoundation\Request $request
* Optional, the request object.
*
* @return \Drupal\Component\Utility\ArgumentsResolverInterface
* The parametrized arguments resolver instance.
*/
public function getArgumentsResolver(RouteMatchInterface $route_match, AccountInterface $account, Request $request = NULL);
}
<?php
/**
* @file
* Contains \Drupal\Core\Access\AccessArgumentsResolverInterface.
*/
namespace Drupal\Core\Access;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\HttpFoundation\Request;
/**
* Resolves the arguments to pass to an access check callable.
*/
interface AccessArgumentsResolverInterface {
/**
* Returns the arguments to pass to the access check callable.
*
* @param callable $callable
* A PHP callable.
* @param \Symfony\Component\Routing\Route $route
* The route to check access to.
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object.
* @param \Drupal\Core\Session\AccountInterface $account
* The currently logged in account.
*
* @return array
* An array of arguments to pass to the callable.
*
* @throws \RuntimeException
* When a value for an argument given is not provided.
*/
public function getArguments(callable $callable, Route $route, Request $request, AccountInterface $account);
}
......@@ -10,17 +10,16 @@
use Drupal\Core\ParamConverter\ParamConverterManagerInterface;
use Drupal\Core\ParamConverter\ParamNotConvertedException;
use Drupal\Core\Routing\Access\AccessInterface;
use Drupal\Core\Routing\RequestHelper;
use Drupal\Core\Routing\RouteMatch;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Routing\RouteProviderInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Drupal\Component\Utility\ArgumentsResolverInterface;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Routing\Exception\RouteNotFoundException;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
......@@ -54,6 +53,11 @@ class AccessManager implements ContainerAwareInterface, AccessManagerInterface {
*/
protected $checkMethods = array();
/**
* Array of access checks which only will be run on the incoming request.
*/
protected $checkNeedsRequest = array();
/**
* An array to map static requirement keys to service IDs.
*
......@@ -75,13 +79,6 @@ class AccessManager implements ContainerAwareInterface, AccessManagerInterface {
*/
protected $routeProvider;
/**
* The url generator.
*
* @var \Symfony\Component\Routing\Generator\UrlGeneratorInterface
*/
protected $urlGenerator;
/**
* The paramconverter manager.
*
......@@ -92,16 +89,9 @@ class AccessManager implements ContainerAwareInterface, AccessManagerInterface {
/**
* The access arguments resolver.
*
* @var \Drupal\Core\Access\AccessArgumentsResolverInterface
*/
protected $argumentsResolver;
/**
* A request stack object.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
* @var \Drupal\Core\Access\AccessArgumentsResolverFactoryInterface
*/
protected $requestStack;
protected $argumentsResolverFactory;
/**
* The current user.
......@@ -115,32 +105,29 @@ class AccessManager implements ContainerAwareInterface, AccessManagerInterface {
*
* @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
* The route provider.
* @param \Symfony\Component\Routing\Generator\UrlGeneratorInterface $url_generator
* The url generator.
* @param \Drupal\Core\ParamConverter\ParamConverterManagerInterface $paramconverter_manager
* The param converter manager.
* @param \Drupal\Core\Access\AccessArgumentsResolverInterface $arguments_resolver
* @param \Drupal\Core\Access\AccessArgumentsResolverFactoryInterface $arguments_resolver_factory
* The access arguments resolver.
* @param \Symfony\Component\HttpFoundation\RequestStack $requestStack
* The request stack object.
* @param \Drupal\Core\Session\AccountInterface $current_user
* The current user.
*/
public function __construct(RouteProviderInterface $route_provider, UrlGeneratorInterface $url_generator, ParamConverterManagerInterface $paramconverter_manager, AccessArgumentsResolverInterface $arguments_resolver, RequestStack $requestStack, AccountInterface $current_user) {
public function __construct(RouteProviderInterface $route_provider, ParamConverterManagerInterface $paramconverter_manager, AccessArgumentsResolverFactoryInterface $arguments_resolver_factory, AccountInterface $current_user) {
$this->routeProvider = $route_provider;
$this->urlGenerator = $url_generator;
$this->paramConverterManager = $paramconverter_manager;
$this->argumentsResolver = $arguments_resolver;
$this->requestStack = $requestStack;
$this->argumentsResolverFactory = $arguments_resolver_factory;
$this->currentUser = $current_user;
}
/**
* {@inheritdoc}
*/
public function addCheckService($service_id, $service_method, array $applies_checks = array()) {
public function addCheckService($service_id, $service_method, array $applies_checks = array(), $needs_incoming_request = FALSE) {
$this->checkIds[] = $service_id;
$this->checkMethods[$service_id] = $service_method;
if ($needs_incoming_request) {
$this->checkNeedsRequest[$service_id] = $service_id;
}
foreach ($applies_checks as $applies_check) {
$this->staticRequirementMap[$applies_check][] = $service_id;
}
......@@ -193,22 +180,17 @@ protected function applies(Route $route) {
/**
* {@inheritdoc}
*/
public function checkNamedRoute($route_name, array $parameters = array(), AccountInterface $account = NULL, Request $route_request = NULL, $return_as_object = FALSE) {
public function checkNamedRoute($route_name, array $parameters = array(), AccountInterface $account = NULL, $return_as_object = FALSE) {
try {
$route = $this->routeProvider->getRouteByName($route_name, $parameters);
if (empty($route_request)) {
// Create a cloned request with fresh attributes.
$route_request = RequestHelper::duplicate($this->requestStack->getCurrentRequest(), $this->urlGenerator->generate($route_name, $parameters));
$route_request->attributes->replace(array());
// Populate $route_request->attributes with both raw and converted
// parameters.
$parameters += $route->getDefaults();
$route_request->attributes->set('_raw_variables', new ParameterBag($parameters));
$parameters[RouteObjectInterface::ROUTE_OBJECT] = $route;
$route_request->attributes->add($this->paramConverterManager->convert($parameters));
}
return $this->check($route, $route_request, $account, $return_as_object);
// ParamConverterManager relies on the route object being available
// from the parameters array.
$parameters[RouteObjectInterface::ROUTE_OBJECT] = $route;
$upcasted_parameters = $this->paramConverterManager->convert($parameters + $route->getDefaults());
$route_match = new RouteMatch($route_name, $route, $upcasted_parameters, $parameters);
return $this->check($route_match, $account, NULL, $return_as_object);
}
catch (RouteNotFoundException $e) {
// Cacheable until extensions change.
......@@ -226,18 +208,36 @@ public function checkNamedRoute($route_name, array $parameters = array(), Accoun
/**
* {@inheritdoc}
*/
public function check(Route $route, Request $request, AccountInterface $account = NULL, $return_as_object = FALSE) {
public function checkRequest(Request $request, AccountInterface $account = NULL, $return_as_object = FALSE) {
$route_match = RouteMatch::createFromRequest($request);
return $this->check($route_match, $account, $request, $return_as_object);
}
/**
* {@inheritdoc}
*/
public function check(RouteMatchInterface $route_match, AccountInterface $account = NULL, Request $request = NULL, $return_as_object = FALSE) {
if (!isset($account)) {
$account = $this->currentUser;
}
$route = $route_match->getRouteObject();
$checks = $route->getOption('_access_checks') ?: array();
$conjunction = $route->getOption('_access_mode') ?: static::ACCESS_MODE_ALL;
if ($conjunction == static::ACCESS_MODE_ALL) {
$result = $this->checkAll($checks, $route, $request, $account);
// Filter out checks which require the incoming request.
if (!isset($request)) {
$checks = array_diff($checks, $this->checkNeedsRequest);
}
else {
$result = $this->checkAny($checks, $route, $request, $account);
$result = AccessResult::create();
if (!empty($checks)) {
$arguments_resolver = $this->argumentsResolverFactory->getArgumentsResolver($route_match, $account, $request);
if ($conjunction == static::ACCESS_MODE_ALL) {
$result = $this->checkAll($checks, $arguments_resolver);
}
else {
$result = $this->checkAny($checks, $arguments_resolver);
}
}
return $return_as_object ? $result : $result->isAllowed();
}
......@@ -247,26 +247,23 @@ public function check(Route $route, Request $request, AccountInterface $account
*
* @param array $checks
* Contains the list of checks on the route definition.
* @param \Symfony\Component\Routing\Route $route
* The route to check access to.
* @param \Symfony\Component\HttpFoundation\Request $request
* The incoming request object.
* @param \Drupal\Core\Session\AccountInterface $account
* The current user.
* @param \Drupal\Component\Utility\ArgumentsResolverInterface $arguments_resolver
* The parametrized arguments resolver instance.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result.
*
* @see \Drupal\Core\Access\AccessResultInterface::andIf()
*/
protected function checkAll(array $checks, Route $route, Request $request, AccountInterface $account) {
protected function checkAll(array $checks, ArgumentsResolverInterface $arguments_resolver) {
$results = array();
foreach ($checks as $service_id) {
if (empty($this->checks[$service_id])) {
$this->loadCheck($service_id);
}
$result = $this->performCheck($service_id, $route, $request, $account);
$result = $this->performCheck($service_id, $arguments_resolver);
$results[] = $result;
// Stop as soon as the first non-allowed check is encountered.
......@@ -294,19 +291,15 @@ protected function checkAll(array $checks, Route $route, Request $request, Accou
*
* @param array $checks
* Contains the list of checks on the route definition.
* @param \Symfony\Component\Routing\Route $route
* The route to check access to.
* @param \Symfony\Component\HttpFoundation\Request $request
* The incoming request object.
* @param \Drupal\Core\Session\AccountInterface $account
* The current user.
* @param \Drupal\Component\Utility\ArgumentsResolverInterface $arguments_resolver
* The parametrized arguments resolver instance.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result.
*
* @see \Drupal\Core\Access\AccessResultInterface::orIf()
*/
protected function checkAny(array $checks, $route, $request, AccountInterface $account) {
protected function checkAny(array $checks, ArgumentsResolverInterface $arguments_resolver) {
// No opinion by default.
$result = AccessResult::create();
......@@ -314,7 +307,7 @@ protected function checkAny(array $checks, $route, $request, AccountInterface $a
if (empty($this->checks[$service_id])) {
$this->loadCheck($service_id);
}
$result = $result->orIf($this->performCheck($service_id, $route, $request, $account));
$result = $result->orIf($this->performCheck($service_id, $arguments_resolver));
}
return $result;
......@@ -325,12 +318,8 @@ protected function checkAny(array $checks, $route, $request, AccountInterface $a
*
* @param string $service_id
* The access check service ID to use.
* @param \Symfony\Component\Routing\Route $route
* The route to check access to.
* @param \Symfony\Component\HttpFoundation\Request $request
* The incoming request object.
* @param \Drupal\Core\Session\AccountInterface $account
* The current user.
* @param \Drupal\Component\Utility\ArgumentsResolverInterface $arguments_resolver
* The parametrized arguments resolver instance.
*
* @throws \Drupal\Core\Access\AccessException
* Thrown when the access check returns an invalid value.
......@@ -338,9 +327,9 @@ protected function checkAny(array $checks, $route, $request, AccountInterface $a
* @return \Drupal\Core\Access\AccessResultInterface
* The access result.
*/
protected function performCheck($service_id, $route, $request, $account) {
protected function performCheck($service_id, ArgumentsResolverInterface $arguments_resolver) {
$callable = array($this->checks[$service_id], $this->checkMethods[$service_id]);
$arguments = $this->argumentsResolver->getArguments($callable, $route, $request, $account);
$arguments = $arguments_resolver->getArguments($callable);
/** @var \Drupal\Core\Access\AccessResultInterface $service_access **/
$service_access = call_user_func_array($callable, $arguments);
......
......@@ -7,10 +7,10 @@
namespace Drupal\Core\Access;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\RouteCollection;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Routing\RouteMatchInterface;
/**
* Provides an interface for attaching and running access check services.
......@@ -44,9 +44,6 @@ interface AccessManagerInterface {
* @param \Drupal\Core\Session\AccountInterface $account
* (optional) Run access checks for this account. Defaults to the current
* user.
* @param \Symfony\Component\HttpFoundation\Request $route_request
* Optional incoming request object. If not provided, one will be built
* using the route information and the current request from the container.
* @param bool $return_as_object
* (optional) Defaults to FALSE.
*
......@@ -57,7 +54,27 @@ interface AccessManagerInterface {
* returned, i.e. TRUE means access is explicitly allowed, FALSE means
* access is either explicitly forbidden or "no opinion".
*/
public function checkNamedRoute($route_name, array $parameters = array(), AccountInterface $account = NULL, Request $route_request = NULL, $return_as_object = FALSE);
public function checkNamedRoute($route_name, array $parameters = array(), AccountInterface $account = NULL, $return_as_object = FALSE);
/**
* Execute access checks against the incoming request.
*
* @param Request $request
* The incoming request.
* @param \Drupal\Core\Session\AccountInterface $account
* (optional) Run access checks for this account. Defaults to the current
* user.
* @param bool $return_as_object
* (optional) Defaults to FALSE.
*
* @return bool|\Drupal\Core\Access\AccessResultInterface
* The access result. Returns a boolean if $return_as_object is FALSE (this
* is the default) and otherwise an AccessResultInterface object.
* When a boolean is returned, the result of AccessInterface::isAllowed() is
* returned, i.e. TRUE means access is explicitly allowed, FALSE means
* access is either explicitly forbidden or "no opinion".
*/
public function checkRequest(Request $request, AccountInterface $account = NULL, $return_as_object = FALSE);
/**
* For each route, saves a list of applicable access checks to the route.
......@@ -77,21 +94,24 @@ public function setChecks(RouteCollection $routes);
* @param array $applies_checks
* (optional) An array of route requirement keys the checker service applies
* to.
* @param bool $needs_incoming_request
* (optional) True if access-check method only acts on an incoming request.
*/
public function addCheckService($service_id, $service_method, array $applies_checks = array());
public function addCheckService($service_id, $service_method, array $applies_checks = array(), $needs_incoming_request = FALSE);
/**
* Checks a route against applicable access check services.
*
* Determines whether the route is accessible or not.