Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

!!! FEATURE: Content Repository Privileges #5298

Merged
merged 82 commits into from
Nov 16, 2024
Merged

Conversation

bwaidelich
Copy link
Member

@bwaidelich bwaidelich commented Oct 17, 2024

Introduces basic security to the event sourced content repository

This is a breaking change mainly because it requires a new, explicit, workspace role assignment in order for the live workspace to be publicly visible:

./flow workspace:assignrole live "Neos.Flow:Everybody" viewer

Otherwise youll face this error:

Read access denied for workspace "live": User is no Neos Administrator and has no explicit role for workspace "live"

Adjustments to VisibilityConstraints:

The method VisibilityConstraints::frontend() was renamed to VisibilityConstraints::default().
But for the cases you have used VisibilityConstraints at all - as mandatory argument for getSubgraph you can now simplify that to use getContentSubgraph directly:

before

$subgraph = $contentRepository->getContentGraph($nodeAddress->workspaceName)->getSubgraph(
    $nodeAddress->dimensionSpacePoint,
    $nodeAddress->workspaceName->isLive() ? VisibilityConstraints::frontend() : VisibilityConstraints::withoutRestrictions()
);

after

$subgraph = $contentRepository->getContentSubgraph($nodeAddress->workspaceName, $nodeAddress->dimensionSpacePoint);

Note that getContentGraph(...)->getSubgraph(...) still works but is only meant to be used if your you want to determine the VisibilityConstraints for some case differently.

Usage

Workspace Permissions

The following workspace roles exist:

  • VIEWER: Can read from the workspace
    • By default, everybody is a VIEWER to the live workspace
  • COLLABORATOR: Can read from and write to the workspace
  • MANAGER: Can read from and write to the workspace and manage it (i.e. change metadata & role assignments)

Furthermore, personal workspaces can be owned by a particular user. The workspace owner implicitly has the MANAGER role, i.e. has full access to that workspace.

Note

Workspace roles are transitive. That means that COLLABORATOR has all privileges of the VIEWER and MANAGER has all privileges of the COLLABORATOR

To publish changes to a workspace, the authenticated user has to have at least the VIEWER role on the workspace to publish and COLLABORATOR role on the target workspace.

