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\EntityTestStringId;
12 use Drupal\entity_test\Entity\EntityTestDefaultAccess;
13 use Drupal\entity_test\Entity\EntityTestNoUuid;
14 use Drupal\entity_test\Entity\EntityTestLabel;
15 use Drupal\entity_test\Entity\EntityTestRev;
16 use Drupal\language\Entity\ConfigurableLanguage;
17 use Drupal\user\Entity\User;
20 * Tests the entity access control handler.
22 * @coversDefaultClass \Drupal\Core\Entity\EntityAccessControlHandler
25 class EntityAccessControlHandlerTest extends EntityLanguageTestBase {
30 public function setUp() {
33 $this->installEntitySchema('entity_test_no_uuid');
34 $this->installEntitySchema('entity_test_rev');
35 $this->installEntitySchema('entity_test_string_id');
39 * Asserts entity access correctly grants or denies access.
41 public function assertEntityAccess($ops, AccessibleInterface $object, AccountInterface $account = NULL) {
42 foreach ($ops as $op => $result) {
43 $message = format_string("Entity access returns @result with operation '@op'.", [
44 '@result' => !isset($result) ? 'null' : ($result ? 'true' : 'false'),
48 $this->assertEqual($result, $object->access($op, $account), $message);
53 * Ensures user labels are accessible for everyone.
55 public function testUserLabelAccess() {
56 // Set up a non-admin user.
57 \Drupal::currentUser()->setAccount($this->createUser(['uid' => 2]));
59 $anonymous_user = User::getAnonymousUser();
60 $user = $this->createUser();
62 // The current user is allowed to view the anonymous user label.
63 $this->assertEntityAccess([
71 // The current user is allowed to view user labels.
72 $this->assertEntityAccess([
80 // Switch to a anonymous user account.
81 $account_switcher = \Drupal::service('account_switcher');
82 $account_switcher->switchTo(new AnonymousUserSession());
84 // The anonymous user is allowed to view the anonymous user label.
85 $this->assertEntityAccess([
93 // The anonymous user is allowed to view user labels.
94 $this->assertEntityAccess([
102 // Restore user account.
103 $account_switcher->switchBack();
107 * Ensures entity access is properly working.
109 public function testEntityAccess() {
110 // Set up a non-admin user that is allowed to view test entities.
111 \Drupal::currentUser()->setAccount($this->createUser(['uid' => 2], ['view test entity']));
113 // Use the 'entity_test_label' entity type in order to test the 'view label'
115 $entity = EntityTestLabel::create([
119 // The current user is allowed to view entities.
120 $this->assertEntityAccess([
125 'view label' => TRUE,
128 // The custom user is not allowed to perform any operation on test entities,
129 // except for viewing their label.
130 $custom_user = $this->createUser();
131 $this->assertEntityAccess([
136 'view label' => TRUE,
137 ], $entity, $custom_user);
141 * Ensures default entity access is checked when necessary.
143 * This ensures that the default checkAccess() implementation of the
144 * entity access control handler is considered if hook_entity_access() has not
145 * explicitly forbidden access. Therefore the default checkAccess()
146 * implementation can forbid access, even after access was already explicitly
147 * allowed by hook_entity_access().
149 * @see \Drupal\entity_test\EntityTestAccessControlHandler::checkAccess()
150 * @see entity_test_entity_access()
152 public function testDefaultEntityAccess() {
153 // Set up a non-admin user that is allowed to view test entities.
154 \Drupal::currentUser()->setAccount($this->createUser(['uid' => 2], ['view test entity']));
155 $entity = EntityTest::create([
156 'name' => 'forbid_access',
159 // The user is denied access to the entity.
160 $this->assertEntityAccess([
169 * Ensures that the default handler is used as a fallback.
171 public function testEntityAccessDefaultController() {
172 // The implementation requires that the global user id can be loaded.
173 \Drupal::currentUser()->setAccount($this->createUser(['uid' => 2]));
175 // Check that the default access control handler is used for entities that don't
176 // have a specific access control handler defined.
177 $handler = $this->container->get('entity.manager')->getAccessControlHandler('entity_test_default_access');
178 $this->assertTrue($handler instanceof EntityAccessControlHandler, 'The default entity handler is used for the entity_test_default_access entity type.');
180 $entity = EntityTestDefaultAccess::create();
181 $this->assertEntityAccess([
190 * Ensures entity access for entity translations is properly working.
192 public function testEntityTranslationAccess() {
194 // Set up a non-admin user that is allowed to view test entity translations.
195 \Drupal::currentUser()->setAccount($this->createUser(['uid' => 2], ['view test entity translations']));
197 // Create two test languages.
198 foreach (['foo', 'bar'] as $langcode) {
199 ConfigurableLanguage::create([
201 'label' => $this->randomString(),
205 $entity = EntityTest::create([
211 $translation = $entity->addTranslation('bar');
212 $this->assertEntityAccess([
218 * Ensures the static access cache works correctly in the absence of an UUID.
220 * @see entity_test_entity_access()
222 public function testEntityWithoutUuidAccessCache() {
223 $account = $this->createUser();
225 $entity1 = EntityTestNoUuid::create([
226 'name' => 'Accessible',
230 $entity2 = EntityTestNoUuid::create([
231 'name' => 'Inaccessible',
235 $this->assertTrue($entity1->access('delete', $account), 'Entity 1 can be deleted.');
236 $this->assertFalse($entity2->access('delete', $account), 'Entity 2 CANNOT be deleted.');
239 ->setName('Inaccessible')
243 $this->assertFalse($entity1->access('delete', $account), 'Entity 1 revision 2 CANNOT be deleted.');
247 * Ensures the static access cache works correctly with a UUID and revisions.
249 * @see entity_test_entity_access()
251 public function testEntityWithUuidAccessCache() {
252 $account = $this->createUser();
254 $entity1 = EntityTestRev::create([
255 'name' => 'Accessible',
259 $entity2 = EntityTestRev::create([
260 'name' => 'Inaccessible',
264 $this->assertTrue($entity1->access('delete', $account), 'Entity 1 can be deleted.');
265 $this->assertFalse($entity2->access('delete', $account), 'Entity 2 CANNOT be deleted.');
268 ->setName('Inaccessible')
272 $this->assertFalse($entity1->access('delete', $account), 'Entity 1 revision 2 CANNOT be deleted.');
276 * Tests hook invocations.
278 public function testHooks() {
279 $state = $this->container->get('state');
280 $entity = EntityTest::create([
284 // Test hook_entity_create_access() and hook_ENTITY_TYPE_create_access().
285 $entity->access('create');
286 $this->assertEqual($state->get('entity_test_entity_create_access'), TRUE);
287 $this->assertIdentical($state->get('entity_test_entity_create_access_context'), [
288 'entity_type_id' => 'entity_test',
289 'langcode' => LanguageInterface::LANGCODE_DEFAULT,
291 $this->assertEqual($state->get('entity_test_entity_test_create_access'), TRUE);
293 // Test hook_entity_access() and hook_ENTITY_TYPE_access().
294 $entity->access('view');
295 $this->assertEqual($state->get('entity_test_entity_access'), TRUE);
296 $this->assertEqual($state->get('entity_test_entity_test_access'), TRUE);
300 * Tests the default access handling for the ID and UUID fields.
302 * @covers ::fieldAccess
303 * @dataProvider providerTestFieldAccess
305 public function testFieldAccess($entity_class, array $entity_create_values, $expected_id_create_access) {
306 // Set up a non-admin user that is allowed to create and update test
308 \Drupal::currentUser()->setAccount($this->createUser(['uid' => 2], ['administer entity_test content']));
310 // Create the entity to test field access with.
311 $entity = $entity_class::create($entity_create_values);
313 // On newly-created entities, field access must allow setting the UUID
315 $this->assertTrue($entity->get('uuid')->access('edit'));
316 $this->assertTrue($entity->get('uuid')->access('edit', NULL, TRUE)->isAllowed());
317 // On newly-created entities, field access will not allow setting the ID
318 // field if the ID is of type serial. It will allow access if it is of type
320 $this->assertEquals($expected_id_create_access, $entity->get('id')->access('edit'));
321 $this->assertEquals($expected_id_create_access, $entity->get('id')->access('edit', NULL, TRUE)->isAllowed());
323 // Save the entity and check that we can not update the ID or UUID fields
327 // If the ID has been set as part of the create ensure it has been set
329 if (isset($entity_create_values['id'])) {
330 $this->assertSame($entity_create_values['id'], $entity->id());
332 // The UUID is hard-coded by the data provider.
333 $this->assertSame('60e3a179-79ed-4653-ad52-5e614c8e8fbe', $entity->uuid());
334 $this->assertFalse($entity->get('uuid')->access('edit'));
335 $access_result = $entity->get('uuid')->access('edit', NULL, TRUE);
336 $this->assertTrue($access_result->isForbidden());
337 $this->assertEquals('The entity UUID cannot be changed.', $access_result->getReason());
339 // Ensure the ID is still not allowed to be edited.
340 $this->assertFalse($entity->get('id')->access('edit'));
341 $access_result = $entity->get('id')->access('edit', NULL, TRUE);
342 $this->assertTrue($access_result->isForbidden());
343 $this->assertEquals('The entity ID cannot be changed.', $access_result->getReason());
346 public function providerTestFieldAccess() {
348 'serial ID entity' => [
351 'name' => 'A test entity',
352 'uuid' => '60e3a179-79ed-4653-ad52-5e614c8e8fbe',
356 'string ID entity' => [
357 EntityTestStringId::class,
359 'id' => 'a_test_entity',
360 'name' => 'A test entity',
361 'uuid' => '60e3a179-79ed-4653-ad52-5e614c8e8fbe',