backports murph-skeleton
This commit is contained in:
parent
bd9668d97e
commit
42820ebdfa
|
@ -14,6 +14,7 @@ use App\Core\Form\Site\NodeType as EntityType;
|
|||
use App\Core\Manager\EntityManager;
|
||||
use App\Core\Repository\Site\NodeRepository;
|
||||
use App\Core\Site\ControllerLocator;
|
||||
use App\Core\Site\RoleLocator;
|
||||
use App\Core\Site\PageLocator;
|
||||
use App\Core\Sitemap\SitemapBuilder;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
|
@ -39,12 +40,14 @@ class NodeAdminController extends AbstractController
|
|||
NodeRepository $nodeRepository,
|
||||
PageLocator $pageLocator,
|
||||
ControllerLocator $controllerLocator,
|
||||
RoleLocator $roleLocator,
|
||||
Request $request
|
||||
): Response {
|
||||
$entity = $factory->create($node->getMenu());
|
||||
$form = $this->createForm(EntityType::class, $entity, [
|
||||
'pages' => $pageLocator->getPages(),
|
||||
'controllers' => $controllerLocator->getControllers(),
|
||||
'roles' => $roleLocator->getRoles(),
|
||||
'navigation' => $node->getMenu()->getNavigation(),
|
||||
]);
|
||||
|
||||
|
@ -109,12 +112,14 @@ class NodeAdminController extends AbstractController
|
|||
PageFactory $pageFactory,
|
||||
PageLocator $pageLocator,
|
||||
ControllerLocator $controllerLocator,
|
||||
RoleLocator $roleLocator,
|
||||
Request $request,
|
||||
string $tab = 'content'
|
||||
): Response {
|
||||
$form = $this->createForm(EntityType::class, $entity, [
|
||||
'pages' => $pageLocator->getPages(),
|
||||
'controllers' => $controllerLocator->getControllers(),
|
||||
'roles' => $roleLocator->getRoles(),
|
||||
'navigation' => $entity->getMenu()->getNavigation(),
|
||||
]);
|
||||
|
||||
|
|
|
@ -69,6 +69,22 @@ class Configuration implements ConfigurationInterface
|
|||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('security')
|
||||
->children()
|
||||
->arrayNode('roles')
|
||||
->prototype('array')
|
||||
->children()
|
||||
->scalarNode('name')
|
||||
->cannotBeEmpty()
|
||||
->end()
|
||||
->scalarNode('role')
|
||||
->cannotBeEmpty()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('pages')
|
||||
->prototype('array')
|
||||
->children()
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
namespace App\Core\Entity\Site;
|
||||
|
||||
use App\Core\Doctrine\Timestampable;
|
||||
use App\Core\Entity\Analytic\Referer;
|
||||
use App\Core\Entity\Analytic\View;
|
||||
use App\Core\Entity\EntityInterface;
|
||||
use App\Core\Entity\NodeView;
|
||||
use App\Core\Entity\Site\Page\Page;
|
||||
use App\Core\Repository\Site\NodeRepository;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
@ -12,8 +13,6 @@ use Doctrine\Common\Collections\Collection;
|
|||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use function Symfony\Component\String\u;
|
||||
use App\Core\Entity\Analytic\View;
|
||||
use App\Core\Entity\Analytic\Referer;
|
||||
|
||||
/**
|
||||
* @Gedmo\Tree(type="nested")
|
||||
|
@ -157,6 +156,16 @@ class Node implements EntityInterface
|
|||
*/
|
||||
protected $analyticReferers;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="array")
|
||||
*/
|
||||
private $securityRoles = [];
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=3, options={"default"="or"})
|
||||
*/
|
||||
private $securityOperator;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->children = new ArrayCollection();
|
||||
|
@ -640,4 +649,28 @@ class Node implements EntityInterface
|
|||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSecurityRoles(): array
|
||||
{
|
||||
return !is_array($this->securityRoles) ? [] : $this->securityRoles;
|
||||
}
|
||||
|
||||
public function setSecurityRoles(array $securityRoles): self
|
||||
{
|
||||
$this->securityRoles = $securityRoles;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSecurityOperator(): ?string
|
||||
{
|
||||
return $this->securityOperator;
|
||||
}
|
||||
|
||||
public function setSecurityOperator(string $securityOperator): self
|
||||
{
|
||||
$this->securityOperator = $securityOperator;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\EventSuscriber\Account;
|
||||
namespace App\Core\EventSubscriber\Account;
|
||||
|
||||
use App\Core\Event\Account\PasswordRequestEvent;
|
||||
use App\Core\Manager\EntityManager;
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\EventSuscriber;
|
||||
namespace App\Core\EventSubscriber;
|
||||
|
||||
use App\Core\Event\EntityManager\EntityManagerEvent;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\EventSuscriber;
|
||||
namespace App\Core\EventSubscriber;
|
||||
|
||||
use App\Core\Event\Setting\NavigationSettingEvent;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
69
core/EventSubscriber/RequestSecurityEventSubscriber.php
Normal file
69
core/EventSubscriber/RequestSecurityEventSubscriber.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\EventSubscriber;
|
||||
|
||||
use App\Core\Repository\Site\NodeRepository;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
||||
use Symfony\Component\Security\Core\Authorization\AuthorizationChecker;
|
||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||
|
||||
/**
|
||||
* class RequestSecurityEventSubscriber.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class RequestSecurityEventSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
protected NodeRepository $nodeRepository;
|
||||
protected AuthorizationChecker $authorizationChecker;
|
||||
|
||||
public function __construct(NodeRepository $nodeRepository, ContainerInterface $container)
|
||||
{
|
||||
$this->nodeRepository = $nodeRepository;
|
||||
$this->authorizationChecker = $container->get('security.authorization_checker');
|
||||
}
|
||||
|
||||
public function onKernelRequest(RequestEvent $event)
|
||||
{
|
||||
$request = $event->getRequest();
|
||||
|
||||
if (!$request->attributes->has('_node')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$node = $this->nodeRepository->findOneBy([
|
||||
'id' => $request->attributes->get('_node'),
|
||||
]);
|
||||
|
||||
$roles = $node->getSecurityRoles();
|
||||
|
||||
if (empty($roles)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$operator = $node->getSecurityOperator();
|
||||
$exception = new AccessDeniedException('Access denied.');
|
||||
|
||||
foreach ($roles as $role) {
|
||||
$isGranted = $this->authorizationChecker->isGranted($role);
|
||||
|
||||
if ('or' === $operator && $isGranted) {
|
||||
return;
|
||||
}
|
||||
if ('and' === $operator && !$isGranted) {
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
RequestEvent::class => ['onKernelRequest', 1],
|
||||
];
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\EventSuscriber;
|
||||
namespace App\Core\EventSubscriber;
|
||||
|
||||
use App\Core\Event\Setting\SettingEvent;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\EventSuscriber\Site;
|
||||
namespace App\Core\EventSubscriber\Site;
|
||||
|
||||
use App\Core\Site\SiteRequest;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
|
@ -1,12 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\EventSuscriber\Site;
|
||||
namespace App\Core\EventSubscriber\Site;
|
||||
|
||||
use App\Core\Cache\SymfonyCacheManager;
|
||||
use App\Core\Entity\EntityInterface;
|
||||
use App\Core\Entity\Site\Menu;
|
||||
use App\Core\Event\EntityManager\EntityManagerEvent;
|
||||
use App\Core\EventSuscriber\EntityManagerEventSubscriber;
|
||||
use App\Core\EventSubscriber\EntityManagerEventSubscriber;
|
||||
use App\Core\Factory\Site\NodeFactory;
|
||||
use App\Core\Manager\EntityManager;
|
||||
use App\Core\Repository\Site\NodeRepository;
|
|
@ -1,11 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\EventSuscriber\Site;
|
||||
namespace App\Core\EventSubscriber\Site;
|
||||
|
||||
use App\Core\Entity\EntityInterface;
|
||||
use App\Core\Entity\Site\Navigation;
|
||||
use App\Core\Event\EntityManager\EntityManagerEvent;
|
||||
use App\Core\EventSuscriber\EntityManagerEventSubscriber;
|
||||
use App\Core\EventSubscriber\EntityManagerEventSubscriber;
|
||||
use App\Core\Manager\EntityManager;
|
||||
use App\Core\Slugify\CodeSlugify;
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\EventSuscriber\Site;
|
||||
namespace App\Core\EventSubscriber\Site;
|
||||
|
||||
use App\Core\Entity\EntityInterface;
|
||||
use App\Core\Entity\Site\Node;
|
||||
use App\Core\Event\EntityManager\EntityManagerEvent;
|
||||
use App\Core\EventSuscriber\EntityManagerEventSubscriber;
|
||||
use App\Core\EventSubscriber\EntityManagerEventSubscriber;
|
||||
use App\Core\Factory\Site\NodeFactory;
|
||||
use App\Core\Manager\EntityManager;
|
||||
use App\Core\Repository\Site\NodeRepository;
|
|
@ -1,12 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\EventSuscriber\Site\Page;
|
||||
namespace App\Core\EventSubscriber\Site\Page;
|
||||
|
||||
use App\Core\Entity\EntityInterface;
|
||||
use App\Core\Entity\Site\Page\FileBlock;
|
||||
use App\Core\Entity\Site\Page\Page;
|
||||
use App\Core\Event\EntityManager\EntityManagerEvent;
|
||||
use App\Core\EventSuscriber\EntityManagerEventSubscriber;
|
||||
use App\Core\EventSubscriber\EntityManagerEventSubscriber;
|
||||
use App\Core\Form\FileUploadHandler;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\EventSuscriber\Site\Page;
|
||||
namespace App\Core\EventSubscriber\Site\Page;
|
||||
|
||||
use App\Core\Entity\EntityInterface;
|
||||
use App\Core\Entity\Site\Page\Page;
|
||||
use App\Core\Event\EntityManager\EntityManagerEvent;
|
||||
use App\Core\EventSuscriber\EntityManagerEventSubscriber;
|
||||
use App\Core\EventSubscriber\EntityManagerEventSubscriber;
|
||||
use App\Core\Form\FileUploadHandler;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\EventSuscriber\Site;
|
||||
namespace App\Core\EventSubscriber\Site;
|
||||
|
||||
use App\Core\Cache\SymfonyCacheManager;
|
||||
use App\Core\Entity\EntityInterface;
|
||||
|
@ -8,7 +8,7 @@ use App\Core\Entity\Site\Menu;
|
|||
use App\Core\Entity\Site\Navigation;
|
||||
use App\Core\Entity\Site\Node;
|
||||
use App\Core\Event\EntityManager\EntityManagerEvent;
|
||||
use App\Core\EventSuscriber\EntityManagerEventSubscriber;
|
||||
use App\Core\EventSubscriber\EntityManagerEventSubscriber;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
|
||||
/**
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\EventSuscriber\Task;
|
||||
namespace App\Core\EventSubscriber\Task;
|
||||
|
||||
use App\Core\Cache\SymfonyCacheManager;
|
||||
use App\Core\Event\Task\TaskInitEvent;
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\EventSuscriber\Task;
|
||||
namespace App\Core\EventSubscriber\Task;
|
||||
|
||||
use App\Core\Event\Task\TaskInitEvent;
|
||||
use App\Core\Event\Task\TaskRunRequestedEvent;
|
|
@ -119,6 +119,41 @@ class NodeType extends AbstractType
|
|||
]
|
||||
);
|
||||
|
||||
if (count($options['roles']) > 0) {
|
||||
$builder->add(
|
||||
'securityRoles',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => 'Roles',
|
||||
'required' => false,
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
'choices' => call_user_func(function () use ($options) {
|
||||
$choices = [];
|
||||
|
||||
foreach ($options['roles'] as $role) {
|
||||
$choices[$role->getName()] = $role->getRole();
|
||||
}
|
||||
|
||||
return $choices;
|
||||
}),
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'securityOperator',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => 'Condition',
|
||||
'required' => true,
|
||||
'choices' => [
|
||||
'At least one role' => 'or',
|
||||
'All roles' => 'and',
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$actions = [
|
||||
'New page' => 'new',
|
||||
'Use an existing page' => 'existing',
|
||||
|
@ -278,6 +313,7 @@ class NodeType extends AbstractType
|
|||
'data_class' => Node::class,
|
||||
'pages' => [],
|
||||
'controllers' => [],
|
||||
'roles' => [],
|
||||
'navigation' => null,
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -217,3 +217,8 @@
|
|||
"Disable": "Désactiver"
|
||||
"Reuse the query string": "Réutiliser la chaîne de requête"
|
||||
"First element": "Premier élément"
|
||||
"Roles": "Rôles"
|
||||
"Condition": "Condition"
|
||||
"Security": "Sécurité"
|
||||
"At least one role": "Au moins un rôle"
|
||||
"All roles": "Tous les rôles"
|
||||
|
|
|
@ -173,6 +173,7 @@
|
|||
{% endif %}
|
||||
|
||||
{{ form_row(form.url) }}
|
||||
{{ form_row(form.code) }}
|
||||
|
||||
<div class="pb-3">
|
||||
<details>
|
||||
|
@ -206,9 +207,34 @@
|
|||
|
||||
{{ form_row(form.disableUrl) }}
|
||||
{{ form_row(form.enableAnalytics) }}
|
||||
{{ form_row(form.code) }}
|
||||
{{ form_row(form.contentType) }}
|
||||
{{ form_row(form.controller) }}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{{ form_row(form.controller) }}
|
||||
</div>
|
||||
<div class="col-md-6 pl-md-2">
|
||||
{{ form_row(form.contentType) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if form.securityRoles is defined %}
|
||||
<div class="pb-3">
|
||||
<details>
|
||||
<summary>
|
||||
{{ 'Security'|trans }}
|
||||
</summary>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{{ form_row(form.securityRoles) }}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{{ form_row(form.securityOperator) }}
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="accordion mb-3" data-collection="collection-node-parameters" id="form-node-edit-parameters-collection">
|
||||
{% for item in form.parameters %}
|
||||
|
|
|
@ -30,13 +30,13 @@ class ControllerLocator
|
|||
$params = $this->params['site']['controllers'] ?? [];
|
||||
|
||||
foreach ($params as $conf) {
|
||||
$controllerConfiguration = new ControllerConfiguration();
|
||||
$controllerConfiguration
|
||||
$configuration = new ControllerConfiguration();
|
||||
$configuration
|
||||
->setName($conf['name'])
|
||||
->setAction($conf['action'])
|
||||
;
|
||||
|
||||
$this->controllers[$conf['action']] = $controllerConfiguration;
|
||||
$this->controllers[$conf['action']] = $configuration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
38
core/Site/RoleConfiguration.php
Normal file
38
core/Site/RoleConfiguration.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Site;
|
||||
|
||||
/**
|
||||
* class RoleConfiguration.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class RoleConfiguration
|
||||
{
|
||||
protected string $name;
|
||||
protected string $role;
|
||||
|
||||
public function setName(string $name): self
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setRole(string $role): self
|
||||
{
|
||||
$this->role = $role;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRole(): string
|
||||
{
|
||||
return $this->role;
|
||||
}
|
||||
}
|
42
core/Site/RoleLocator.php
Normal file
42
core/Site/RoleLocator.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Site;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
|
||||
/**
|
||||
* class RoleLocator.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class RoleLocator
|
||||
{
|
||||
protected array $params;
|
||||
protected array $roles = [];
|
||||
|
||||
public function __construct(ParameterBagInterface $bag)
|
||||
{
|
||||
$this->params = $bag->get('core');
|
||||
$this->loadRoles();
|
||||
}
|
||||
|
||||
public function getRoles(): array
|
||||
{
|
||||
return $this->roles;
|
||||
}
|
||||
|
||||
protected function loadRoles(): void
|
||||
{
|
||||
$params = $this->params['site']['security']['roles'] ?? [];
|
||||
|
||||
foreach ($params as $conf) {
|
||||
$configuration = new RoleConfiguration();
|
||||
$configuration
|
||||
->setName($conf['name'])
|
||||
->setRole($conf['role'])
|
||||
;
|
||||
|
||||
$this->roles[$conf['name']] = $configuration;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace App\EventSuscriber;
|
||||
namespace App\EventSubscriber;
|
||||
|
||||
use App\Core\Event\Setting\NavigationSettingEvent;
|
||||
use App\Core\EventSuscriber\NavigationSettingEventSubscriber as EventSubscriber;
|
||||
use App\Core\EventSubscriber\NavigationSettingEventSubscriber as EventSubscriber;
|
||||
use App\Core\Setting\NavigationSettingManager;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
|
@ -1,9 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace App\EventSuscriber;
|
||||
namespace App\EventSubscriber;
|
||||
|
||||
use App\Core\Event\Setting\SettingEvent;
|
||||
use App\Core\EventSuscriber\SettingEventSubscriber as EventSubscriber;
|
||||
use App\Core\EventSubscriber\SettingEventSubscriber as EventSubscriber;
|
||||
use App\Core\Setting\SettingManager;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
Loading…
Reference in a new issue