Skip to content

Instantly share code, notes, and snippets.

@aertmann
Last active August 28, 2018 13:13
Show Gist options
  • Save aertmann/1494c64b30e464900171db3d24fd8611 to your computer and use it in GitHub Desktop.
Save aertmann/1494c64b30e464900171db3d24fd8611 to your computer and use it in GitHub Desktop.
Multi-site access restriction with Neos CMS
{
"repositories": [
{
"type": "git",
"url": "https://github.com/bwaidelich/Wwwision.AssetConstraints"
}
],
"require": {
"wwwision/assetconstraints": "~0.5.0"
}
privilegeTargets:
'Neos\Flow\Security\Authorization\Privilege\Entity\Doctrine\EntityPrivilege':
'Acme.Package:Site.Alpha':
matcher: 'isType("Neos\Neos\Domain\Model\Site") && property("nodeName") == "alpha"'
'Acme.Package:Site.Beta':
matcher: 'isType("Neos\Neos\Domain\Model\Site") && property("nodeName") == "beta"'
'Neos\Neos\Security\Authorization\Privilege\NodeTreePrivilege':
'Acme.Package:Nodes.Alpha':
matcher: 'isDescendantNodeOf("/sites/alpha")'
'Acme.Package:Nodes.Beta':
matcher: 'isDescendantNodeOf("/sites/beta")'
'Wwwision\AssetConstraints\Security\Authorization\Privilege\ReadAssetPrivilege':
'Acme.Package:Assets.Alpha':
matcher: 'isInCollection("Alpha")'
'Acme.Package:Assets.Beta':
matcher: 'isInCollection("Beta")'
'Wwwision\AssetConstraints\Security\Authorization\Privilege\ReadAssetCollectionPrivilege':
'Acme.Package:AssetCollection.Alpha':
matcher: 'isTitled("Alpha")'
'Acme.Package:AssetCollection.Beta':
matcher: 'isTitled("Beta")'
roles:
# Grant non-authenticated users permission to all sites & assets
'Neos.Flow:Anonymous':
privileges:
-
privilegeTarget: 'Acme.Package:Site.Alpha'
permission: GRANT
-
privilegeTarget: 'Acme.Package:Assets.Alpha'
permission: GRANT
-
privilegeTarget: 'Acme.Package:Site.Beta'
permission: GRANT
-
privilegeTarget: 'Acme.Package:Assets.Beta'
permission: GRANT
# Grant administrators permission to all sites, nodes, assets & collections
'Neos.Neos:Administrator':
privileges:
-
privilegeTarget: 'Acme.Package:Site.Alpha'
permission: GRANT
-
privilegeTarget: 'Acme.Package:Nodes.Alpha'
permission: GRANT
-
privilegeTarget: 'Acme.Package:Assets.Alpha'
permission: GRANT
-
privilegeTarget: 'Acme.Package:AssetCollection.Alpha'
permission: GRANT
-
privilegeTarget: 'Acme.Package:Site.Beta'
permission: GRANT
-
privilegeTarget: 'Acme.Package:Nodes.Beta'
permission: GRANT
-
privilegeTarget: 'Acme.Package:Assets.Beta'
permission: GRANT
-
privilegeTarget: 'Acme.Package:AssetCollection.Beta'
permission: GRANT
'Acme.Package:Alpha':
privileges:
-
privilegeTarget: 'Acme.Package:Site.Alpha'
permission: GRANT
-
privilegeTarget: 'Acme.Package:Nodes.Alpha'
permission: GRANT
-
privilegeTarget: 'Acme.Package:Assets.Alpha'
permission: GRANT
-
privilegeTarget: 'Acme.Package:AssetCollection.Alpha'
permission: GRANT
'Acme.Package:Beta':
privileges:
-
privilegeTarget: 'Acme.Package:Site.Beta'
permission: GRANT
-
privilegeTarget: 'Acme.Package:Nodes.Beta'
permission: GRANT
-
privilegeTarget: 'Acme.Package:Assets.Beta'
permission: GRANT
-
privilegeTarget: 'Acme.Package:AssetCollection.Beta'
permission: GRANT
Neos:
Flow:
security:
authentication:
providers:
NeosBackendProvider:
provider: 'Acme\Package\Security\Authentication\Provider\SiteAwarePersistedUsernamePasswordProvider'
<?php
namespace Acme\Package\Security\Authentication\Provider;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\EntityNotFoundException;
use Neos\Flow\Annotations as Flow;
use Neos\Utility\ObjectAccess;
use Neos\Flow\Security\Authentication\AuthenticationManagerInterface;
use Neos\Flow\Security\Authentication\Provider\PersistedUsernamePasswordProvider;
use Neos\Flow\Security\Authentication\TokenInterface;
use Neos\Flow\Security\Policy\PolicyService;
use Neos\Neos\Domain\Repository\DomainRepository;
use Neos\Neos\Domain\Repository\SiteRepository;
/**
* A custom site aware authentication provider extending the existing persisted username password provider
*/
class SiteAwarePersistedUsernamePasswordProvider extends PersistedUsernamePasswordProvider
{
/**
* @Flow\Inject
* @var DomainRepository
*/
protected $domainRepository;
/**
* @Flow\Inject
* @var SiteRepository
*/
protected $siteRepository;
/**
* @Flow\Inject
* @var AuthenticationManagerInterface
*/
protected $authenticationManager;
/**
* @Flow\Inject
* @var PolicyService
*/
protected $policyService;
/**
* Doctrine's Entity Manager
*
* @Flow\Inject
* @var ObjectManager
*/
protected $entityManager;
/**
* Checks if a user has access to a site by enabling entity privileges and checking if user has access to current site
*
* @param TokenInterface $authenticationToken The token to be authenticated
* @return void
*/
public function authenticate(TokenInterface $authenticationToken)
{
parent::authenticate($authenticationToken);
if ($authenticationToken->getAuthenticationStatus() !== TokenInterface::AUTHENTICATION_SUCCESSFUL) {
return;
}
// Force set isAuthenticated to true on authentication manager to update roles needed for entity privileges
ObjectAccess::setProperty($this->authenticationManager, 'isAuthenticated', true, true);
$domain = $this->domainRepository->findOneByActiveRequest();
if (!$domain) {
if (!array_key_exists('Neos.Neos:Administrator', $this->securityContext->getRoles())) {
$this->rollback($authenticationToken);
}
return;
}
// Check if user can access site and ensure it's not already loaded
$this->entityManager->clear('Neos\Neos\Domain\Model\Site');
try {
$site = $this->siteRepository->findByIdentifier(ObjectAccess::getProperty($domain->getSite(), 'Persistence_Object_Identifier', true));
$site->getName(); // Retrieve entity to check if it can be accessed
} catch (\Exception $e) {
$this->rollback($authenticationToken);
}
}
/**
* @param TokenInterface $authenticationToken
* @return void
*/
protected function rollback(TokenInterface $authenticationToken)
{
$authenticationToken->setAuthenticationStatus(TokenInterface::WRONG_CREDENTIALS);
ObjectAccess::setProperty($this->authenticationManager, 'isAuthenticated', false, true);
$this->policyService->reset();
$this->securityContext->refreshRoles();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment