diff --git a/core/modules/media/src/MediaAccessControlHandler.php b/core/modules/media/src/MediaAccessControlHandler.php index 434abfe719..0a48e05f9d 100644 --- a/core/modules/media/src/MediaAccessControlHandler.php +++ b/core/modules/media/src/MediaAccessControlHandler.php @@ -26,6 +26,15 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter $access_result = AccessResult::allowedIf($account->hasPermission('view media') && $entity->isPublished()) ->cachePerPermissions() ->addCacheableDependency($entity); + if (!$access_result->isAllowed()) { + $owner_access = AccessResult::allowedIf($account->hasPermission('view media') && $is_owner) + ->cachePerPermissions() + ->cachePerUser() + ->addCacheableDependency($entity); + if ($owner_access->isAllowed()) { + return $owner_access; + } + } if (!$access_result->isAllowed()) { $access_result->setReason("The 'view media' permission is required and the media item must be published."); } diff --git a/core/modules/media/tests/src/Functional/MediaAccessTest.php b/core/modules/media/tests/src/Functional/MediaAccessTest.php index ba02f083bf..c8074d1f12 100644 --- a/core/modules/media/tests/src/Functional/MediaAccessTest.php +++ b/core/modules/media/tests/src/Functional/MediaAccessTest.php @@ -91,6 +91,13 @@ public function testMediaAccess() { $this->drupalGet('media/' . $user_media->id() . '/delete'); $this->assertCacheContext('user'); $assert_session->statusCodeEquals(200); + // Verify that the author can always view the media item even if it is + // unpublished. + $this->grantPermissions($role, ['view media']); + $user_media->setUnpublished()->save(); + $this->drupalGet('media/' . $user_media->id()); + $assert_session->statusCodeEquals(200); + $this->assertCacheContext('user'); // Test 'update any media' and 'delete any media' permissions. $this->drupalGet('media/' . $media->id() . '/edit'); diff --git a/core/modules/media/tests/src/Functional/MediaAccessTest.php b/core/modules/media/tests/src/Functional/MediaAccessTest.php.orig similarity index 100% copy from core/modules/media/tests/src/Functional/MediaAccessTest.php copy to core/modules/media/tests/src/Functional/MediaAccessTest.php.orig diff --git a/core/modules/media/tests/src/Kernel/MediaAccessControlHandlerTest.php b/core/modules/media/tests/src/Kernel/MediaAccessControlHandlerTest.php new file mode 100644 index 0000000000..961ffa0a6c --- /dev/null +++ b/core/modules/media/tests/src/Kernel/MediaAccessControlHandlerTest.php @@ -0,0 +1,140 @@ +accessControlHandler = $this->container->get('entity_type.manager')->getAccessControlHandler('media'); + } + + /** + * @covers ::checkAccess + * @covers ::checkCreateAccess + * @dataProvider testAccessProvider + */ + public function testAccess($which_user, $which_entity, $view_access_result, $update_access_result, $delete_access_result, $create_access_result) { + // We must always create user 1, so that a "normal" user has a ID >1. + $root_user = $this->drupalCreateUser(); + + $permissions = []; + if ($which_user === 'user') { + $permissions = ['view media']; + } + elseif ($which_user === 'owner') { + $permissions = [ + 'view media', + 'update media', + 'delete media', + 'create media', + ]; + } + elseif ($which_user === 'admin') { + $permissions = [ + 'view media', + 'update any media', + 'delete any media', + 'create media', + ]; + } + + $user = $this->drupalCreateUser($permissions); + + $entity_values['id'] = 'llama'; + + $entity_values['uid'] = (in_array('owner', $which_entity)) ? $user->id() : 0; + $entity_values['status'] = (in_array('published', $which_entity)) ? TRUE : FALSE; + + $type = $this->createMediaType('test'); + + $entity_values['bundle'] = $type->id(); + $entity = Media::create($entity_values); + $entity->save(); + + static::assertEquals($view_access_result, $this->accessControlHandler->access($entity, 'view', $user, TRUE)); + static::assertEquals($update_access_result, $this->accessControlHandler->access($entity, 'update', $user, TRUE)); + static::assertEquals($delete_access_result, $this->accessControlHandler->access($entity, 'delete', $user, TRUE)); + static::assertEquals($create_access_result, $this->accessControlHandler->createAccess(NULL, $user, [], TRUE)); + } + + public function testAccessProvider() { + $c = new ContainerBuilder(); + $cache_contexts_manager = $this->prophesize(CacheContextsManager::class); + $cache_contexts_manager->assertValidTokens()->willReturn(TRUE); + $cache_contexts_manager->reveal(); + $c->set('cache_contexts_manager', $cache_contexts_manager); + \Drupal::setContainer($c); + + return [ + 'permissionless + owner' => [ + 'permissionless', + ['owner'], + AccessResult::neutral()->addCacheContexts(['user.permissions'])->addCacheTags(['media:1'])->setReason("The 'view media' permission is required and the media item must be published."), + AccessResult::neutral()->addCacheContexts(['user.permissions', 'user'])->addCacheTags(['media:1']), + AccessResult::neutral()->addCacheContexts(['user.permissions', 'user'])->addCacheTags(['media:1']), + AccessResult::neutral()->addCacheContexts(['user.permissions'])->setReason("The following permissions are required: 'administer media' OR 'create media'."), + ], + 'user + owner' => [ + 'user', + ['owner'], + AccessResult::allowed()->addCacheContexts(['user.permissions', 'user'])->addCacheTags(['media:1']), + AccessResult::neutral()->addCacheContexts(['user.permissions', 'user'])->addCacheTags(['media:1']), + AccessResult::neutral()->addCacheContexts(['user.permissions', 'user'])->addCacheTags(['media:1']), + AccessResult::neutral()->addCacheContexts(['user.permissions'])->setReason("The following permissions are required: 'administer media' OR 'create media'."), + ], + 'user + published' => [ + 'user', + ['published'], + AccessResult::allowed()->addCacheContexts(['user.permissions'])->addCacheTags(['media:1']), + AccessResult::neutral()->addCacheContexts(['user.permissions', 'user'])->addCacheTags(['media:1']), + AccessResult::neutral()->addCacheContexts(['user.permissions', 'user'])->addCacheTags(['media:1']), + AccessResult::neutral()->addCacheContexts(['user.permissions'])->setReason("The following permissions are required: 'administer media' OR 'create media'."), + ], + 'owner + owner' => [ + 'owner', + ['owner'], + AccessResult::allowed()->addCacheContexts(['user.permissions', 'user'])->addCacheTags(['media:1']), + AccessResult::allowed()->addCacheContexts(['user.permissions', 'user'])->addCacheTags(['media:1']), + AccessResult::allowed()->addCacheContexts(['user.permissions', 'user'])->addCacheTags(['media:1']), + AccessResult::allowed()->addCacheContexts(['user.permissions']), + ], + 'admin + published' => [ + 'admin', + ['published'], + AccessResult::allowed()->addCacheContexts(['user.permissions'])->addCacheTags(['media:1']), + AccessResult::allowed()->addCacheContexts(['user.permissions']), + AccessResult::allowed()->addCacheContexts(['user.permissions']), + AccessResult::allowed()->addCacheContexts(['user.permissions']), + ], + ]; + } + +}