Workspace role assignments can be changed through the workspace module (WIP, see #5132) or via CLI:

./flow workspace:assignrole

ReadNodePrivilege

In general, the ReadNodePrivilege works like it did before Neos 9: It allows to completely hide sub trees of the content repository from a given user group.
But the implementation and configuration format has changed.

This is how a ReadNodePrivilege can be configured via Policy.yaml:

privilegeTargets:
  'Neos\Neos\Security\Authorization\Privilege\ReadNodePrivilege':
    'Neos.Demo:ReadBlog':
      matcher: 'blog'

The matcher refers to a SubtreeTag, so this example would hide all nodes that are tagged with blog (including their descendants) from all users that don't have a GRANT on that specific privilege target.

Tip

By default, the privilege is active for all configured content repositories. By prefixing the matcher with <content-repository-id>: this can be limited to a specific CR (for example default:blog)

EditNodePrivilege

Likewise the purpose of the EditNodePrivilege is like before Neos 9: To restrict certain users from changing nodes (includes creation, setting properties or references, disabling/enabling, tagging and moving, ...) in a given subtree.

The new configuration syntax is similar to the one for ReadNodePrivilege:

privilegeTargets:
  'Neos\Neos\Security\Authorization\Privilege\EditNodePrivilege':
    'Neos.Demo:EditBlogNodes':
      matcher: 'blog'

Again, the matcher refers to a SubtreeTag, so this example would disallow users without a corresponding GRANT permission to edit nodes in the subtree that is tagged blog.

Note

Both privileges expect a subtree to be tagged. That is not yet possible via the Neos UI or CLI but requires interaction with the PHP API ($contentRepository->handle(TagSubtree::create(....))) – we'll provide a way to do that, follow #3732 for updates

Removed privilege types

The following Content Repository privilege types have been removed because their behavior was inconsistent and/or because they led to (performance) issues: NodeTreePrivilege, CreateNodePrivilege, ReadNodePropertyPrivilege, EditNodePropertyPrivilege

We will most probably re-add some of the functionality in a future version, but for now you can implement the AuthProviderInterface in order to enforce custom authorization requirements.

Examples

Disallow a role to edit nodes of a subtree

With the following Policy.yaml:

privilegeTargets:
  'Neos\Neos\Security\Authorization\Privilege\EditNodePrivilege':
    'Neos.Demo:EditBlogNodes':
      matcher: 'blog'
roles:
  'Neos.Neos:Administrator':
    privileges:
      -
        privilegeTarget: 'Neos.Demo:EditBlogNodes'
        permission: GRANT

only administrators are allowed to edit nodes in the blog subtree.

Note

The Neos UI does not currently properly reflect readonly nodes – The inspector will be empty and inline editable fields will be editable – but changing their content will lead to an AccessDenied exception

In order to also hide the uneditable subtree, the following can be added:

privilegeTargets:
  # ...
  'Neos\Neos\Security\Authorization\Privilege\ReadNodePrivilege':
    'Neos.Demo:ReadBlogNodes':
      matcher: 'blog'
roles:
  'Neos.Neos:Administrator':
    privileges:
      # ...
      -
        privilegeTarget: 'Neos.Demo:ReadBlogNodes'
        permission: GRANT

Allow a role to only edit within a given subtree

To achieve the opposite of the above, granting a user group to only edit nodes within a subtree, the whole tree must be tagged (i.e. the homepage node, all descendants will inherit the tag).

With all nodes being tagged demosite and the blog nodes beeing tagged blog in addition, the following Policy.yaml will do:

privilegeTargets:
  'Neos\Neos\Security\Authorization\Privilege\EditNodePrivilege':
    'Neos.Demo:EditAnyNode':
      matcher: 'demosite'
    'Neos.Demo:EditBlogNodes':
      matcher: 'blog'
roles:
  'Neos.Neos:Administrator':
    privileges:
      -
        privilegeTarget: 'Neos.Demo:EditAnyNode'
        permission: GRANT
  'Neos.Neos:Editor':
    privileges:
      -
        privilegeTarget: 'Neos.Demo:EditBlogNodes'
        permission: GRANT

With that, the administrator will be able to edit any node, but editors can only edit nodes within the blog subtree.

Related: #3732

```yaml

privilegeTargets:

  'Neos\Neos\Security\Authorization\Privilege\SubtreeTagPrivilege':

    'Some.Package:FooInAllContentRepositories':
      matcher: 'foo'

    'Some.Package:BarInDefaultContentRepository':
      matcher: 'default:bar'
```
# Conflicts:
#	Neos.Neos/Classes/View/FusionExceptionView.php
to `getAuthenticatedUserId()` and make return type nullable
… new `ContentRepositoryAuthorizationService` singleton
- we must not write the settings to the file system via $crSecurity_testingPolicyPathAndFilename as following processes and tests in the same context would be affected. Also flushing and reloading the configuration catch (`flushConfigurationCache`) is expensive and the tests must not mess with that low level flow behaviour

- instead we only rely on faking the `PolicyService`'s runtime caches. This is done by injecting a custom configuration into `$policyConfiguration` and then initialising the `PolicyService`
Now we throw the cr AccessDenied exception instead of flows access denied in all places of the workspace service to make behaviour consistent.

$contentRepository->handle might for example throw the `AccessDenied` when creating a workspace
And introduce the behaviour of the `FunctionalTestCase`:
neos/flow-development-collection@b9c89e3
assert that base workspace creation is only allowed for writing

and that managing (setting title and roles) is only allowed to managers
{
public static function becauseCommandIsNotGranted(CommandInterface $command, string $reason): self
{
return new self(sprintf('Command "%s" was denied: %s', $command::class, $reason), 1729086686);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm this should probably contain the cr id to ease debugging? But we have this missing alotta places in exceptions

Copy link
Member

@kitsunet kitsunet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried various scenarios locally, checked performance, looked over the code. ✅🙌👏🚀

@kitsunet kitsunet merged commit 725381d into 9.0 Nov 16, 2024
10 checks passed
@mhsdesign mhsdesign deleted the feature/3732-cr-privileges-v2 branch November 18, 2024 08:54
neos-bot pushed a commit to neos/neos that referenced this pull request Nov 18, 2024
@crydotsnake

This comment was marked as off-topic.

mhsdesign added a commit to mhsdesign/neos-development-collection that referenced this pull request Jan 6, 2025
With the introduction of explicit user assignments for workspaces neos#5146 and roles and the full evaluation of those via neos#5298 we have replaced the previously still kept 8.3 yaml security configuration for workspaces.

This decision was done as for security we can no longer use flows security framework which uses aop inside the content-repository library, and also we wanted to cleanup the user <-> workspace relation ship as well as the concept of internal vs shared workspaces.

Following yaml roles were removed:
- `Neos.ContentRepository:Administrator`
- `Neos.ContentRepository:InternalWorkspaceAccess`

Following yaml targets were removed:
- `Neos.Neos:PublicWorkspaceAccess`
- `Neos.Neos:OtherWorkspacesAccess`
- `Neos.Neos:Backend.OtherUsersPersonalWorkspaceAccess`
- `Neos.Neos:Backend.PublishOwnWorkspaceContent`
- `Neos.Neos:Backend.DiscardOwnWorkspaceContent`
- `Neos.Workspace.Ui:Backend.PublishAllToLiveWorkspace`

Note that these targets were moved to the `Neos.Workspace.Ui:Backend` package via neos#5118 in 9.0 but were removed now either way:

- `Neos.Neos:Backend.Module.Management.Workspaces.ManageOwnWorkspaces` (`Neos.Workspace.Ui:Backend.Module.Management.Workspace.ManageOwnWorkspaces`)
- `Neos.Neos:Backend.Module.Management.Workspaces.ManageInternalWorkspaces` (`Neos.Workspace.Ui:Backend.Module.Management.Workspace.ManageInternalWorkspaces`)
- `Neos.Neos:Backend.Module.Management.Workspaces.ManageAllPrivateWorkspaces` (`Neos.Workspace.Ui:Backend.Module.Management.Workspace.ManageAllPrivateWorkspaces`)
mhsdesign added a commit to mhsdesign/neos-development-collection that referenced this pull request Jan 6, 2025
…kspaceName` in policy for workspaces

`current.userInformation.personalWorkspaceName` (`UserService::getPersonalWorkspaceName()`) was initially removed in c3f51e2

because with multiple content repositories we cannot find out the value:

```php
public function getPersonalWorkspaceName(): ?string
{
    $currentUser = $this->userDomainService->getCurrentUser();
    $cr = 'default'; // TODO!!!
    $this->workspaceService->getPersonalWorkspaceForUser($cr, $currentUser);
    return $workspace->workspaceName->value;
}
```

This is luckily no longer needed as the now called `NodeAddressToNodeConverter` (which we decided to keep in Neos 9.0: neos#4873)
Will handle this itself through the security in `ContentRepository::getContentSubgraph()` via neos#5298

Additionally, this pr makes `UserService::getPersonalWorkspaceName()` throw and exception to ease upgrading as otherwise `NULL` will be evaluated.
mhsdesign added a commit to mhsdesign/neos-development-collection that referenced this pull request Jan 6, 2025
…kspaceName` in policy for workspaces

`current.userInformation.personalWorkspaceName` (`UserService::getPersonalWorkspaceName()`) was initially removed in c3f51e2

because with multiple content repositories we cannot find out the value:

```php
public function getPersonalWorkspaceName(): ?string
{
    $currentUser = $this->userDomainService->getCurrentUser();
    $cr = 'default'; // TODO!!!
    $this->workspaceService->getPersonalWorkspaceForUser($cr, $currentUser);
    return $workspace->workspaceName->value;
}
```

This is luckily no longer needed as the now called `NodeAddressToNodeConverter` (which we decided to keep in Neos 9.0: neos#4873)
Will handle this itself through the security in `ContentRepository::getContentSubgraph()` via neos#5298

Additionally, this pr makes `UserService::getPersonalWorkspaceName()` throw and exception to ease upgrading as otherwise `NULL` will be evaluated.
Sebobo pushed a commit to mhsdesign/neos-development-collection that referenced this pull request Jan 13, 2025
With the introduction of explicit user assignments for workspaces neos#5146 and roles and the full evaluation of those via neos#5298 we have replaced the previously still kept 8.3 yaml security configuration for workspaces.

This decision was done as for security we can no longer use flows security framework which uses aop inside the content-repository library, and also we wanted to cleanup the user <-> workspace relation ship as well as the concept of internal vs shared workspaces.

Following yaml roles were removed:
- `Neos.ContentRepository:Administrator`
- `Neos.ContentRepository:InternalWorkspaceAccess`

Following yaml targets were removed:
- `Neos.Neos:PublicWorkspaceAccess`
- `Neos.Neos:OtherWorkspacesAccess`
- `Neos.Neos:Backend.OtherUsersPersonalWorkspaceAccess`
- `Neos.Neos:Backend.PublishOwnWorkspaceContent`
- `Neos.Neos:Backend.DiscardOwnWorkspaceContent`
- `Neos.Workspace.Ui:Backend.PublishAllToLiveWorkspace`

Note that these targets were moved to the `Neos.Workspace.Ui:Backend` package via neos#5118 in 9.0 but were removed now either way:

- `Neos.Neos:Backend.Module.Management.Workspaces.ManageOwnWorkspaces` (`Neos.Workspace.Ui:Backend.Module.Management.Workspace.ManageOwnWorkspaces`)
- `Neos.Neos:Backend.Module.Management.Workspaces.ManageInternalWorkspaces` (`Neos.Workspace.Ui:Backend.Module.Management.Workspace.ManageInternalWorkspaces`)
- `Neos.Neos:Backend.Module.Management.Workspaces.ManageAllPrivateWorkspaces` (`Neos.Workspace.Ui:Backend.Module.Management.Workspace.ManageAllPrivateWorkspaces`)
mhsdesign added a commit to mhsdesign/neos-development-collection that referenced this pull request Jan 13, 2025
…kspaceName` in policy for workspaces

`current.userInformation.personalWorkspaceName` (`UserService::getPersonalWorkspaceName()`) was initially removed in c3f51e2

because with multiple content repositories we cannot find out the value:

```php
public function getPersonalWorkspaceName(): ?string
{
    $currentUser = $this->userDomainService->getCurrentUser();
    $cr = 'default'; // TODO!!!
    $this->workspaceService->getPersonalWorkspaceForUser($cr, $currentUser);
    return $workspace->workspaceName->value;
}
```

This is luckily no longer needed as the now called `NodeAddressToNodeConverter` (which we decided to keep in Neos 9.0: neos#4873)
Will handle this itself through the security in `ContentRepository::getContentSubgraph()` via neos#5298

Additionally, this pr makes `UserService::getPersonalWorkspaceName()` throw and exception to ease upgrading as otherwise `NULL` will be evaluated.
neos-bot pushed a commit to neos/contentrepository-nodeaccess that referenced this pull request Jan 13, 2025
With the introduction of explicit user assignments for workspaces neos/neos-development-collection#5146 and roles and the full evaluation of those via neos/neos-development-collection#5298 we have replaced the previously still kept 8.3 yaml security configuration for workspaces.

This decision was done as for security we can no longer use flows security framework which uses aop inside the content-repository library, and also we wanted to cleanup the user <-> workspace relation ship as well as the concept of internal vs shared workspaces.

Following yaml roles were removed:
- `Neos.ContentRepository:Administrator`
- `Neos.ContentRepository:InternalWorkspaceAccess`

Following yaml targets were removed:
- `Neos.Neos:PublicWorkspaceAccess`
- `Neos.Neos:OtherWorkspacesAccess`
- `Neos.Neos:Backend.OtherUsersPersonalWorkspaceAccess`
- `Neos.Neos:Backend.PublishOwnWorkspaceContent`
- `Neos.Neos:Backend.DiscardOwnWorkspaceContent`
- `Neos.Workspace.Ui:Backend.PublishAllToLiveWorkspace`

Note that these targets were moved to the `Neos.Workspace.Ui:Backend` package via neos/neos-development-collection#5118 in 9.0 but were removed now either way:

- `Neos.Neos:Backend.Module.Management.Workspaces.ManageOwnWorkspaces` (`Neos.Workspace.Ui:Backend.Module.Management.Workspace.ManageOwnWorkspaces`)
- `Neos.Neos:Backend.Module.Management.Workspaces.ManageInternalWorkspaces` (`Neos.Workspace.Ui:Backend.Module.Management.Workspace.ManageInternalWorkspaces`)
- `Neos.Neos:Backend.Module.Management.Workspaces.ManageAllPrivateWorkspaces` (`Neos.Workspace.Ui:Backend.Module.Management.Workspace.ManageAllPrivateWorkspaces`)
neos-bot pushed a commit to neos/neos that referenced this pull request Jan 13, 2025
With the introduction of explicit user assignments for workspaces neos/neos-development-collection#5146 and roles and the full evaluation of those via neos/neos-development-collection#5298 we have replaced the previously still kept 8.3 yaml security configuration for workspaces.

This decision was done as for security we can no longer use flows security framework which uses aop inside the content-repository library, and also we wanted to cleanup the user <-> workspace relation ship as well as the concept of internal vs shared workspaces.

Following yaml roles were removed:
- `Neos.ContentRepository:Administrator`
- `Neos.ContentRepository:InternalWorkspaceAccess`

Following yaml targets were removed:
- `Neos.Neos:PublicWorkspaceAccess`
- `Neos.Neos:OtherWorkspacesAccess`
- `Neos.Neos:Backend.OtherUsersPersonalWorkspaceAccess`
- `Neos.Neos:Backend.PublishOwnWorkspaceContent`
- `Neos.Neos:Backend.DiscardOwnWorkspaceContent`
- `Neos.Workspace.Ui:Backend.PublishAllToLiveWorkspace`

Note that these targets were moved to the `Neos.Workspace.Ui:Backend` package via neos/neos-development-collection#5118 in 9.0 but were removed now either way:

- `Neos.Neos:Backend.Module.Management.Workspaces.ManageOwnWorkspaces` (`Neos.Workspace.Ui:Backend.Module.Management.Workspace.ManageOwnWorkspaces`)
- `Neos.Neos:Backend.Module.Management.Workspaces.ManageInternalWorkspaces` (`Neos.Workspace.Ui:Backend.Module.Management.Workspace.ManageInternalWorkspaces`)
- `Neos.Neos:Backend.Module.Management.Workspaces.ManageAllPrivateWorkspaces` (`Neos.Workspace.Ui:Backend.Module.Management.Workspace.ManageAllPrivateWorkspaces`)
neos-bot pushed a commit to neos/neos that referenced this pull request Jan 13, 2025
…kspaceName` in policy for workspaces

`current.userInformation.personalWorkspaceName` (`UserService::getPersonalWorkspaceName()`) was initially removed in ed761c8

because with multiple content repositories we cannot find out the value:

```php
public function getPersonalWorkspaceName(): ?string
{
    $currentUser = $this->userDomainService->getCurrentUser();
    $cr = 'default'; // TODO!!!
    $this->workspaceService->getPersonalWorkspaceForUser($cr, $currentUser);
    return $workspace->workspaceName->value;
}
```

This is luckily no longer needed as the now called `NodeAddressToNodeConverter` (which we decided to keep in Neos 9.0: neos/neos-development-collection#4873)
Will handle this itself through the security in `ContentRepository::getContentSubgraph()` via neos/neos-development-collection#5298

Additionally, this pr makes `UserService::getPersonalWorkspaceName()` throw and exception to ease upgrading as otherwise `NULL` will be evaluated.
neos-bot pushed a commit to neos/workspace-ui that referenced this pull request Jan 13, 2025
With the introduction of explicit user assignments for workspaces neos/neos-development-collection#5146 and roles and the full evaluation of those via neos/neos-development-collection#5298 we have replaced the previously still kept 8.3 yaml security configuration for workspaces.

This decision was done as for security we can no longer use flows security framework which uses aop inside the content-repository library, and also we wanted to cleanup the user <-> workspace relation ship as well as the concept of internal vs shared workspaces.

Following yaml roles were removed:
- `Neos.ContentRepository:Administrator`
- `Neos.ContentRepository:InternalWorkspaceAccess`

Following yaml targets were removed:
- `Neos.Neos:PublicWorkspaceAccess`
- `Neos.Neos:OtherWorkspacesAccess`
- `Neos.Neos:Backend.OtherUsersPersonalWorkspaceAccess`
- `Neos.Neos:Backend.PublishOwnWorkspaceContent`
- `Neos.Neos:Backend.DiscardOwnWorkspaceContent`
- `Neos.Workspace.Ui:Backend.PublishAllToLiveWorkspace`

Note that these targets were moved to the `Neos.Workspace.Ui:Backend` package via neos/neos-development-collection#5118 in 9.0 but were removed now either way:

- `Neos.Neos:Backend.Module.Management.Workspaces.ManageOwnWorkspaces` (`Neos.Workspace.Ui:Backend.Module.Management.Workspace.ManageOwnWorkspaces`)
- `Neos.Neos:Backend.Module.Management.Workspaces.ManageInternalWorkspaces` (`Neos.Workspace.Ui:Backend.Module.Management.Workspace.ManageInternalWorkspaces`)
- `Neos.Neos:Backend.Module.Management.Workspaces.ManageAllPrivateWorkspaces` (`Neos.Workspace.Ui:Backend.Module.Management.Workspace.ManageAllPrivateWorkspaces`)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants