3 namespace Drupal\Core\Access;
5 use Drupal\Core\ParamConverter\ParamConverterManagerInterface;
6 use Drupal\Core\ParamConverter\ParamNotConvertedException;
7 use Drupal\Core\Routing\RouteMatch;
8 use Drupal\Core\Routing\RouteMatchInterface;
9 use Drupal\Core\Routing\RouteProviderInterface;
10 use Drupal\Core\Session\AccountInterface;
11 use Drupal\Component\Utility\ArgumentsResolverInterface;
12 use Symfony\Component\HttpFoundation\Request;
13 use Symfony\Component\Routing\Exception\RouteNotFoundException;
14 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
17 * Attaches access check services to routes and runs them on request.
19 * @see \Drupal\Tests\Core\Access\AccessManagerTest
21 class AccessManager implements AccessManagerInterface {
25 * @var \Drupal\Core\Routing\RouteProviderInterface
27 protected $routeProvider;
30 * The paramconverter manager.
32 * @var \Drupal\Core\ParamConverter\ParamConverterManagerInterface
34 protected $paramConverterManager;
37 * The access arguments resolver.
39 * @var \Drupal\Core\Access\AccessArgumentsResolverFactoryInterface
41 protected $argumentsResolverFactory;
46 * @var \Drupal\Core\Session\AccountInterface
48 protected $currentUser;
53 * @var \Drupal\Core\Access\CheckProviderInterface
55 protected $checkProvider;
58 * Constructs a AccessManager instance.
60 * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
62 * @param \Drupal\Core\ParamConverter\ParamConverterManagerInterface $paramconverter_manager
63 * The param converter manager.
64 * @param \Drupal\Core\Access\AccessArgumentsResolverFactoryInterface $arguments_resolver_factory
65 * The access arguments resolver.
66 * @param \Drupal\Core\Session\AccountInterface $current_user
68 * @param CheckProviderInterface $check_provider
70 public function __construct(RouteProviderInterface $route_provider, ParamConverterManagerInterface $paramconverter_manager, AccessArgumentsResolverFactoryInterface $arguments_resolver_factory, AccountInterface $current_user, CheckProviderInterface $check_provider) {
71 $this->routeProvider = $route_provider;
72 $this->paramConverterManager = $paramconverter_manager;
73 $this->argumentsResolverFactory = $arguments_resolver_factory;
74 $this->currentUser = $current_user;
75 $this->checkProvider = $check_provider;
81 public function checkNamedRoute($route_name, array $parameters = [], AccountInterface $account = NULL, $return_as_object = FALSE) {
83 $route = $this->routeProvider->getRouteByName($route_name, $parameters);
85 // ParamConverterManager relies on the route name and object being
86 // available from the parameters array.
87 $parameters[RouteObjectInterface::ROUTE_NAME] = $route_name;
88 $parameters[RouteObjectInterface::ROUTE_OBJECT] = $route;
89 $upcasted_parameters = $this->paramConverterManager->convert($parameters + $route->getDefaults());
91 $route_match = new RouteMatch($route_name, $route, $upcasted_parameters, $parameters);
92 return $this->check($route_match, $account, NULL, $return_as_object);
94 catch (RouteNotFoundException $e) {
95 // Cacheable until extensions change.
96 $result = AccessResult::forbidden()->addCacheTags(['config:core.extension']);
97 return $return_as_object ? $result : $result->isAllowed();
99 catch (ParamNotConvertedException $e) {
100 // Uncacheable because conversion of the parameter may not have been
101 // possible due to dynamic circumstances.
102 $result = AccessResult::forbidden()->setCacheMaxAge(0);
103 return $return_as_object ? $result : $result->isAllowed();
110 public function checkRequest(Request $request, AccountInterface $account = NULL, $return_as_object = FALSE) {
111 $route_match = RouteMatch::createFromRequest($request);
112 return $this->check($route_match, $account, $request, $return_as_object);
118 public function check(RouteMatchInterface $route_match, AccountInterface $account = NULL, Request $request = NULL, $return_as_object = FALSE) {
119 if (!isset($account)) {
120 $account = $this->currentUser;
122 $route = $route_match->getRouteObject();
123 $checks = $route->getOption('_access_checks') ?: [];
125 // Filter out checks which require the incoming request.
126 if (!isset($request)) {
127 $checks = array_diff($checks, $this->checkProvider->getChecksNeedRequest());
130 $result = AccessResult::neutral();
131 if (!empty($checks)) {
132 $arguments_resolver = $this->argumentsResolverFactory->getArgumentsResolver($route_match, $account, $request);
133 $result = AccessResult::allowed();
134 foreach ($checks as $service_id) {
135 $result = $result->andIf($this->performCheck($service_id, $arguments_resolver));
138 return $return_as_object ? $result : $result->isAllowed();
142 * Performs the specified access check.
144 * @param string $service_id
145 * The access check service ID to use.
146 * @param \Drupal\Component\Utility\ArgumentsResolverInterface $arguments_resolver
147 * The parametrized arguments resolver instance.
149 * @return \Drupal\Core\Access\AccessResultInterface
152 * @throws \Drupal\Core\Access\AccessException
153 * Thrown when the access check returns an invalid value.
155 protected function performCheck($service_id, ArgumentsResolverInterface $arguments_resolver) {
156 $callable = $this->checkProvider->loadCheck($service_id);
157 $arguments = $arguments_resolver->getArguments($callable);
158 /** @var \Drupal\Core\Access\AccessResultInterface $service_access **/
159 $service_access = call_user_func_array($callable, $arguments);
161 if (!$service_access instanceof AccessResultInterface) {
162 throw new AccessException("Access error in $service_id. Access services must return an object that implements AccessResultInterface.");
165 return $service_access;