From 8f2653c706fdcb6266ed6ff4a5de31205cd4b88a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Fri, 17 Jul 2026 14:27:13 +0200 Subject: [PATCH] wip: administrative roles --- config/packages/security.yaml | 2 - config/services.yaml | 8 ++ migrations/Version20260717103431.php | 35 +++++++ .../XmlParser/CrmAttributesResponseParser.php | 8 ++ .../Accommodation/CalendarController.php | 2 + .../Admin/Accommodation/CreateController.php | 2 + .../Admin/Accommodation/DeleteController.php | 2 + .../Admin/Accommodation/EditController.php | 2 + .../Admin/Accommodation/IndexController.php | 2 + .../AccommodationBooking/CreateController.php | 2 + .../AccommodationBooking/EditController.php | 2 + .../GenerateAccessLinkController.php | 2 + .../AccommodationBooking/IndexController.php | 2 + .../SendAccessLinkController.php | 2 + .../AccommodationBooking/ShowController.php | 2 + .../AccommodationPrice/CreateController.php | 2 + .../AccommodationPrice/DeleteController.php | 2 + .../AccommodationPrice/EditController.php | 2 + .../AdditionalService/CreateController.php | 2 + .../AdditionalService/DeleteController.php | 2 + .../AdditionalService/EditController.php | 2 + .../Admin/BoardService/CreateController.php | 2 + .../Admin/BoardService/DeleteController.php | 2 + .../Admin/BoardService/EditController.php | 2 + .../BookingEditDraft/DeleteController.php | 2 + .../BookingEditDraft/ExportController.php | 2 + .../BookingEditDraft/IndexController.php | 2 + .../Admin/BookingEditDraft/ShowController.php | 2 + src/Controller/Admin/DashboardController.php | 3 + .../Admin/Log/DownloadController.php | 2 + src/Controller/Admin/Log/IndexController.php | 2 + .../Admin/Log/XmlDumpController.php | 2 + src/Controller/Admin/User/IndexController.php | 2 + src/Entity/Groups/AccommodationBooking.php | 17 ++++ src/Menu/GroupsMenuBuilder.php | 50 ++++++++++ .../Voter/AdministrativeAccessVoter.php | 33 +++++++ templates/layout_admin.html.twig | 6 +- .../CrmAttributesResponseParserTest.php | 92 +++++++++++++++++++ 38 files changed, 307 insertions(+), 3 deletions(-) create mode 100644 migrations/Version20260717103431.php create mode 100644 src/Menu/GroupsMenuBuilder.php create mode 100644 src/Security/Voter/AdministrativeAccessVoter.php create mode 100644 tests/BusProNet/XmlParser/CrmAttributesResponseParserTest.php diff --git a/config/packages/security.yaml b/config/packages/security.yaml index 199292ca..1386ffa1 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -18,7 +18,6 @@ security: role_hierarchy: ROLE_ADMIN: [ ROLE_GROUPS_ADMIN ] ROLE_GROUPS_ADMIN: [ ROLE_GROUPS_MANAGER ] - firewalls: dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ @@ -53,7 +52,6 @@ security: access_control: - { path: ^/webhooks/mailjet/newsletter$, roles: ROLE_MAILJET_WEBHOOK, requires_channel: https } - { path: ^/authorize, roles: IS_AUTHENTICATED_REMEMBERED, requires_channel: https } - - { path: ^/admin, roles: ROLE_ADMIN, requires_channel: https } - { path: ^/, roles: PUBLIC_ACCESS, requires_channel: https } when@test: diff --git a/config/services.yaml b/config/services.yaml index f6db3931..5a22d773 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -121,6 +121,14 @@ services: method: createMainMenu alias: admin_main + App\Menu\GroupsMenuBuilder: + arguments: + $factory: '@knp_menu.factory' + tags: + - name: knp_menu.menu_builder + method: createMainMenu + alias: groups_main + App\Service\BpnXmlSyncManager: arguments: $xmlSource: '@xml_source.storage' diff --git a/migrations/Version20260717103431.php b/migrations/Version20260717103431.php new file mode 100644 index 00000000..fc9e50e5 --- /dev/null +++ b/migrations/Version20260717103431.php @@ -0,0 +1,35 @@ +addSql('ALTER TABLE accommodation_booking ADD managed_by_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE accommodation_booking ADD CONSTRAINT FK_AC389924873649CA FOREIGN KEY (managed_by_id) REFERENCES user (id) ON DELETE SET NULL'); + $this->addSql('CREATE INDEX IDX_AC389924873649CA ON accommodation_booking (managed_by_id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE accommodation_booking DROP FOREIGN KEY FK_AC389924873649CA'); + $this->addSql('DROP INDEX IDX_AC389924873649CA ON accommodation_booking'); + $this->addSql('ALTER TABLE accommodation_booking DROP managed_by_id'); + } +} diff --git a/src/BusProNet/XmlParser/CrmAttributesResponseParser.php b/src/BusProNet/XmlParser/CrmAttributesResponseParser.php index 87ac3808..f6c8a455 100644 --- a/src/BusProNet/XmlParser/CrmAttributesResponseParser.php +++ b/src/BusProNet/XmlParser/CrmAttributesResponseParser.php @@ -16,6 +16,8 @@ class CrmAttributesResponseParser private const BPN_CRM_ID_ADMIN = 1292; private const BPN_CRM_ID_MANAGER = 1293; private const BPN_CRM_ID_TEAMER = 1070; + private const BPN_CRM_ID_GROUPS_MANAGER = 1477; + private const BPN_CRM_ID_GROUPS_ADMIN = 1478; private const BPN_DEFAULT_HOTEL_CODE = 'SSL'; public function parse(Crawler $result): CrmAttributes @@ -57,6 +59,12 @@ class CrmAttributesResponseParser if (self::BPN_CRM_ID_TEAMER === $attribute->id && true === $attribute->selected) { $roles[] = 'ROLE_TEAMER'; } + if (self::BPN_CRM_ID_GROUPS_MANAGER === $attribute->id && true === $attribute->selected) { + $roles[] = 'ROLE_GROUPS_MANAGER'; + } + if (self::BPN_CRM_ID_GROUPS_ADMIN === $attribute->id && true === $attribute->selected) { + $roles[] = 'ROLE_GROUPS_ADMIN'; + } $attributes[] = $attribute; }) diff --git a/src/Controller/Admin/Accommodation/CalendarController.php b/src/Controller/Admin/Accommodation/CalendarController.php index a21edb7c..1e0e93d5 100644 --- a/src/Controller/Admin/Accommodation/CalendarController.php +++ b/src/Controller/Admin/Accommodation/CalendarController.php @@ -9,7 +9,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_GROUPS_ADMIN')] class CalendarController extends AbstractController { #[Route('/admin/accommodation/{id}/calendar', name: 'app_admin_accommodation_calendar', methods: ['GET'])] diff --git a/src/Controller/Admin/Accommodation/CreateController.php b/src/Controller/Admin/Accommodation/CreateController.php index 1b62c2e6..ee1983fe 100644 --- a/src/Controller/Admin/Accommodation/CreateController.php +++ b/src/Controller/Admin/Accommodation/CreateController.php @@ -13,7 +13,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_GROUPS_ADMIN')] class CreateController extends AbstractController { public function __construct( diff --git a/src/Controller/Admin/Accommodation/DeleteController.php b/src/Controller/Admin/Accommodation/DeleteController.php index 5c7c34fc..3d9c0b07 100644 --- a/src/Controller/Admin/Accommodation/DeleteController.php +++ b/src/Controller/Admin/Accommodation/DeleteController.php @@ -12,7 +12,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_GROUPS_ADMIN')] class DeleteController extends AbstractController { public function __construct( diff --git a/src/Controller/Admin/Accommodation/EditController.php b/src/Controller/Admin/Accommodation/EditController.php index 89898da3..d3ea0438 100644 --- a/src/Controller/Admin/Accommodation/EditController.php +++ b/src/Controller/Admin/Accommodation/EditController.php @@ -13,7 +13,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_GROUPS_ADMIN')] class EditController extends AbstractController { public function __construct( diff --git a/src/Controller/Admin/Accommodation/IndexController.php b/src/Controller/Admin/Accommodation/IndexController.php index 8cfd6acd..25f6b04d 100644 --- a/src/Controller/Admin/Accommodation/IndexController.php +++ b/src/Controller/Admin/Accommodation/IndexController.php @@ -8,7 +8,9 @@ use App\Repository\Groups\AccommodationRepository; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_GROUPS_ADMIN')] class IndexController extends AbstractController { public function __construct( diff --git a/src/Controller/Admin/AccommodationBooking/CreateController.php b/src/Controller/Admin/AccommodationBooking/CreateController.php index 1dfbe38e..2c74b710 100644 --- a/src/Controller/Admin/AccommodationBooking/CreateController.php +++ b/src/Controller/Admin/AccommodationBooking/CreateController.php @@ -13,7 +13,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_GROUPS_MANAGER')] class CreateController extends AbstractController { public function __construct( diff --git a/src/Controller/Admin/AccommodationBooking/EditController.php b/src/Controller/Admin/AccommodationBooking/EditController.php index 37c51363..9475d59f 100644 --- a/src/Controller/Admin/AccommodationBooking/EditController.php +++ b/src/Controller/Admin/AccommodationBooking/EditController.php @@ -17,7 +17,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_GROUPS_MANAGER')] class EditController extends AbstractController { public function __construct( diff --git a/src/Controller/Admin/AccommodationBooking/GenerateAccessLinkController.php b/src/Controller/Admin/AccommodationBooking/GenerateAccessLinkController.php index d814aecb..22ff5102 100644 --- a/src/Controller/Admin/AccommodationBooking/GenerateAccessLinkController.php +++ b/src/Controller/Admin/AccommodationBooking/GenerateAccessLinkController.php @@ -12,7 +12,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_GROUPS_MANAGER')] class GenerateAccessLinkController extends AbstractController { public function __construct( diff --git a/src/Controller/Admin/AccommodationBooking/IndexController.php b/src/Controller/Admin/AccommodationBooking/IndexController.php index a6abfc3a..9b69c96b 100644 --- a/src/Controller/Admin/AccommodationBooking/IndexController.php +++ b/src/Controller/Admin/AccommodationBooking/IndexController.php @@ -10,7 +10,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_GROUPS_MANAGER')] class IndexController extends AbstractController { public function __construct( diff --git a/src/Controller/Admin/AccommodationBooking/SendAccessLinkController.php b/src/Controller/Admin/AccommodationBooking/SendAccessLinkController.php index aeb8da53..bba6f719 100644 --- a/src/Controller/Admin/AccommodationBooking/SendAccessLinkController.php +++ b/src/Controller/Admin/AccommodationBooking/SendAccessLinkController.php @@ -12,7 +12,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_GROUPS_MANAGER')] class SendAccessLinkController extends AbstractController { public function __construct( diff --git a/src/Controller/Admin/AccommodationBooking/ShowController.php b/src/Controller/Admin/AccommodationBooking/ShowController.php index 0e63eece..79d23b1b 100644 --- a/src/Controller/Admin/AccommodationBooking/ShowController.php +++ b/src/Controller/Admin/AccommodationBooking/ShowController.php @@ -12,7 +12,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_GROUPS_MANAGER')] class ShowController extends AbstractController { use ReturnUrlTrait; diff --git a/src/Controller/Admin/AccommodationPrice/CreateController.php b/src/Controller/Admin/AccommodationPrice/CreateController.php index 8767b861..8cd82b5d 100644 --- a/src/Controller/Admin/AccommodationPrice/CreateController.php +++ b/src/Controller/Admin/AccommodationPrice/CreateController.php @@ -13,7 +13,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_GROUPS_ADMIN')] class CreateController extends AbstractController { public function __construct( diff --git a/src/Controller/Admin/AccommodationPrice/DeleteController.php b/src/Controller/Admin/AccommodationPrice/DeleteController.php index f925cdc3..1c83c20f 100644 --- a/src/Controller/Admin/AccommodationPrice/DeleteController.php +++ b/src/Controller/Admin/AccommodationPrice/DeleteController.php @@ -12,7 +12,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_GROUPS_ADMIN')] class DeleteController extends AbstractController { public function __construct( diff --git a/src/Controller/Admin/AccommodationPrice/EditController.php b/src/Controller/Admin/AccommodationPrice/EditController.php index 3da1bf11..f08dfd51 100644 --- a/src/Controller/Admin/AccommodationPrice/EditController.php +++ b/src/Controller/Admin/AccommodationPrice/EditController.php @@ -12,7 +12,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_GROUPS_ADMIN')] class EditController extends AbstractController { public function __construct( diff --git a/src/Controller/Admin/AdditionalService/CreateController.php b/src/Controller/Admin/AdditionalService/CreateController.php index f7a9bfb6..5f3f4b6b 100644 --- a/src/Controller/Admin/AdditionalService/CreateController.php +++ b/src/Controller/Admin/AdditionalService/CreateController.php @@ -13,7 +13,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_GROUPS_ADMIN')] class CreateController extends AbstractController { public function __construct( diff --git a/src/Controller/Admin/AdditionalService/DeleteController.php b/src/Controller/Admin/AdditionalService/DeleteController.php index dabd1d1d..59bbbb39 100644 --- a/src/Controller/Admin/AdditionalService/DeleteController.php +++ b/src/Controller/Admin/AdditionalService/DeleteController.php @@ -12,7 +12,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_GROUPS_ADMIN')] class DeleteController extends AbstractController { public function __construct( diff --git a/src/Controller/Admin/AdditionalService/EditController.php b/src/Controller/Admin/AdditionalService/EditController.php index 10ed0200..1db5b3e8 100644 --- a/src/Controller/Admin/AdditionalService/EditController.php +++ b/src/Controller/Admin/AdditionalService/EditController.php @@ -15,7 +15,9 @@ use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_GROUPS_ADMIN')] class EditController extends AbstractController { public function __construct( diff --git a/src/Controller/Admin/BoardService/CreateController.php b/src/Controller/Admin/BoardService/CreateController.php index 77bf468c..3bc18647 100644 --- a/src/Controller/Admin/BoardService/CreateController.php +++ b/src/Controller/Admin/BoardService/CreateController.php @@ -13,7 +13,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_GROUPS_ADMIN')] class CreateController extends AbstractController { public function __construct( diff --git a/src/Controller/Admin/BoardService/DeleteController.php b/src/Controller/Admin/BoardService/DeleteController.php index c61d3aad..9685cf18 100644 --- a/src/Controller/Admin/BoardService/DeleteController.php +++ b/src/Controller/Admin/BoardService/DeleteController.php @@ -12,7 +12,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_GROUPS_ADMIN')] class DeleteController extends AbstractController { public function __construct( diff --git a/src/Controller/Admin/BoardService/EditController.php b/src/Controller/Admin/BoardService/EditController.php index dbbc99a1..5294adf4 100644 --- a/src/Controller/Admin/BoardService/EditController.php +++ b/src/Controller/Admin/BoardService/EditController.php @@ -12,7 +12,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_GROUPS_ADMIN')] class EditController extends AbstractController { public function __construct( diff --git a/src/Controller/Admin/BookingEditDraft/DeleteController.php b/src/Controller/Admin/BookingEditDraft/DeleteController.php index c4a3a3c1..d49c56ca 100644 --- a/src/Controller/Admin/BookingEditDraft/DeleteController.php +++ b/src/Controller/Admin/BookingEditDraft/DeleteController.php @@ -13,7 +13,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_GROUPS_ADMIN')] class DeleteController extends AbstractController { use ReturnUrlTrait; diff --git a/src/Controller/Admin/BookingEditDraft/ExportController.php b/src/Controller/Admin/BookingEditDraft/ExportController.php index 1dba9c5b..bc76b116 100644 --- a/src/Controller/Admin/BookingEditDraft/ExportController.php +++ b/src/Controller/Admin/BookingEditDraft/ExportController.php @@ -10,7 +10,9 @@ use App\Service\BookingExporter; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_GROUPS_ADMIN')] class ExportController extends AbstractController { use ReturnUrlTrait; diff --git a/src/Controller/Admin/BookingEditDraft/IndexController.php b/src/Controller/Admin/BookingEditDraft/IndexController.php index 50cb371a..08a36b08 100644 --- a/src/Controller/Admin/BookingEditDraft/IndexController.php +++ b/src/Controller/Admin/BookingEditDraft/IndexController.php @@ -11,7 +11,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_GROUPS_ADMIN')] class IndexController extends AbstractController { use ReturnUrlTrait; diff --git a/src/Controller/Admin/BookingEditDraft/ShowController.php b/src/Controller/Admin/BookingEditDraft/ShowController.php index 8359ce86..2795f589 100644 --- a/src/Controller/Admin/BookingEditDraft/ShowController.php +++ b/src/Controller/Admin/BookingEditDraft/ShowController.php @@ -10,7 +10,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_GROUPS_ADMIN')] class ShowController extends AbstractController { use ReturnUrlTrait; diff --git a/src/Controller/Admin/DashboardController.php b/src/Controller/Admin/DashboardController.php index 513cc440..a6d3818a 100644 --- a/src/Controller/Admin/DashboardController.php +++ b/src/Controller/Admin/DashboardController.php @@ -4,10 +4,13 @@ declare(strict_types=1); namespace App\Controller\Admin; +use App\Security\Voter\AdministrativeAccessVoter; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted(AdministrativeAccessVoter::ADMINISTRATIVE_ACCESS)] class DashboardController extends AbstractController { #[Route('/admin/dashboard', name: 'app_admin_dashboard')] diff --git a/src/Controller/Admin/Log/DownloadController.php b/src/Controller/Admin/Log/DownloadController.php index 7d733132..ce026a07 100644 --- a/src/Controller/Admin/Log/DownloadController.php +++ b/src/Controller/Admin/Log/DownloadController.php @@ -10,7 +10,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\ResponseHeaderBag; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_ADMIN')] class DownloadController extends AbstractController { public function __construct(private readonly XmlDumpReader $xmlDumpReader) diff --git a/src/Controller/Admin/Log/IndexController.php b/src/Controller/Admin/Log/IndexController.php index b9122f7d..b1c66da4 100644 --- a/src/Controller/Admin/Log/IndexController.php +++ b/src/Controller/Admin/Log/IndexController.php @@ -10,7 +10,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_ADMIN')] class IndexController extends AbstractController { public function __construct( diff --git a/src/Controller/Admin/Log/XmlDumpController.php b/src/Controller/Admin/Log/XmlDumpController.php index 397d32f3..9d9c7cca 100644 --- a/src/Controller/Admin/Log/XmlDumpController.php +++ b/src/Controller/Admin/Log/XmlDumpController.php @@ -10,7 +10,9 @@ use League\Flysystem\FilesystemException; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_ADMIN')] class XmlDumpController extends AbstractController { public function __construct(private readonly XmlDumpReader $xmlDumpReader) diff --git a/src/Controller/Admin/User/IndexController.php b/src/Controller/Admin/User/IndexController.php index 868adad5..1f6f2121 100644 --- a/src/Controller/Admin/User/IndexController.php +++ b/src/Controller/Admin/User/IndexController.php @@ -10,7 +10,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; +#[IsGranted('ROLE_ADMIN')] class IndexController extends AbstractController { public function __construct( diff --git a/src/Entity/Groups/AccommodationBooking.php b/src/Entity/Groups/AccommodationBooking.php index 31028a48..a854cd9d 100644 --- a/src/Entity/Groups/AccommodationBooking.php +++ b/src/Entity/Groups/AccommodationBooking.php @@ -8,6 +8,7 @@ use App\Entity\BlameableEntity; use App\Entity\BlameableEntityInterface; use App\Entity\TimestampableEntity; use App\Entity\TimestampableEntityInterface; +use App\Entity\User; use App\Enum\Groups\AdditionalServiceType; use App\Repository\Groups\AccommodationBookingRepository; use Doctrine\DBAL\Types\Types; @@ -125,6 +126,10 @@ class AccommodationBooking implements BlameableEntityInterface, TimestampableEnt #[Assert\Range(min: 1, max: 100)] private ?int $additionalServicesDiscount = null; + #[ORM\ManyToOne] + #[ORM\JoinColumn(onDelete: 'SET NULL')] + private ?User $managedBy = null; + public function __construct() { $this->uuid = Uuid::v4()->toRfc4122(); @@ -516,4 +521,16 @@ class AccommodationBooking implements BlameableEntityInterface, TimestampableEnt return $this; } + + public function getManagedBy(): ?User + { + return $this->managedBy; + } + + public function setManagedBy(?User $managedBy): self + { + $this->managedBy = $managedBy; + + return $this; + } } diff --git a/src/Menu/GroupsMenuBuilder.php b/src/Menu/GroupsMenuBuilder.php new file mode 100644 index 00000000..bb8e1fdb --- /dev/null +++ b/src/Menu/GroupsMenuBuilder.php @@ -0,0 +1,50 @@ + $options + */ + public function createMainMenu(array $options): ItemInterface + { + $menu = $this->createRootElement(); + + $menu->addChild('Dashboard', [ + 'route' => 'app_admin_dashboard', + 'linkAttributes' => [ + 'title' => 'Dashboard', + ], + 'extras' => [ + 'icon' => 'chart', + ], + ]); + $menu->addChild('Gruppenbuchungen', [ + 'route' => 'app_admin_accommodationbooking', + 'linkAttributes' => [ + 'title' => 'Buchungen', + ], + 'extras' => [ + 'icon' => 'list', + 'routes' => [['pattern' => '/^app_admin_accommodationbooking\//']], + ], + ]); + $menu->addChild('Gruppenhäuser', [ + 'route' => 'app_admin_accommodation', + 'linkAttributes' => [ + 'title' => 'Gruppenhäuser', + ], + 'extras' => [ + 'icon' => 'house', + 'routes' => [['pattern' => '/^app_admin_accommodation\//']], + ], + ]); + + $this->addLogoutItem($menu); + + return $menu; + } +} diff --git a/src/Security/Voter/AdministrativeAccessVoter.php b/src/Security/Voter/AdministrativeAccessVoter.php new file mode 100644 index 00000000..c0276e26 --- /dev/null +++ b/src/Security/Voter/AdministrativeAccessVoter.php @@ -0,0 +1,33 @@ + + */ +class AdministrativeAccessVoter extends Voter +{ + public const ADMINISTRATIVE_ACCESS = 'ADMINISTRATIVE_ACCESS'; + + public function __construct( + private readonly AuthorizationCheckerInterface $authorizationChecker, + ) { + } + + protected function supports(string $attribute, mixed $subject): bool + { + return self::ADMINISTRATIVE_ACCESS === $attribute; + } + + protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool + { + return $this->authorizationChecker->isGranted('ROLE_ADMIN') + || $this->authorizationChecker->isGranted('ROLE_GROUPS_MANAGER'); + } +} diff --git a/templates/layout_admin.html.twig b/templates/layout_admin.html.twig index 94aadd58..aa8bb856 100644 --- a/templates/layout_admin.html.twig +++ b/templates/layout_admin.html.twig @@ -37,7 +37,11 @@
- {{ knp_menu_render(knp_menu_get('admin_main')) }} + {% if is_granted('ROLE_ADMIN') %} + {{ knp_menu_render(knp_menu_get('admin_main')) }} + {% elseif is_granted('ROLE_GROUPS_MANAGER') %} + {{ knp_menu_render(knp_menu_get('groups_main')) }} + {% endif %}
diff --git a/tests/BusProNet/XmlParser/CrmAttributesResponseParserTest.php b/tests/BusProNet/XmlParser/CrmAttributesResponseParserTest.php new file mode 100644 index 00000000..21dced65 --- /dev/null +++ b/tests/BusProNet/XmlParser/CrmAttributesResponseParserTest.php @@ -0,0 +1,92 @@ +parser = new CrmAttributesResponseParser(); + } + + public function testParseAssignsGroupsManagerRoleWhenSelected(): void + { + $roles = $this->parseRoles($this->selectionXml(1477, true)); + + self::assertContains('ROLE_GROUPS_MANAGER', $roles); + self::assertNotContains('ROLE_GROUPS_ADMIN', $roles); + } + + public function testParseAssignsGroupsAdminRoleWhenSelected(): void + { + $roles = $this->parseRoles($this->selectionXml(1478, true)); + + self::assertContains('ROLE_GROUPS_ADMIN', $roles); + self::assertNotContains('ROLE_GROUPS_MANAGER', $roles); + } + + public function testParseAssignsNoGroupsRolesWhenNotSelected(): void + { + $roles = $this->parseRoles($this->selectionXml(1477, false)); + + self::assertNotContains('ROLE_GROUPS_MANAGER', $roles); + self::assertNotContains('ROLE_GROUPS_ADMIN', $roles); + self::assertSame(['ROLE_CUSTOMER'], $roles); + } + + public function testParseStillAssignsExistingAdminManagerTeamerRoles(): void + { + $xmlContent = ' + + + + + + + + + + +'; + + $roles = $this->parseRoles($xmlContent); + + self::assertContains('ROLE_ADMIN', $roles); + self::assertContains('ROLE_MANAGER', $roles); + self::assertContains('ROLE_TEAMER', $roles); + self::assertContains('ROLE_HOUSE_MANAGER', $roles); + } + + /** + * @return string[] + */ + private function parseRoles(string $xmlContent): array + { + $crawler = new Crawler($xmlContent); + $resultNode = $crawler->filterXPath('//ergebnis'); + + return $this->parser->parse($resultNode)->roles; + } + + private function selectionXml(int $id, bool $selected): string + { + return sprintf(' + + + + + + + + +', $id, $selected ? 'True' : 'False'); + } +}