Loading core/core.services.yml +4 −0 Original line number Diff line number Diff line Loading @@ -1175,6 +1175,10 @@ services: class: Drupal\Core\Entity\EntityAccessCheck tags: - { name: access_check, applies_to: _entity_access } access_check.entity_bundles: class: Drupal\Core\Entity\EntityBundleAccessCheck tags: - { name: access_check, applies_to: _entity_bundles } access_check.entity_create: class: Drupal\Core\Entity\EntityCreateAccessCheck arguments: ['@entity_type.manager'] Loading core/lib/Drupal/Core/Entity/EntityBundleAccessCheck.php 0 → 100644 +51 −0 Original line number Diff line number Diff line <?php namespace Drupal\Core\Entity; use Drupal\Core\Access\AccessResult; use Drupal\Core\Routing\Access\AccessInterface; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Session\AccountInterface; use Symfony\Component\Routing\Route; /** * Provides an entity bundle checker for the _entity_bundles route requirement. */ class EntityBundleAccessCheck implements AccessInterface { /** * Checks entity bundle match based on the _entity_bundles route requirement. * * @code * example.route: * path: foo/{example_entity_type}/{other_parameter} * requirements: * _entity_bundles: 'example_entity_type:example_bundle|other_example_bundle' * @endcode * * @param \Symfony\Component\Routing\Route $route * The route to check against. * @param \Drupal\Core\Routing\RouteMatchInterface $route_match * The parametrized route. * @param \Drupal\Core\Session\AccountInterface $account * The currently logged in account. * * @return \Drupal\Core\Access\AccessResultInterface * The access result. */ public function access(Route $route, RouteMatchInterface $route_match, AccountInterface $account) { if ($route->hasRequirement('_entity_bundles')) { list($entity_type, $bundle_definition) = explode(':', $route->getRequirement('_entity_bundles')); $bundles = explode('|', $bundle_definition); $parameters = $route_match->getParameters(); if ($parameters->has($entity_type)) { $entity = $parameters->get($entity_type); if ($entity instanceof EntityInterface && in_array($entity->bundle(), $bundles, TRUE)) { return AccessResult::allowed()->addCacheableDependency($entity); } } } return AccessResult::neutral('The entity bundle does not match the route _entity_bundles requirement.'); } } core/tests/Drupal/Tests/Core/Entity/EntityBundleAccessCheckTest.php 0 → 100644 +97 −0 Original line number Diff line number Diff line <?php namespace Drupal\Tests\Core\Entity; use Drupal\Core\Cache\Context\CacheContextsManager; use Drupal\Core\DependencyInjection\Container; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Session\AccountInterface; use Drupal\node\NodeInterface; use Symfony\Component\HttpFoundation\ParameterBag; use Symfony\Component\Routing\Route; use Drupal\Core\Access\AccessResult; use Drupal\Core\Entity\EntityBundleAccessCheck; use Drupal\Tests\UnitTestCase; /** * Unit test of entity access checking system. * * @coversDefaultClass \Drupal\Core\Entity\EntityBundleAccessCheck * * @group Access * @group Entity */ class EntityBundleAccessCheckTest extends UnitTestCase { /** * {@inheritdoc} */ protected function setUp() { $cache_contexts_manager = $this->prophesize(CacheContextsManager::class)->reveal(); $container = new Container(); $container->set('cache_contexts_manager', $cache_contexts_manager); \Drupal::setContainer($container); } /** * Data provider. */ public function getBundleAndAccessResult() { return [ [ 'article', 'node:article', AccessResult::allowed(), ], [ 'page', 'node:article', AccessResult::neutral('The entity bundle does not match the route _entity_bundles requirement.'), ], [ 'page', 'node:article|page', AccessResult::allowed(), ], [ 'article', 'node:article|page', AccessResult::allowed(), ], [ 'book_page', 'node:article|page', AccessResult::neutral('The entity bundle does not match the route _entity_bundles requirement.'), ], ]; } /** * @covers ::access * * @dataProvider getBundleAndAccessResult */ public function testRouteAccess($bundle, $access_requirement, $access_result) { $route = new Route('/foo/{node}', [], ['_entity_bundles' => $access_requirement], ['parameters' => ['node' => ['type' => 'entity:node']]]); /** @var \Drupal\Core\Session\AccountInterface $account */ $account = $this->prophesize(AccountInterface::class)->reveal(); /** @var \Drupal\node\NodeInterface|\Prophecy\Prophecy\ObjectProphecy $node */ $node = $this->prophesize(NodeInterface::class); $node->bundle()->willReturn($bundle); $node->getCacheContexts()->willReturn([]); $node->getCacheTags()->willReturn([]); $node->getCacheMaxAge()->willReturn(-1); $node = $node->reveal(); /** @var \Drupal\Core\Routing\RouteMatchInterface|\Prophecy\Prophecy\ObjectProphecy $route_match */ $route_match = $this->prophesize(RouteMatchInterface::class); $route_match->getRawParameters()->willReturn(new ParameterBag(['node' => 1])); $route_match->getParameters()->willReturn(new ParameterBag(['node' => $node])); $route_match = $route_match->reveal(); $access_check = new EntityBundleAccessCheck(); $this->assertEquals($access_result, $access_check->access($route, $route_match, $account)); } } Loading
core/core.services.yml +4 −0 Original line number Diff line number Diff line Loading @@ -1175,6 +1175,10 @@ services: class: Drupal\Core\Entity\EntityAccessCheck tags: - { name: access_check, applies_to: _entity_access } access_check.entity_bundles: class: Drupal\Core\Entity\EntityBundleAccessCheck tags: - { name: access_check, applies_to: _entity_bundles } access_check.entity_create: class: Drupal\Core\Entity\EntityCreateAccessCheck arguments: ['@entity_type.manager'] Loading
core/lib/Drupal/Core/Entity/EntityBundleAccessCheck.php 0 → 100644 +51 −0 Original line number Diff line number Diff line <?php namespace Drupal\Core\Entity; use Drupal\Core\Access\AccessResult; use Drupal\Core\Routing\Access\AccessInterface; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Session\AccountInterface; use Symfony\Component\Routing\Route; /** * Provides an entity bundle checker for the _entity_bundles route requirement. */ class EntityBundleAccessCheck implements AccessInterface { /** * Checks entity bundle match based on the _entity_bundles route requirement. * * @code * example.route: * path: foo/{example_entity_type}/{other_parameter} * requirements: * _entity_bundles: 'example_entity_type:example_bundle|other_example_bundle' * @endcode * * @param \Symfony\Component\Routing\Route $route * The route to check against. * @param \Drupal\Core\Routing\RouteMatchInterface $route_match * The parametrized route. * @param \Drupal\Core\Session\AccountInterface $account * The currently logged in account. * * @return \Drupal\Core\Access\AccessResultInterface * The access result. */ public function access(Route $route, RouteMatchInterface $route_match, AccountInterface $account) { if ($route->hasRequirement('_entity_bundles')) { list($entity_type, $bundle_definition) = explode(':', $route->getRequirement('_entity_bundles')); $bundles = explode('|', $bundle_definition); $parameters = $route_match->getParameters(); if ($parameters->has($entity_type)) { $entity = $parameters->get($entity_type); if ($entity instanceof EntityInterface && in_array($entity->bundle(), $bundles, TRUE)) { return AccessResult::allowed()->addCacheableDependency($entity); } } } return AccessResult::neutral('The entity bundle does not match the route _entity_bundles requirement.'); } }
core/tests/Drupal/Tests/Core/Entity/EntityBundleAccessCheckTest.php 0 → 100644 +97 −0 Original line number Diff line number Diff line <?php namespace Drupal\Tests\Core\Entity; use Drupal\Core\Cache\Context\CacheContextsManager; use Drupal\Core\DependencyInjection\Container; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Session\AccountInterface; use Drupal\node\NodeInterface; use Symfony\Component\HttpFoundation\ParameterBag; use Symfony\Component\Routing\Route; use Drupal\Core\Access\AccessResult; use Drupal\Core\Entity\EntityBundleAccessCheck; use Drupal\Tests\UnitTestCase; /** * Unit test of entity access checking system. * * @coversDefaultClass \Drupal\Core\Entity\EntityBundleAccessCheck * * @group Access * @group Entity */ class EntityBundleAccessCheckTest extends UnitTestCase { /** * {@inheritdoc} */ protected function setUp() { $cache_contexts_manager = $this->prophesize(CacheContextsManager::class)->reveal(); $container = new Container(); $container->set('cache_contexts_manager', $cache_contexts_manager); \Drupal::setContainer($container); } /** * Data provider. */ public function getBundleAndAccessResult() { return [ [ 'article', 'node:article', AccessResult::allowed(), ], [ 'page', 'node:article', AccessResult::neutral('The entity bundle does not match the route _entity_bundles requirement.'), ], [ 'page', 'node:article|page', AccessResult::allowed(), ], [ 'article', 'node:article|page', AccessResult::allowed(), ], [ 'book_page', 'node:article|page', AccessResult::neutral('The entity bundle does not match the route _entity_bundles requirement.'), ], ]; } /** * @covers ::access * * @dataProvider getBundleAndAccessResult */ public function testRouteAccess($bundle, $access_requirement, $access_result) { $route = new Route('/foo/{node}', [], ['_entity_bundles' => $access_requirement], ['parameters' => ['node' => ['type' => 'entity:node']]]); /** @var \Drupal\Core\Session\AccountInterface $account */ $account = $this->prophesize(AccountInterface::class)->reveal(); /** @var \Drupal\node\NodeInterface|\Prophecy\Prophecy\ObjectProphecy $node */ $node = $this->prophesize(NodeInterface::class); $node->bundle()->willReturn($bundle); $node->getCacheContexts()->willReturn([]); $node->getCacheTags()->willReturn([]); $node->getCacheMaxAge()->willReturn(-1); $node = $node->reveal(); /** @var \Drupal\Core\Routing\RouteMatchInterface|\Prophecy\Prophecy\ObjectProphecy $route_match */ $route_match = $this->prophesize(RouteMatchInterface::class); $route_match->getRawParameters()->willReturn(new ParameterBag(['node' => 1])); $route_match->getParameters()->willReturn(new ParameterBag(['node' => $node])); $route_match = $route_match->reveal(); $access_check = new EntityBundleAccessCheck(); $this->assertEquals($access_result, $access_check->access($route, $route_match, $account)); } }