Skip to content

Commit

Permalink
BUGFIX: Fix workspace role assignment after migration, import and sit…
Browse files Browse the repository at this point in the history
…e creation

With #5146 a basic workspace access control was implemented. With that, users won't have access to workspaces unless they have a role assigned.

This can be achieved via

```shell
./flow workspace:assignrole
```

This bugfix makes sure, that the default behavior (users with the role `Neos.Neos:LivePublisher` can collaborate on the `live` workspace) is automatically ensured when creating/importing a site.

Furthermore, the migration command has been fixed to add metadata & role assignments even if no workspace title was set:

```shell
./flow migrateevents:migrateWorkspaceMetadataToWorkspaceService
```

Related: #4726
  • Loading branch information
bwaidelich committed Oct 11, 2024
1 parent 276f1cb commit 4e3c72f
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 159 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Neos\ContentRepositoryRegistry\Service;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Neos\ContentRepository\Core\Factory\ContentRepositoryServiceInterface;
use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface;
use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNodeAndSerializedProperties;
Expand Down Expand Up @@ -498,31 +499,14 @@ public function migrateWorkspaceMetadataToWorkspaceService(\Closure $outputFn):

switch ($eventType) {
case 'RootWorkspaceWasCreated':
$eventData = self::decodeEventPayload($eventEnvelope);
if (!isset($eventData['workspaceTitle'])) {
// without the field it's not a legacy workspace creation event
continue 2;
}
$workspaces[$eventData['workspaceName']] = [
'workspaceName' => $eventData['workspaceName'],
'workspaceTitle' => $eventData['workspaceTitle'],
'workspaceDescription' => $eventData['workspaceDescription'],
'baseWorkspaceName' => null,
'workspaceOwner' => null
];
break;
case 'WorkspaceWasCreated':
$eventData = self::decodeEventPayload($eventEnvelope);
if (!isset($eventData['workspaceTitle'])) {
// without the field it's not a legacy workspace creation event
continue 2;
}
$workspaces[$eventData['workspaceName']] = [
'workspaceName' => $eventData['workspaceName'],
'baseWorkspaceName' => $eventData['baseWorkspaceName'],
'workspaceTitle' => $eventData['workspaceTitle'],
'workspaceDescription' => $eventData['workspaceDescription'],
'workspaceOwner' => $eventData['workspaceOwner'] ?? null
'workspaceTitle' => !empty($eventData['workspaceTitle']) ? $eventData['workspaceTitle'] : $eventData['workspaceName'],
'workspaceDescription' => $eventData['workspaceDescription'] ?? '',
'baseWorkspaceName' => $eventData['baseWorkspaceName'] ?? null,
'workspaceOwner' => $eventData['workspaceOwner'] ?? null,
];
break;
case 'WorkspaceBaseWorkspaceWasChanged':
Expand Down Expand Up @@ -574,78 +558,72 @@ public function migrateWorkspaceMetadataToWorkspaceService(\Closure $outputFn):

$outputFn(sprintf('Found %d legacy workspace events resulting in %d workspaces.', $numberOfHandledWorkspaceEvents, count($workspaces)));

// adding metadata
// adding metadata & role assignments
$addedWorkspaceMetadata = 0;
foreach ($workspaces as $workspaceRow) {
$workspaceName = WorkspaceName::fromString($workspaceRow['workspaceName']);
$outputFn(sprintf('Workspace "%s"', $workspaceName->value));
$baseWorkspaceName = isset($workspaceRow['baseWorkspaceName']) ? WorkspaceName::fromString($workspaceRow['baseWorkspaceName']) : null;
$workspaceOwner = $workspaceRow['workspaceOwner'] ?? null;
$isPersonalWorkspace = str_starts_with($workspaceName->value, 'user-');
$isPrivateWorkspace = $workspaceOwner !== null && !$isPersonalWorkspace;
$isInternalWorkspace = $baseWorkspaceName !== null && $workspaceOwner === null;

$query = <<<SQL
SELECT COUNT(1) FROM neos_neos_workspace_metadata WHERE
content_repository_id = :contentRepositoryId
AND workspace_name = :workspaceName
SQL;
$metadataExists = $this->connection->fetchOne($query, [
'contentRepositoryId' => $this->contentRepositoryId->value,
'workspaceName' => $workspaceName->value,
]);
if ($metadataExists !== 0) {
$outputFn(sprintf('Metadata for "%s" exists already.', $workspaceName->value));
continue;
}
if ($baseWorkspaceName === null) {
$classification = WorkspaceClassification::ROOT;
} elseif ($isPersonalWorkspace) {
$classification = WorkspaceClassification::PERSONAL;
} else {
$classification = WorkspaceClassification::SHARED;
}
$this->connection->insert('neos_neos_workspace_metadata', [
'content_repository_id' => $this->contentRepositoryId->value,
'workspace_name' => $workspaceName->value,
'title' => $workspaceRow['workspaceTitle'],
'description' => $workspaceRow['workspaceDescription'],
'classification' => $classification->value,
'owner_user_id' => $isPersonalWorkspace ? $workspaceOwner : null,
]);
if ($workspaceName->isLive()) {
$this->connection->insert('neos_neos_workspace_role', [
try {
$this->connection->insert('neos_neos_workspace_metadata', [
'content_repository_id' => $this->contentRepositoryId->value,
'workspace_name' => $workspaceName->value,
'title' => $workspaceRow['workspaceTitle'],
'description' => $workspaceRow['workspaceDescription'],
'classification' => $classification->value,
'owner_user_id' => $isPersonalWorkspace ? $workspaceOwner : null,
]);
$outputFn(' Added metadata');
} catch (UniqueConstraintViolationException) {
$outputFn(' Metadata already exists');
}
$roleAssignment = [];
if ($workspaceName->isLive()) {
$roleAssignment = [
'subject_type' => WorkspaceRoleSubjectType::GROUP->value,
'subject' => 'Neos.Neos:LivePublisher',
'role' => WorkspaceRole::COLLABORATOR->value,
]);
];
} elseif ($isInternalWorkspace) {
$this->connection->insert('neos_neos_workspace_role', [
'content_repository_id' => $this->contentRepositoryId->value,
'workspace_name' => $workspaceName->value,
$roleAssignment = [
'subject_type' => WorkspaceRoleSubjectType::GROUP->value,
'subject' => 'Neos.Neos:AbstractEditor',
'role' => WorkspaceRole::COLLABORATOR->value,
]);
];
} elseif ($isPrivateWorkspace) {
$this->connection->insert('neos_neos_workspace_role', [
'content_repository_id' => $this->contentRepositoryId->value,
'workspace_name' => $workspaceName->value,
$roleAssignment = [
'subject_type' => WorkspaceRoleSubjectType::USER->value,
'subject' => $workspaceOwner,
'role' => WorkspaceRole::COLLABORATOR->value,
]);
];
}
if ($roleAssignment !== []) {
try {
$this->connection->insert('neos_neos_workspace_role', [
'content_repository_id' => $this->contentRepositoryId->value,
'workspace_name' => $workspaceName->value,
...$roleAssignment,
]);
$outputFn(' Role assignment added');
} catch (UniqueConstraintViolationException) {
$outputFn(' Role assignment already exists');
}
}
$outputFn(sprintf('Added metadata for "%s".', $workspaceName->value));
$addedWorkspaceMetadata++;
}
if ($addedWorkspaceMetadata === 0) {
$outputFn('Migration was not necessary.');
return;
}

$outputFn(sprintf('Added metadata for %d workspaces.', $addedWorkspaceMetadata));
$outputFn(sprintf('Added metadata & role assignments for %d workspaces.', $addedWorkspaceMetadata));
}

/** ------------------------ */
Expand Down
8 changes: 8 additions & 0 deletions Neos.Neos/Classes/Command/CrCommandController.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Neos\ContentRepository\Core\Projection\CatchUpOptions;
use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId;
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId;
use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName;
use Neos\ContentRepository\Export\ExportService;
use Neos\ContentRepository\Export\ExportServiceFactory;
use Neos\ContentRepository\Export\ImportService;
Expand All @@ -22,6 +23,9 @@
use Neos\Flow\ResourceManagement\ResourceRepository;
use Neos\Media\Domain\Repository\AssetRepository;
use Neos\Neos\AssetUsage\AssetUsageService;
use Neos\Neos\Domain\Model\WorkspaceRole;
use Neos\Neos\Domain\Model\WorkspaceRoleAssignment;
use Neos\Neos\Domain\Service\WorkspaceService;
use Neos\Utility\Files;

class CrCommandController extends CommandController
Expand All @@ -40,6 +44,7 @@ public function __construct(
private readonly ContentRepositoryRegistry $contentRepositoryRegistry,
private readonly ProjectionReplayServiceFactory $projectionReplayServiceFactory,
private readonly AssetUsageService $assetUsageService,
private readonly WorkspaceService $workspaceService,
) {
parent::__construct();
}
Expand Down Expand Up @@ -114,6 +119,9 @@ public function importCommand(string $path, string $contentRepository = 'default
$projectionService = $this->contentRepositoryRegistry->buildService($contentRepositoryId, $this->projectionReplayServiceFactory);
$projectionService->replayAllProjections(CatchUpOptions::create());

$this->outputLine('Assigning live workspace role');
$this->workspaceService->assignWorkspaceRole($contentRepositoryId, WorkspaceName::forLive(), WorkspaceRoleAssignment::createForGroup('Neos.Neos:LivePublisher', WorkspaceRole::COLLABORATOR));

$this->outputLine('<success>Done</success>');
}
}
10 changes: 8 additions & 2 deletions Neos.Neos/Classes/Domain/Service/SiteService.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,20 @@ class SiteService
*/
protected $assetCollectionRepository;

/**
* @Flow\Inject
* @var WorkspaceService
*/
protected $workspaceService;

/**
* Remove given site all nodes for that site and all domains associated.
*/
public function pruneSite(Site $site): void
{
$siteServiceInternals = $this->contentRepositoryRegistry->buildService(
$site->getConfiguration()->contentRepositoryId,
new SiteServiceInternalsFactory()
new SiteServiceInternalsFactory($this->workspaceService)
);

try {
Expand Down Expand Up @@ -176,7 +182,7 @@ public function createSite(

$siteServiceInternals = $this->contentRepositoryRegistry->buildService(
$site->getConfiguration()->contentRepositoryId,
new SiteServiceInternalsFactory()
new SiteServiceInternalsFactory($this->workspaceService)
);
$siteServiceInternals->createSiteNodeIfNotExists($site, $nodeTypeName);

Expand Down
Loading

0 comments on commit 4e3c72f

Please sign in to comment.