Skip to content

Commit

Permalink
Issue #27: Replace global permissions with per-bundle perms and 'edit…
Browse files Browse the repository at this point in the history
… own'.
  • Loading branch information
donquixote authored and hfiguiere committed Oct 24, 2024
1 parent 4ef31f8 commit 8a1d00b
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 9 deletions.
27 changes: 22 additions & 5 deletions collabora_online.module
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,26 @@ function collabora_online_entity_operation(EntityInterface $entity) {
* Checks access for the new media operations provided by this module.
*/
function collabora_online_media_access(MediaInterface $media, string $operation, AccountInterface $account): AccessResultInterface {
$permission = match ($operation) {
'preview' => 'preview any media in collabora',
'edit' => 'edit any media in collabora',
};
return AccessResult::allowedIfHasPermission($account, $permission);
$type = $media->bundle();
switch ($operation) {
case 'preview in collabora':
return AccessResult::allowedIfHasPermission($account, "preview $type in collabora");

case 'edit in collabora':
if ($account->hasPermission("edit any $type in collabora")) {
return AccessResult::allowed()->cachePerPermissions();
}
if ($account->hasPermission("edit own $type in collabora")) {
// Use '==' because Drupal sometimes loads integers as strings.
$is_owner = ($account->id() && $account->id() == $media->getOwnerId());
return AccessResult::allowedIf($is_owner)
->cachePerPermissions()
->cachePerUser()
->addCacheableDependency($media);
}
return AccessResult::neutral()->cachePerPermissions();

default:
return AccessResult::neutral();
}
}
7 changes: 3 additions & 4 deletions collabora_online.permissions.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
preview media in collabora:
title: 'Preview media in Collabora'
edit any media in collabora:
title: 'Edit any media in Collabora'
administer collabora instance:
title: 'Administer the Collabora instance'
restrict access: true

permission_callbacks:
- \Drupal\collabora_online\CollaboraMediaPermissions::mediaTypePermissions
81 changes: 81 additions & 0 deletions src/CollaboraMediaPermissions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

namespace Drupal\collabora_online;

use Drupal\Core\DependencyInjection\AutowireTrait;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\BundlePermissionHandlerTrait;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\media\MediaTypeInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
* Provides permissions for Collabora per media type.
*
* @see \Drupal\media\MediaPermissions
*/
class CollaboraMediaPermissions implements ContainerInjectionInterface {

use AutowireTrait;
use BundlePermissionHandlerTrait;
use StringTranslationTrait;

/**
* Constructor.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
* The entity type manager service.
*/
public function __construct(
protected readonly EntityTypeManagerInterface $entityTypeManager,
) {}

/**
* {@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(): array {
// Generate media permissions for all media types.
$media_types = $this->entityTypeManager->getStorage('media_type')->loadMultiple();
return $this->generatePermissions($media_types, [$this, 'buildPermissions']);
}

/**
* Returns a list of 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 [
"preview $type_id in collabora" => [
'title' => $this->t('%type_name: Preview media file in Collabora', $type_params),
],
"edit own $type_id in collabora" => [
'title' => $this->t('%type_name: Edit own media file in Collabora', $type_params),
],
"edit any $type_id in collabora" => [
'title' => $this->t('%type_name: Edit any media file in Collabora', $type_params),
],
];
}

}
86 changes: 86 additions & 0 deletions tests/src/Functional/PermissionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php

declare(strict_types=1);

namespace Drupal\Tests\collabora_online\Functional;

use Drupal\file\Entity\File;
use Drupal\media\Entity\Media;
use Drupal\media\MediaInterface;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\media\Traits\MediaTypeCreationTrait;
use Drupal\Tests\TestFileCreationTrait;
use Drupal\user\Entity\Role;
use Drupal\user\PermissionHandlerInterface;

/**
* Tests dynamically created permissions.
*/
class PermissionTest extends BrowserTestBase {

use MediaTypeCreationTrait;

/**
* {@inheritdoc}
*/
protected static $modules = [
'media',
'collabora_online',
];

/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';

/**
* Tests that dynamic permissions are properly created.
*/
public function testDynamicPermissions(): void {
$this->createMediaType('file', [
'id' => 'public_wiki',
'label' => 'Public wiki',
]);
/** @var \Drupal\user\PermissionHandlerInterface $permission_handler */
$permission_handler = \Drupal::service(PermissionHandlerInterface::class);
$permissions = $permission_handler->getPermissions();
$permissions = array_filter(
$permissions,
fn (array $permission) => $permission['provider'] === 'collabora_online',
);
// Remove noise that is hard to diff.
$permissions = array_map(
static function (array $permission) {
$permission['title'] = (string) $permission['title'];
if ($permission['description'] === NULL) {
unset($permission['description']);
}
if ($permission['provider'] === 'collabora_online') {
unset($permission['provider']);
}
return $permission;
},
$permissions,
);
ksort($permissions);
$this->assertSame([
'administer collabora instance' => [
'title' => 'Administer the Collabora instance',
'restrict access' => TRUE,
],
'edit any public_wiki in collabora' => [
'title' => '<em class="placeholder">Public wiki</em>: Edit any media file in Collabora',
'dependencies' => ['config' => ['media.type.public_wiki']],
],
'edit own public_wiki in collabora' => [
'title' => '<em class="placeholder">Public wiki</em>: Edit own media file in Collabora',
'dependencies' => ['config' => ['media.type.public_wiki']],
],
'preview public_wiki in collabora' => [
'title' => '<em class="placeholder">Public wiki</em>: Preview media file in Collabora',
'dependencies' => ['config' => ['media.type.public_wiki']],
],
], $permissions);
}

}

0 comments on commit 8a1d00b

Please sign in to comment.