AccessAwareRouter.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. <?php
  2. namespace Drupal\Core\Routing;
  3. use Drupal\Core\Access\AccessManagerInterface;
  4. use Drupal\Core\Access\AccessResultReasonInterface;
  5. use Drupal\Core\Cache\CacheableDependencyInterface;
  6. use Drupal\Core\Http\Exception\CacheableAccessDeniedHttpException;
  7. use Drupal\Core\Session\AccountInterface;
  8. use Symfony\Component\HttpFoundation\Request;
  9. use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
  10. use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
  11. use Symfony\Component\Routing\RequestContext as SymfonyRequestContext;
  12. use Symfony\Component\Routing\RequestContextAwareInterface;
  13. use Symfony\Component\Routing\RouterInterface;
  14. /**
  15. * A router class for Drupal with access check and upcasting.
  16. */
  17. class AccessAwareRouter implements AccessAwareRouterInterface {
  18. /**
  19. * The router doing the actual routing.
  20. *
  21. * @var \Symfony\Component\Routing\Matcher\RequestMatcherInterface
  22. */
  23. protected $router;
  24. /**
  25. * The access manager.
  26. *
  27. * @var \Drupal\Core\Access\AccessManagerInterface
  28. */
  29. protected $accessManager;
  30. /**
  31. * The account to use in access checks.
  32. *
  33. * @var \Drupal\Core\Session\AccountInterface
  34. */
  35. protected $account;
  36. /**
  37. * Constructs a router for Drupal with access check and upcasting.
  38. *
  39. * @param \Symfony\Component\Routing\Matcher\RequestMatcherInterface $router
  40. * The router doing the actual routing.
  41. * @param \Drupal\Core\Access\AccessManagerInterface $access_manager
  42. * The access manager.
  43. * @param \Drupal\Core\Session\AccountInterface $account
  44. * The account to use in access checks.
  45. */
  46. public function __construct(RequestMatcherInterface $router, AccessManagerInterface $access_manager, AccountInterface $account) {
  47. $this->router = $router;
  48. $this->accessManager = $access_manager;
  49. $this->account = $account;
  50. }
  51. /**
  52. * {@inheritdoc}
  53. */
  54. public function __call($name, $arguments) {
  55. // Ensure to call every other function to the router.
  56. return call_user_func_array([$this->router, $name], $arguments);
  57. }
  58. /**
  59. * {@inheritdoc}
  60. */
  61. public function setContext(SymfonyRequestContext $context) {
  62. if ($this->router instanceof RequestContextAwareInterface) {
  63. $this->router->setContext($context);
  64. }
  65. }
  66. /**
  67. * {@inheritdoc}
  68. */
  69. public function getContext() {
  70. if ($this->router instanceof RequestContextAwareInterface) {
  71. return $this->router->getContext();
  72. }
  73. }
  74. /**
  75. * {@inheritdoc}
  76. *
  77. * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
  78. * Thrown when access checking failed.
  79. */
  80. public function matchRequest(Request $request) {
  81. $parameters = $this->router->matchRequest($request);
  82. $request->attributes->add($parameters);
  83. $this->checkAccess($request);
  84. // We can not return $parameters because the access check can change the
  85. // request attributes.
  86. return $request->attributes->all();
  87. }
  88. /**
  89. * Apply access check service to the route and parameters in the request.
  90. *
  91. * @param \Symfony\Component\HttpFoundation\Request $request
  92. * The request to access check.
  93. */
  94. protected function checkAccess(Request $request) {
  95. // The cacheability (if any) of this request's access check result must be
  96. // applied to the response.
  97. $access_result = $this->accessManager->checkRequest($request, $this->account, TRUE);
  98. // Allow a master request to set the access result for a subrequest: if an
  99. // access result attribute is already set, don't overwrite it.
  100. if (!$request->attributes->has(AccessAwareRouterInterface::ACCESS_RESULT)) {
  101. $request->attributes->set(AccessAwareRouterInterface::ACCESS_RESULT, $access_result);
  102. }
  103. if (!$access_result->isAllowed()) {
  104. if ($access_result instanceof CacheableDependencyInterface && $request->isMethodCacheable()) {
  105. throw new CacheableAccessDeniedHttpException($access_result, $access_result instanceof AccessResultReasonInterface ? $access_result->getReason() : NULL);
  106. }
  107. else {
  108. throw new AccessDeniedHttpException($access_result instanceof AccessResultReasonInterface ? $access_result->getReason() : NULL);
  109. }
  110. }
  111. }
  112. /**
  113. * {@inheritdoc}
  114. */
  115. public function getRouteCollection() {
  116. if ($this->router instanceof RouterInterface) {
  117. return $this->router->getRouteCollection();
  118. }
  119. }
  120. /**
  121. * {@inheritdoc}
  122. */
  123. public function generate($name, $parameters = [], $referenceType = self::ABSOLUTE_PATH) {
  124. if ($this->router instanceof UrlGeneratorInterface) {
  125. return $this->router->generate($name, $parameters, $referenceType);
  126. }
  127. }
  128. /**
  129. * {@inheritdoc}
  130. *
  131. * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
  132. * Thrown when access checking failed.
  133. */
  134. public function match($pathinfo) {
  135. return $this->matchRequest(Request::create($pathinfo));
  136. }
  137. }