Skip to content

Commit 484c1ab

Browse files
committed
BUGFIX: Only allow to publish if the current user can write to the base
1 parent d514e61 commit 484c1ab

File tree

2 files changed

+77
-4
lines changed

2 files changed

+77
-4
lines changed

Neos.Neos/Classes/Security/ContentRepositoryAuthProvider/ContentRepositoryAuthProvider.php

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use Neos\ContentRepository\Core\Feature\SubtreeTagging\Command\UntagSubtree;
2727
use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Command\CreateRootWorkspace;
2828
use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Command\CreateWorkspace;
29+
use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Exception\BaseWorkspaceDoesNotExist;
2930
use Neos\ContentRepository\Core\Feature\WorkspaceModification\Command\ChangeBaseWorkspace;
3031
use Neos\ContentRepository\Core\Feature\WorkspaceModification\Command\DeleteWorkspace;
3132
use Neos\ContentRepository\Core\Feature\WorkspacePublication\Command\DiscardIndividualNodesFromWorkspace;
@@ -36,6 +37,8 @@
3637
use Neos\ContentRepository\Core\Projection\ContentGraph\ContentGraphReadModelInterface;
3738
use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints;
3839
use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId;
40+
use Neos\ContentRepository\Core\SharedModel\Exception\WorkspaceDoesNotExist;
41+
use Neos\ContentRepository\Core\SharedModel\Exception\WorkspaceHasNoBaseWorkspaceName;
3942
use Neos\ContentRepository\Core\SharedModel\Node\NodeAddress;
4043
use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName;
4144
use Neos\Flow\Security\Context as SecurityContext;
@@ -134,6 +137,28 @@ public function canExecuteCommand(CommandInterface $command): Privilege
134137
}
135138
return Privilege::granted(sprintf('User has "manage" permissions for workspace "%s" and "read" permissions for base workspace "%s"', $command->workspaceName->value, $command->baseWorkspaceName->value));
136139
}
140+
if ($command instanceof PublishWorkspace || $command instanceof PublishIndividualNodesFromWorkspace) {
141+
$workspacePermissions = $this->getWorkspacePermissionsForCurrentUser($command->workspaceName);
142+
if (!$workspacePermissions->write) {
143+
return Privilege::denied(sprintf('Missing "write" permissions for workspace "%s": %s', $command->workspaceName->value, $workspacePermissions->getReason()));
144+
}
145+
$workspace = $this->contentGraphReadModel->findWorkspaceByName($command->workspaceName);
146+
if ($workspace === null) {
147+
throw WorkspaceDoesNotExist::butWasSupposedTo($command->workspaceName);
148+
}
149+
if ($workspace->baseWorkspaceName === null) {
150+
throw WorkspaceHasNoBaseWorkspaceName::butWasSupposedTo($workspace->workspaceName);
151+
}
152+
$baseWorkspace = $this->contentGraphReadModel->findWorkspaceByName($workspace->baseWorkspaceName);
153+
if ($baseWorkspace === null) {
154+
throw BaseWorkspaceDoesNotExist::butWasSupposedTo($workspace->workspaceName);
155+
}
156+
$baseWorkspacePermissions = $this->getWorkspacePermissionsForCurrentUser($baseWorkspace->workspaceName);
157+
if (!$baseWorkspacePermissions->write) {
158+
return Privilege::denied(sprintf('Missing "write" permissions for base workspace "%s": %s', $baseWorkspace->workspaceName->value, $baseWorkspacePermissions->getReason()));
159+
}
160+
return Privilege::granted(sprintf('User has "manage" permissions for workspace "%s" and "write" permissions for base workspace "%s"', $command->workspaceName->value, $baseWorkspace->workspaceName->value));
161+
}
137162
return match ($command::class) {
138163
AddDimensionShineThrough::class,
139164
ChangeNodeAggregateName::class,
@@ -143,8 +168,6 @@ public function canExecuteCommand(CommandInterface $command): Privilege
143168
UpdateRootNodeAggregateDimensions::class,
144169
DiscardWorkspace::class,
145170
DiscardIndividualNodesFromWorkspace::class,
146-
PublishWorkspace::class,
147-
PublishIndividualNodesFromWorkspace::class,
148171
RebaseWorkspace::class => $this->requireWorkspaceWritePermission($command->workspaceName),
149172
DeleteWorkspace::class => $this->requireWorkspaceManagePermission($command->workspaceName),
150173
default => Privilege::granted('Command not restricted'),

Neos.Neos/Tests/Behavior/Features/ContentRepository/Security/WorkspacePermissions.feature

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,9 +277,59 @@ Feature: Workspace permission related features
277277
| UpdateRootNodeAggregateDimensions | {"nodeAggregateId":"root"} |
278278
| DiscardWorkspace | {} |
279279
| DiscardIndividualNodesFromWorkspace | {"nodesToDiscard":[{"nodeAggregateId":"a1"}]} |
280-
| PublishWorkspace | {} |
281-
| PublishIndividualNodesFromWorkspace | {"nodesToPublish":[{"nodeAggregateId":"a1"}]} |
282280
| RebaseWorkspace | {} |
283281
# note, creating a core workspace will not grant permissions to it to the current user: Missing "read" permissions for base workspace "new-workspace"
284282
| CreateWorkspace | {"workspaceName":"new-workspace","baseWorkspaceName":"workspace","newContentStreamId":"any"} |
285283

284+
Scenario Outline: Publishing a workspace without WRITE permissions to live
285+
# make changes as owner
286+
Given I am authenticated as owner
287+
288+
And the following CreateNodeAggregateWithNode commands are executed:
289+
| nodeAggregateId | nodeTypeName | parentNodeAggregateId | workspaceName | originDimensionSpacePoint |
290+
| shernode-homes | Neos.Neos:Document | a | workspace | {"language":"de"} |
291+
| other-node | Neos.Neos:Document | a | workspace | {"language":"de"} |
292+
293+
# someone else attempts to publish
294+
Given I am authenticated as <user>
295+
296+
And the command PublishIndividualNodesFromWorkspace is executed with payload and exceptions are caught:
297+
| Key | Value |
298+
| workspaceName | "workspace" |
299+
| nodesToPublish | [{"nodeAggregateId":"shernode-homes"}] |
300+
Then the last command should have thrown an exception of type "AccessDenied" with code 1729086686
301+
302+
And the command PublishWorkspace is executed with payload and exceptions are caught:
303+
| Key | Value |
304+
| workspaceName | "workspace" |
305+
Then the last command should have thrown an exception of type "AccessDenied" with code 1729086686
306+
307+
Examples:
308+
| user |
309+
| restricted_editor |
310+
| simple_user |
311+
| uninvolved |
312+
| editor |
313+
| admin |
314+
315+
Scenario Outline: Publishing a workspace with WRITE permissions to live
316+
Given I am authenticated as <user>
317+
318+
And the following CreateNodeAggregateWithNode commands are executed:
319+
| nodeAggregateId | nodeTypeName | parentNodeAggregateId | workspaceName | originDimensionSpacePoint |
320+
| shernode-homes | Neos.Neos:Document | a | workspace | {"language":"de"} |
321+
| other-node | Neos.Neos:Document | a | workspace | {"language":"de"} |
322+
323+
And the command PublishIndividualNodesFromWorkspace is executed with payload:
324+
| Key | Value |
325+
| workspaceName | "workspace" |
326+
| nodesToPublish | [{"nodeAggregateId":"shernode-homes"}] |
327+
328+
And the command PublishWorkspace is executed with payload:
329+
| Key | Value |
330+
| workspaceName | "workspace" |
331+
332+
Examples:
333+
| user |
334+
| owner |
335+
| collaborator |

0 commit comments

Comments
 (0)