diff --git a/src/EntityAccess.php b/src/EntityAccess.php index 36aeffa..fc9d78b 100644 --- a/src/EntityAccess.php +++ b/src/EntityAccess.php @@ -3,13 +3,16 @@ namespace Drupal\workspace; use Drupal\Core\Access\AccessResult; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Session\AccountInterface; +use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\multiversion\Entity\WorkspaceInterface; /** * Service wrapper for hooks relating to entity access control. */ class EntityAccess { + use StringTranslationTrait; /** * @var int @@ -19,12 +22,21 @@ class EntityAccess { protected $defaultWorkspaceId; /** + * @var \Drupal\Core\Entity\EntityTypeManagerInterface + */ + protected $entityTypeManager; + + /** * Constructs a new EntityAccess. * + * @param EntityTypeManagerInterface $entity_type_manager + * The entity type manager service. * @param int $default_workspace + * The ID of the default workspace. */ - public function __construct($default_workspace) { + public function __construct(EntityTypeManagerInterface $entity_type_manager, $default_workspace) { $this->defaultWorkspaceId = $default_workspace; + $this->entityTypeManager = $entity_type_manager; } /** @@ -47,12 +59,16 @@ class EntityAccess { 'delete' => ['any' => 'delete_any_workspace', 'own' => 'delete_own_workspace'], ]; - $result =AccessResult::allowedIf($operation == 'view' && $workspace->id() == $this->defaultWorkspaceId) + // The default workspace is always viewable, no matter what. + $result = AccessResult::allowedIf($operation == 'view' && $workspace->id() == $this->defaultWorkspaceId) + // Or if the user has permission to access any workspace at all. ->orIf(AccessResult::allowedIfHasPermission($account, $operations[$operation]['any'])) + // Or if it's their own workspace, and they have permission to access their own workspace. ->orIf( AccessResult::allowedIf($workspace->getOwnerId() == $account->id()) ->andIf(AccessResult::allowedIfHasPermission($account, $operations[$operation]['own'])) - ); + ) + ->orIf(AccessResult::allowedIfHasPermission($account, $operation . '_workspace_' . $workspace->id())); return $result; } @@ -85,24 +101,74 @@ class EntityAccess { */ public function workspacePermissions() { $perms = []; - /* - /* @var \Drupal\workbench_moderation\ModerationStateInterface[] $states - $states = ModerationState::loadMultiple(); - /* @var \Drupal\workbench_moderation\ModerationStateTransitionInterface $transition - foreach (ModerationStateTransition::loadMultiple() as $id => $transition) { - $perms['use ' . $id . ' transition'] = [ - 'title' => $this->t('Use the %transition_name transition', [ - '%transition_name' => $transition->label(), - ]), - 'description' => $this->t('Move content from %from state to %to state.', [ - '%from' => $states[$transition->getFromState()]->label(), - '%to' => $states[$transition->getToState()]->label(), - ]), - ]; + + foreach ($this->getAllWorkspaces() as $workspace) { + $perms += $this->createWorkspaceViewPermission($workspace) + + $this->createWorkspaceEditPermission($workspace) + + $this->createWorkspaceDeletePermission($workspace); } return $perms; - */ + } + + /** + * Returns a list of all workspace entities in the system. + * + * @return WorkspaceInterface[] + */ + protected function getAllWorkspaces() { + return $this->entityTypeManager->getStorage('workspace')->loadMultiple(); + } + + /** + * Derives the view permission for a specific workspace. + * + * @param \Drupal\multiversion\Entity\WorkspaceInterface $workspace + * The workspace from which to derive the permission. + * @return array + * A single-item array with the permission to define. + */ + protected function createWorkspaceViewPermission(WorkspaceInterface $workspace) { + $perms['view_workspace_' . $workspace->id()] = [ + 'title' => $this->t('View the %workspace workspace', ['%workspace' => $workspace->label()]), + 'description' => $this->t('View the %workspace workspace and content within it', ['%workspace' => $workspace->label()]), + ]; + + return $perms; + } + + /** + * Derives the edit permission for a specific workspace. + * + * @param \Drupal\multiversion\Entity\WorkspaceInterface $workspace + * The workspace from which to derive the permission. + * @return array + * A single-item array with the permission to define. + */ + protected function createWorkspaceEditPermission(WorkspaceInterface $workspace) { + $perms['update_workspace_' . $workspace->id()] = [ + 'title' => $this->t('Edit the %workspace workspace', ['%workspace' => $workspace->label()]), + 'description' => $this->t('Edit the %workspace workspace itself', ['%workspace' => $workspace->label()]), + ]; + + return $perms; + } + + /** + * Derives the delete permission for a specific workspace. + * + * @param \Drupal\multiversion\Entity\WorkspaceInterface $workspace + * The workspace from which to derive the permission. + * @return array + * A single-item array with the permission to define. + */ + protected function createWorkspaceDeletePermission(WorkspaceInterface $workspace) { + $perms['delete_workspace_' . $workspace->id()] = [ + 'title' => $this->t('Delete the %workspace workspace', ['%workspace' => $workspace->label()]), + 'description' => $this->t('View the %workspace workspace and all content within it', ['%workspace' => $workspace->label()]), + ]; + + return $perms; } } diff --git a/tests/src/Functional/WorkspaceIndividualPermissionsTest.php b/tests/src/Functional/WorkspaceIndividualPermissionsTest.php new file mode 100644 index 0000000..f86885f --- /dev/null +++ b/tests/src/Functional/WorkspaceIndividualPermissionsTest.php @@ -0,0 +1,96 @@ +drupalCreateUser($permissions); + + // Login as a limited-access user and create a workspace. + $this->drupalLogin($editor1); + + $this->createWorkspaceThroughUI('Bears', 'bears'); + $bears = $this->getOneWorkspaceByLabel('Bears'); + + // Now login as a different user with permission to edit that workspace, + // specifically. + + $editor2 = $this->drupalCreateUser(array_merge($permissions, ['update_workspace_' . $bears->id()])); + + $this->drupalLogin($editor2); + $session = $this->getSession(); + + $this->drupalGet("/admin/structure/workspace/{$bears->id()}/edit"); + $this->assertEquals(200, $session->getStatusCode()); + } + + + /** + * Verifies that a user can view a specific workspace. + */ + public function testViewIndividualWorkspace() { + $permissions = [ + 'access administration pages', + 'administer site configuration', + 'create_workspace', + 'edit_own_workspace', + ]; + + $editor1 = $this->drupalCreateUser($permissions); + + // Login as a limited-access user and create a workspace. + $this->drupalLogin($editor1); + + $this->createWorkspaceThroughUI('Bears', 'bears'); + $bears = $this->getOneWorkspaceByLabel('Bears'); + + // Now login as a different user and create a workspace. + + $editor2 = $this->drupalCreateUser(array_merge($permissions, ['view_workspace_' . $bears->id()])); + + $this->drupalLogin($editor2); + $session = $this->getSession(); + + $this->createWorkspaceThroughUI('Packers', 'packers'); + + $packers = $this->getOneWorkspaceByLabel('Packers'); + + // Load the activate form for the Bears workspace. It should work, because + // the user has the permission specific to that workspace. + $this->drupalGet("admin/structure/workspace/{$bears->id()}/activate"); + $this->assertEquals(200, $session->getStatusCode()); + + // But editor 1 cannot view the Packers workspace. + + $this->drupalLogin($editor1); + $this->drupalGet("admin/structure/workspace/{$packers->id()}/activate"); + $this->assertEquals(403, $session->getStatusCode()); + } +} diff --git a/tests/src/Functional/WorkspacePermissionsTest.php b/tests/src/Functional/WorkspacePermissionsTest.php index bc27375..57458fd 100644 --- a/tests/src/Functional/WorkspacePermissionsTest.php +++ b/tests/src/Functional/WorkspacePermissionsTest.php @@ -20,11 +20,6 @@ class WorkspacePermissionsTest extends BrowserTestBase { public static $modules = ['workspace', 'multiversion']; - public function setUp() { - parent::setUp(); - - } - /** * Verifies that a user can create but not edit a workspace. * diff --git a/tests/src/Functional/WorkspaceViewTest.php b/tests/src/Functional/WorkspaceViewTest.php index 4363ce4..7899a94 100644 --- a/tests/src/Functional/WorkspaceViewTest.php +++ b/tests/src/Functional/WorkspaceViewTest.php @@ -18,15 +18,10 @@ class WorkspaceViewTest extends BrowserTestBase { public static $modules = ['workspace', 'multiversion']; - public function setUp() { - parent::setUp(); - - } - /** - * Verifies that a user can create and edit only their own workspace. + * Verifies that a user can view their own workspace. */ - public function testEditOwnWorkspace() { + public function testViewOwnWorkspace() { $permissions = [ 'access administration pages', 'administer site configuration', @@ -42,8 +37,6 @@ class WorkspaceViewTest extends BrowserTestBase { $this->createWorkspaceThroughUI('Bears', 'bears'); - // Now edit that same workspace; We should be able to do so. - $bears = $this->getOneWorkspaceByLabel('Bears'); // Now login as a different user and create a workspace. @@ -66,4 +59,46 @@ class WorkspaceViewTest extends BrowserTestBase { $this->drupalGet("admin/structure/workspace/{$packers->id()}/activate"); $this->assertEquals(200, $session->getStatusCode()); } + + /** + * Verifies that a user can view any workspace. + */ + public function testViewAnyWorkspace() { + $permissions = [ + 'access administration pages', + 'administer site configuration', + 'create_workspace', + 'edit_own_workspace', + 'view_any_workspace', + ]; + + $editor1 = $this->drupalCreateUser($permissions); + + // Login as a limited-access user and create a workspace. + $this->drupalLogin($editor1); + + $this->createWorkspaceThroughUI('Bears', 'bears'); + + $bears = $this->getOneWorkspaceByLabel('Bears'); + + // Now login as a different user and create a workspace. + + $editor2 = $this->drupalCreateUser($permissions); + + $this->drupalLogin($editor2); + $session = $this->getSession(); + + $this->createWorkspaceThroughUI('Packers', 'packers'); + + $packers = $this->getOneWorkspaceByLabel('Packers'); + + // Load the activate form for the Bears workspace. This user should be + // able to see both workspaces because of the "view any" permission. + $this->drupalGet("admin/structure/workspace/{$bears->id()}/activate"); + $this->assertEquals(200, $session->getStatusCode()); + + // But editor 2 should be able to activate the Packers workspace. + $this->drupalGet("admin/structure/workspace/{$packers->id()}/activate"); + $this->assertEquals(200, $session->getStatusCode()); + } } diff --git a/workspace.services.yml b/workspace.services.yml index 18ef9ff..38e0956 100644 --- a/workspace.services.yml +++ b/workspace.services.yml @@ -34,7 +34,7 @@ services: - { name: event_subscriber } workspace.entity_access: class: Drupal\workspace\EntityAccess - arguments: ['%workspace.default%'] + arguments: ['@entity_type.manager', '%workspace.default%'] access_check.workspace_view: class: Drupal\workspace\Access\WorkspaceViewCheck tags: