3 namespace Drupal\KernelTests\Core\Entity;
5 use Drupal\Core\Language\LanguageInterface;
6 use Drupal\Core\Session\AccountInterface;
7 use Drupal\Core\Access\AccessibleInterface;
8 use Drupal\Core\Entity\EntityAccessControlHandler;
9 use Drupal\Core\Session\AnonymousUserSession;
10 use Drupal\entity_test\Entity\EntityTest;
11 use Drupal\entity_test\Entity\EntityTestDefaultAccess;
12 use Drupal\entity_test\Entity\EntityTestNoUuid;
13 use Drupal\entity_test\Entity\EntityTestLabel;
14 use Drupal\entity_test\Entity\EntityTestRev;
15 use Drupal\language\Entity\ConfigurableLanguage;
16 use Drupal\user\Entity\User;
19 * Tests the entity access control handler.
23 class EntityAccessControlHandlerTest extends EntityLanguageTestBase {
28 public function setUp() {
31 $this->installEntitySchema('entity_test_no_uuid');
32 $this->installEntitySchema('entity_test_rev');
36 * Asserts entity access correctly grants or denies access.
38 public function assertEntityAccess($ops, AccessibleInterface $object, AccountInterface $account = NULL) {
39 foreach ($ops as $op => $result) {
40 $message = format_string("Entity access returns @result with operation '@op'.", [
41 '@result' => !isset($result) ? 'null' : ($result ? 'true' : 'false'),
45 $this->assertEqual($result, $object->access($op, $account), $message);
50 * Ensures user labels are accessible for everyone.
52 public function testUserLabelAccess() {
53 // Set up a non-admin user.
54 \Drupal::currentUser()->setAccount($this->createUser(['uid' => 2]));
56 $anonymous_user = User::getAnonymousUser();
57 $user = $this->createUser();
59 // The current user is allowed to view the anonymous user label.
60 $this->assertEntityAccess([
68 // The current user is allowed to view user labels.
69 $this->assertEntityAccess([
77 // Switch to a anonymous user account.
78 $account_switcher = \Drupal::service('account_switcher');
79 $account_switcher->switchTo(new AnonymousUserSession());
81 // The anonymous user is allowed to view the anonymous user label.
82 $this->assertEntityAccess([
90 // The anonymous user is allowed to view user labels.
91 $this->assertEntityAccess([
99 // Restore user account.
100 $account_switcher->switchBack();
104 * Ensures entity access is properly working.
106 public function testEntityAccess() {
107 // Set up a non-admin user that is allowed to view test entities.
108 \Drupal::currentUser()->setAccount($this->createUser(['uid' => 2], ['view test entity']));
110 // Use the 'entity_test_label' entity type in order to test the 'view label'
112 $entity = EntityTestLabel::create([
116 // The current user is allowed to view entities.
117 $this->assertEntityAccess([
122 'view label' => TRUE,
125 // The custom user is not allowed to perform any operation on test entities,
126 // except for viewing their label.
127 $custom_user = $this->createUser();
128 $this->assertEntityAccess([
133 'view label' => TRUE,
134 ], $entity, $custom_user);
138 * Ensures default entity access is checked when necessary.
140 * This ensures that the default checkAccess() implementation of the
141 * entity access control handler is considered if hook_entity_access() has not
142 * explicitly forbidden access. Therefore the default checkAccess()
143 * implementation can forbid access, even after access was already explicitly
144 * allowed by hook_entity_access().
146 * @see \Drupal\entity_test\EntityTestAccessControlHandler::checkAccess()
147 * @see entity_test_entity_access()
149 public function testDefaultEntityAccess() {
150 // Set up a non-admin user that is allowed to view test entities.
151 \Drupal::currentUser()->setAccount($this->createUser(['uid' => 2], ['view test entity']));
152 $entity = EntityTest::create([
153 'name' => 'forbid_access',
156 // The user is denied access to the entity.
157 $this->assertEntityAccess([
166 * Ensures that the default handler is used as a fallback.
168 public function testEntityAccessDefaultController() {
169 // The implementation requires that the global user id can be loaded.
170 \Drupal::currentUser()->setAccount($this->createUser(['uid' => 2]));
172 // Check that the default access control handler is used for entities that don't
173 // have a specific access control handler defined.
174 $handler = $this->container->get('entity.manager')->getAccessControlHandler('entity_test_default_access');
175 $this->assertTrue($handler instanceof EntityAccessControlHandler, 'The default entity handler is used for the entity_test_default_access entity type.');
177 $entity = EntityTestDefaultAccess::create();
178 $this->assertEntityAccess([
187 * Ensures entity access for entity translations is properly working.
189 public function testEntityTranslationAccess() {
191 // Set up a non-admin user that is allowed to view test entity translations.
192 \Drupal::currentUser()->setAccount($this->createUser(['uid' => 2], ['view test entity translations']));
194 // Create two test languages.
195 foreach (['foo', 'bar'] as $langcode) {
196 ConfigurableLanguage::create([
198 'label' => $this->randomString(),
202 $entity = EntityTest::create([
208 $translation = $entity->addTranslation('bar');
209 $this->assertEntityAccess([
215 * Ensures the static access cache works correctly in the absence of an UUID.
217 * @see entity_test_entity_access()
219 public function testEntityWithoutUuidAccessCache() {
220 $account = $this->createUser();
222 $entity1 = EntityTestNoUuid::create([
223 'name' => 'Accessible',
227 $entity2 = EntityTestNoUuid::create([
228 'name' => 'Inaccessible',
232 $this->assertTrue($entity1->access('delete', $account), 'Entity 1 can be deleted.');
233 $this->assertFalse($entity2->access('delete', $account), 'Entity 2 CANNOT be deleted.');
236 ->setName('Inaccessible')
240 $this->assertFalse($entity1->access('delete', $account), 'Entity 1 revision 2 CANNOT be deleted.');
244 * Ensures the static access cache works correctly with a UUID and revisions.
246 * @see entity_test_entity_access()
248 public function testEntityWithUuidAccessCache() {
249 $account = $this->createUser();
251 $entity1 = EntityTestRev::create([
252 'name' => 'Accessible',
256 $entity2 = EntityTestRev::create([
257 'name' => 'Inaccessible',
261 $this->assertTrue($entity1->access('delete', $account), 'Entity 1 can be deleted.');
262 $this->assertFalse($entity2->access('delete', $account), 'Entity 2 CANNOT be deleted.');
265 ->setName('Inaccessible')
269 $this->assertFalse($entity1->access('delete', $account), 'Entity 1 revision 2 CANNOT be deleted.');
273 * Tests hook invocations.
275 public function testHooks() {
276 $state = $this->container->get('state');
277 $entity = EntityTest::create([
281 // Test hook_entity_create_access() and hook_ENTITY_TYPE_create_access().
282 $entity->access('create');
283 $this->assertEqual($state->get('entity_test_entity_create_access'), TRUE);
284 $this->assertIdentical($state->get('entity_test_entity_create_access_context'), [
285 'entity_type_id' => 'entity_test',
286 'langcode' => LanguageInterface::LANGCODE_DEFAULT,
288 $this->assertEqual($state->get('entity_test_entity_test_create_access'), TRUE);
290 // Test hook_entity_access() and hook_ENTITY_TYPE_access().
291 $entity->access('view');
292 $this->assertEqual($state->get('entity_test_entity_access'), TRUE);
293 $this->assertEqual($state->get('entity_test_entity_test_access'), TRUE);