Commit fb74ea47 authored by larowlan's avatar larowlan

Issue #2862422 by chr.fritsch, robpowell, phenaproxima, webflo, bdimaggio,...

Issue #2862422 by chr.fritsch, robpowell, phenaproxima, webflo, bdimaggio, seanB, naveenvalecha, sunset_bill, vijaycs85, yoroy, starshaped, xjm, Berdir, hazong: Add per-media type creation permissions for media

(cherry picked from commit 4443678c)
parent 86b363eb
......@@ -6,6 +6,7 @@
*/
use Drupal\user\RoleInterface;
use Drupal\user\Entity\Role;
/**
* Implements hook_install().
......@@ -77,3 +78,45 @@ function media_requirements($phase) {
return $requirements;
}
/**
* Introduce per-bundle permissions.
*/
function media_update_8500() {
$media_types = \Drupal::entityQuery('media_type')->execute();
/** @var \Drupal\user\RoleInterface $role */
foreach (Role::loadMultiple() as $role) {
if ($role->hasPermission('update media')) {
foreach ($media_types as $media_type) {
$role->grantPermission("edit own $media_type media");
}
}
if ($role->hasPermission('update any media')) {
foreach ($media_types as $media_type) {
$role->grantPermission("edit any $media_type media");
}
}
if ($role->hasPermission('delete media')) {
foreach ($media_types as $media_type) {
$role->grantPermission("delete own $media_type media");
}
}
if ($role->hasPermission('delete any media')) {
foreach ($media_types as $media_type) {
$role->grantPermission("delete any $media_type media");
}
}
if ($role->hasPermission('create media')) {
foreach ($media_types as $media_type) {
$role->grantPermission("create $media_type media");
}
}
$role->save();
}
}
......@@ -9,6 +9,7 @@ administer media types:
view media:
title: 'View media'
# @todo: Deprecate some permissions in https://www.drupal.org/project/drupal/issues/2925459
update media:
title: 'Update own media'
......@@ -31,3 +32,6 @@ view all media revisions:
access media overview:
title: 'Access media overview'
description: 'Users with this permission can access the media overview page.'
permission_callbacks:
- \Drupal\media\MediaPermissions::mediaTypePermissions
......@@ -66,7 +66,7 @@
* "revision_log_message" = "revision_log_message",
* },
* bundle_entity_type = "media_type",
* permission_granularity = "entity_type",
* permission_granularity = "bundle",
* admin_permission = "administer media",
* field_ui_base_route = "entity.media_type.edit_form",
* common_reference_target = TRUE,
......
......@@ -20,6 +20,7 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter
return AccessResult::allowed()->cachePerPermissions();
}
$type = $entity->bundle();
$is_owner = ($account->id() && $account->id() === $entity->getOwnerId());
switch ($operation) {
case 'view':
......@@ -32,22 +33,38 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter
return $access_result;
case 'update':
if ($account->hasPermission('edit any ' . $type . ' media')) {
return AccessResult::allowed()->cachePerPermissions();
}
if ($account->hasPermission('edit own ' . $type . ' media') && $is_owner) {
return AccessResult::allowed()->cachePerPermissions()->cachePerUser()->addCacheableDependency($entity);
}
// @todo Deprecate this permission in
// https://www.drupal.org/project/drupal/issues/2925459.
if ($account->hasPermission('update any media')) {
return AccessResult::allowed()->cachePerPermissions();
}
return AccessResult::allowedIf($account->hasPermission('update media') && $is_owner)
->cachePerPermissions()
->cachePerUser()
->addCacheableDependency($entity);
if ($account->hasPermission('update media') && $is_owner) {
return AccessResult::allowed()->cachePerPermissions()->cachePerUser()->addCacheableDependency($entity);
}
return AccessResult::neutral()->cachePerPermissions();
case 'delete':
if ($account->hasPermission('delete any ' . $type . ' media')) {
return AccessResult::allowed()->cachePerPermissions();
}
if ($account->hasPermission('delete own ' . $type . ' media') && $is_owner) {
return AccessResult::allowed()->cachePerPermissions()->cachePerUser()->addCacheableDependency($entity);
}
// @todo Deprecate this permission in
// https://www.drupal.org/project/drupal/issues/2925459.
if ($account->hasPermission('delete any media')) {
return AccessResult::allowed()->cachePerPermissions();
}
return AccessResult::allowedIf($account->hasPermission('delete media') && $is_owner)
->cachePerPermissions()
->cachePerUser()
->addCacheableDependency($entity);
if ($account->hasPermission('delete media') && $is_owner) {
return AccessResult::allowed()->cachePerPermissions()->cachePerUser()->addCacheableDependency($entity);
}
return AccessResult::neutral()->cachePerPermissions();
default:
return AccessResult::neutral()->cachePerPermissions();
......@@ -58,7 +75,12 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter
* {@inheritdoc}
*/
protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
return AccessResult::allowedIfHasPermissions($account, ['administer media', 'create media'], 'OR');
$permissions = [
'administer media',
'create media',
'create ' . $entity_bundle . ' media',
];
return AccessResult::allowedIfHasPermissions($account, $permissions, 'OR');
}
}
<?php
namespace Drupal\media;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides dynamic permissions for each media type.
*/
class MediaPermissions implements ContainerInjectionInterface {
use StringTranslationTrait;
/**
* The entity type manager service.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* MediaPermissions constructor.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager service.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
$this->entityTypeManager = $entity_type_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static($container->get('entity_type.manager'));
}
/**
* Returns an array of media type permissions.
*
* @return array
* The media type permissions.
*
* @see \Drupal\user\PermissionHandlerInterface::getPermissions()
*/
public function mediaTypePermissions() {
$perms = [];
// Generate media permissions for all media types.
$media_types = $this->entityTypeManager
->getStorage('media_type')->loadMultiple();
foreach ($media_types as $type) {
$perms += $this->buildPermissions($type);
}
return $perms;
}
/**
* Returns a list of media permissions for a given media type.
*
* @param \Drupal\media\MediaTypeInterface $type
* The media type.
*
* @return array
* An associative array of permission names and descriptions.
*/
protected function buildPermissions(MediaTypeInterface $type) {
$type_id = $type->id();
$type_params = ['%type_name' => $type->label()];
return [
"create $type_id media" => [
'title' => $this->t('%type_name: Create new media', $type_params),
],
"edit own $type_id media" => [
'title' => $this->t('%type_name: Edit own media', $type_params),
],
"edit any $type_id media" => [
'title' => $this->t('%type_name: Edit any media', $type_params),
],
"delete own $type_id media" => [
'title' => $this->t('%type_name: Delete own media', $type_params),
],
"delete any $type_id media" => [
'title' => $this->t('%type_name: Delete any media', $type_params),
],
];
}
}
......@@ -81,43 +81,66 @@ public function testMediaAccess() {
$this->assertSame("The 'view media' permission is required and the media item must be published.", $access_result->getReason());
$this->grantPermissions($role, ['view media']);
$this->drupalGet('media/' . $media->id());
$this->assertCacheContext('user');
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(200);
// Test 'create BUNDLE media' permission.
$this->drupalGet('media/add/' . $media_type->id());
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(403);
$permissions = ['create ' . $media_type->id() . ' media'];
$this->grantPermissions($role, $permissions);
$this->drupalGet('media/add/' . $media_type->id());
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(200);
user_role_revoke_permissions($role->id(), $permissions);
$role = Role::load(RoleInterface::AUTHENTICATED_ID);
// Test 'create media' permission.
$this->drupalGet('media/add/' . $media_type->id());
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(403);
$this->grantPermissions($role, ['create media']);
$permissions = ['create media'];
$this->grantPermissions($role, $permissions);
$this->drupalGet('media/add/' . $media_type->id());
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(200);
user_role_revoke_permissions($role->id(), $permissions);
$role = Role::load(RoleInterface::AUTHENTICATED_ID);
// Test 'update media' and 'delete media' permissions.
// Test 'edit own BUNDLE media' and 'delete own BUNDLE media' permissions.
$this->drupalGet('media/' . $user_media->id() . '/edit');
$this->assertCacheContext('user');
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(403);
$this->drupalGet('media/' . $user_media->id() . '/delete');
$this->assertCacheContext('user');
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(403);
$this->grantPermissions($role, ['update media']);
$this->grantPermissions($role, ['delete media']);
$permissions = [
'edit own ' . $user_media->bundle() . ' media',
'delete own ' . $user_media->bundle() . ' media',
];
$this->grantPermissions($role, $permissions);
$this->drupalGet('media/' . $user_media->id() . '/edit');
$this->assertCacheContext('user');
$assert_session->statusCodeEquals(200);
$this->drupalGet('media/' . $user_media->id() . '/delete');
$this->assertCacheContext('user');
$assert_session->statusCodeEquals(200);
user_role_revoke_permissions($role->id(), $permissions);
$role = Role::load(RoleInterface::AUTHENTICATED_ID);
// Test 'update any media' and 'delete any media' permissions.
// Test 'edit any BUNDLE media' and 'delete any BUNDLE media' permissions.
$this->drupalGet('media/' . $media->id() . '/edit');
$this->assertCacheContext('user');
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(403);
$this->drupalGet('media/' . $media->id() . '/delete');
$this->assertCacheContext('user');
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(403);
$this->grantPermissions($role, ['update any media']);
$this->grantPermissions($role, ['delete any media']);
$permissions = [
'edit any ' . $media->bundle() . ' media',
'delete any ' . $media->bundle() . ' media',
];
$this->grantPermissions($role, $permissions);
$this->drupalGet('media/' . $media->id() . '/edit');
$this->assertCacheContext('user.permissions');
$assert_session->statusCodeEquals(200);
......
......@@ -20,11 +20,6 @@ trait MediaFunctionalTestTrait {
'administer media display',
'administer media types',
'view media',
'create media',
'update media',
'update any media',
'delete media',
'delete any media',
// Other permissions.
'administer views',
'access content overview',
......
<?php
namespace Drupal\Tests\media\Functional\Update;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
use Drupal\user\Entity\Role;
/**
* Tests that media settings are properly updated during database updates.
*
* @group media
*/
class MediaUpdateTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
__DIR__ . '/../../../fixtures/update/drupal-8.media-enabled.php',
];
}
/**
* Tests that media permissions are correctly migrated.
*
* @see media_update_8500()
*/
public function testBundlePermission() {
$role = Role::load(Role::AUTHENTICATED_ID);
$this->grantPermissions($role, [
'update media',
'update any media',
'delete media',
'delete any media',
'create media',
]);
$this->runUpdates();
/** @var \Drupal\user\RoleInterface $role */
$role = Role::load(Role::AUTHENTICATED_ID);
$media_types = \Drupal::entityQuery('media_type')->execute();
foreach ($media_types as $media_type) {
$this->assertTrue($role->hasPermission("create $media_type media"));
$this->assertTrue($role->hasPermission("edit own $media_type media"));
$this->assertTrue($role->hasPermission("edit any $media_type media"));
$this->assertTrue($role->hasPermission("delete own $media_type media"));
$this->assertTrue($role->hasPermission("delete any $media_type media"));
}
}
}
......@@ -45,17 +45,17 @@ protected function setUpAuthorization($method) {
break;
case 'POST':
$this->grantPermissionsToTestedRole(['create media']);
$this->grantPermissionsToTestedRole(['create camelids media']);
break;
case 'PATCH':
$this->grantPermissionsToTestedRole(['update any media']);
$this->grantPermissionsToTestedRole(['edit any camelids media']);
// @todo Remove this in https://www.drupal.org/node/2824851.
$this->grantPermissionsToTestedRole(['access content']);
break;
case 'DELETE':
$this->grantPermissionsToTestedRole(['delete any media']);
$this->grantPermissionsToTestedRole(['delete any camelids media']);
break;
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment