From a129dbe963cda8dcaebb1b14fdb42b8f3c22ce01 Mon Sep 17 00:00:00 2001 From: Laurent Constantin Date: Fri, 31 May 2024 17:35:45 +0200 Subject: [PATCH] feat(kobo): KoboDevice admin is accessible to all users --- src/Controller/Kobo/KoboAdminController.php | 81 ------------- src/Controller/Kobo/KoboDeviceController.php | 108 ++++++++++++++++++ src/Form/KoboType.php | 27 +++-- src/Menu/MenuBuilder.php | 1 + src/Security/Voter/KoboDeviceVoter.php | 45 ++++++++ templates/kobo_admin/_delete_form.html.twig | 4 - templates/kobo_admin/edit.html.twig | 16 --- templates/kobo_admin/new.html.twig | 14 --- .../kobodevice_user/_delete_form.html.twig | 6 + .../_form.html.twig | 0 templates/kobodevice_user/edit.html.twig | 16 +++ .../index.html.twig | 12 +- .../instructions.html.twig | 0 templates/kobodevice_user/new.html.twig | 14 +++ .../show.html.twig | 8 +- 15 files changed, 219 insertions(+), 133 deletions(-) delete mode 100644 src/Controller/Kobo/KoboAdminController.php create mode 100644 src/Controller/Kobo/KoboDeviceController.php create mode 100644 src/Security/Voter/KoboDeviceVoter.php delete mode 100644 templates/kobo_admin/_delete_form.html.twig delete mode 100644 templates/kobo_admin/edit.html.twig delete mode 100644 templates/kobo_admin/new.html.twig create mode 100644 templates/kobodevice_user/_delete_form.html.twig rename templates/{kobo_admin => kobodevice_user}/_form.html.twig (100%) create mode 100644 templates/kobodevice_user/edit.html.twig rename templates/{kobo_admin => kobodevice_user}/index.html.twig (59%) rename templates/{kobo_admin => kobodevice_user}/instructions.html.twig (100%) create mode 100644 templates/kobodevice_user/new.html.twig rename templates/{kobo_admin => kobodevice_user}/show.html.twig (62%) diff --git a/src/Controller/Kobo/KoboAdminController.php b/src/Controller/Kobo/KoboAdminController.php deleted file mode 100644 index 4b48c6fa..00000000 --- a/src/Controller/Kobo/KoboAdminController.php +++ /dev/null @@ -1,81 +0,0 @@ -render('kobo_admin/index.html.twig', [ - 'kobos' => $koboDeviceRepository->findAll(), - ]); - } - - #[Route('/new', name: 'app_kobo_admin_new', methods: ['GET', 'POST'])] - public function new(Request $request, EntityManagerInterface $entityManager): Response - { - $koboDevice = new KoboDevice(); - $form = $this->createForm(KoboType::class, $koboDevice); - $form->handleRequest($request); - - if ($form->isSubmitted() && $form->isValid()) { - $entityManager->persist($koboDevice); - $entityManager->flush(); - - return $this->redirectToRoute('app_kobo_admin_index', [], Response::HTTP_SEE_OTHER); - } - - return $this->render('kobo_admin/new.html.twig', [ - 'kobo' => $koboDevice, - 'form' => $form, - ]); - } - - #[Route('/{id}', name: 'app_kobo_admin_show', methods: ['GET'])] - public function show(KoboDevice $kobo): Response - { - return $this->render('kobo_admin/show.html.twig', [ - 'kobo' => $kobo, - ]); - } - - #[Route('/{id}/edit', name: 'app_kobo_admin_edit', methods: ['GET', 'POST'])] - public function edit(Request $request, KoboDevice $kobo, EntityManagerInterface $entityManager): Response - { - $form = $this->createForm(KoboType::class, $kobo); - $form->handleRequest($request); - - if ($form->isSubmitted() && $form->isValid()) { - $entityManager->flush(); - - return $this->redirectToRoute('app_kobo_admin_index', [], Response::HTTP_SEE_OTHER); - } - - return $this->render('kobo_admin/edit.html.twig', [ - 'kobo' => $kobo, - 'form' => $form, - ]); - } - - #[Route('/{id}', name: 'app_kobo_admin_delete', methods: ['POST'])] - public function delete(Request $request, KoboDevice $kobo, EntityManagerInterface $entityManager): Response - { - if ($this->isCsrfTokenValid('delete'.$kobo->getId(), (string) $request->request->get('_token'))) { - $entityManager->remove($kobo); - $entityManager->flush(); - } - - return $this->redirectToRoute('app_kobo_admin_index', [], Response::HTTP_SEE_OTHER); - } -} diff --git a/src/Controller/Kobo/KoboDeviceController.php b/src/Controller/Kobo/KoboDeviceController.php new file mode 100644 index 00000000..99edc978 --- /dev/null +++ b/src/Controller/Kobo/KoboDeviceController.php @@ -0,0 +1,108 @@ +getUser() === null) { + throw $this->createAccessDeniedException(); + } + + return $this->render('kobodevice_user/index.html.twig', [ + 'kobos' => $koboDeviceRepository->findAllByUser($this->getUser()), + ]); + } + + #[Route('/new', name: 'app_kobodevice_user_new', methods: ['GET', 'POST'])] + public function new(Request $request, EntityManagerInterface $entityManager): Response + { + $user = $this->getUser(); + if (!$user instanceof User) { + throw $this->createAccessDeniedException(); + } + $koboDevice = new KoboDevice(); + $koboDevice->setUser($user); + + if (!$this->isGranted('CREATE', $koboDevice)) { + throw $this->createAccessDeniedException(); + } + + $form = $this->createForm(KoboType::class, $koboDevice); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $entityManager->persist($koboDevice); + $entityManager->flush(); + + return $this->redirectToRoute('app_kobodevice_user_index', [], Response::HTTP_SEE_OTHER); + } + + return $this->render('kobodevice_user/new.html.twig', [ + 'kobo' => $koboDevice, + 'form' => $form, + ]); + } + + #[Route('/{id}', name: 'app_kobodevice_user_show', methods: ['GET'])] + public function show(KoboDevice $koboDevice): Response + { + if (!$this->isGranted('VIEW', $koboDevice)) { + throw $this->createAccessDeniedException(); + } + + return $this->render('kobodevice_user/show.html.twig', [ + 'kobo' => $koboDevice, + ]); + } + + #[Route('/{id}/edit', name: 'app_kobodevice_user_edit', methods: ['GET', 'POST'])] + public function edit(Request $request, KoboDevice $koboDevice, EntityManagerInterface $entityManager): Response + { + if (!$this->isGranted('EDIT', $koboDevice)) { + throw $this->createAccessDeniedException('You don\'t have permission to edit this koboDevice'); + } + + $form = $this->createForm(KoboType::class, $koboDevice); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $entityManager->flush(); + + return $this->redirectToRoute('app_kobodevice_user_index', [], Response::HTTP_SEE_OTHER); + } + + return $this->render('kobodevice_user/edit.html.twig', [ + 'kobo' => $koboDevice, + 'form' => $form, + ]); + } + + #[Route('/{id}', name: 'app_kobodevice_user_delete', methods: ['POST'])] + public function delete(Request $request, KoboDevice $koboDevice, EntityManagerInterface $entityManager): Response + { + if (!$this->isGranted('DELETE', $koboDevice)) { + throw $this->createAccessDeniedException(); + } + + if ($this->isCsrfTokenValid('delete'.$koboDevice->getId(), (string) $request->request->get('_token'))) { + $entityManager->remove($koboDevice); + $entityManager->flush(); + } + + return $this->redirectToRoute('app_kobodevice_user_index', [], Response::HTTP_SEE_OTHER); + } +} diff --git a/src/Form/KoboType.php b/src/Form/KoboType.php index 47a3cc04..0a58555c 100644 --- a/src/Form/KoboType.php +++ b/src/Form/KoboType.php @@ -6,12 +6,17 @@ use App\Entity\Shelf; use App\Entity\User; use Symfony\Bridge\Doctrine\Form\Type\EntityType; +use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; class KoboType extends AbstractType { + public function __construct(protected Security $security) + { + } + public function buildForm(FormBuilderInterface $builder, array $options): void { $builder @@ -20,18 +25,20 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ->add('forceSync', null, [ 'label' => 'Force Sync', 'required' => false, - ]) - ->add('user', EntityType::class, [ + ]); + if ($this->security->isGranted('ROLE_ADMIN')) { + $builder->add('user', EntityType::class, [ 'class' => User::class, 'choice_label' => 'username', - ]) - ->add('shelves', EntityType::class, [ - 'label' => 'Sync with Shelves', - 'class' => Shelf::class, - 'choice_label' => 'name', - 'multiple' => true, - 'expanded' => true, - ]) + ]); + } + $builder->add('shelves', EntityType::class, [ + 'label' => 'Sync with Shelves', + 'class' => Shelf::class, + 'choice_label' => 'name', + 'multiple' => true, + 'expanded' => true, + ]) ; } diff --git a/src/Menu/MenuBuilder.php b/src/Menu/MenuBuilder.php index 5b7d3897..5e8ae014 100644 --- a/src/Menu/MenuBuilder.php +++ b/src/Menu/MenuBuilder.php @@ -79,6 +79,7 @@ public function createMainMenu(array $options): ItemInterface } $menu->addChild('profile_divider', ['label' => $user->getUsername()])->setExtra('divider', true); + $menu->addChild('Kobo Devices', ['route' => 'app_kobodevice_user_index', ...$this->defaultAttr])->setExtra('icon', 'gear-fill'); $menu->addChild('My profile', ['route' => 'app_user_profile', ...$this->defaultAttr])->setExtra('icon', 'person-circle'); $menu->addChild('My shelves', ['route' => 'app_shelf_crud_index', ...$this->defaultAttr])->setExtra('icon', 'bookshelf'); if ($user->isDisplayTimeline()) { diff --git a/src/Security/Voter/KoboDeviceVoter.php b/src/Security/Voter/KoboDeviceVoter.php new file mode 100644 index 00000000..482f2d64 --- /dev/null +++ b/src/Security/Voter/KoboDeviceVoter.php @@ -0,0 +1,45 @@ +getUser(); + // if the user is anonymous, do not grant access + if (!$user instanceof UserInterface) { + return false; + } + + if (!$subject instanceof KoboDevice) { + return false; + } + + if ($subject->getUser() === $user) { + return true; + } + + if (in_array('ROLE_ADMIN', $user->getRoles(), true)) { + return true; + } + + return false; + } +} diff --git a/templates/kobo_admin/_delete_form.html.twig b/templates/kobo_admin/_delete_form.html.twig deleted file mode 100644 index f1d33e82..00000000 --- a/templates/kobo_admin/_delete_form.html.twig +++ /dev/null @@ -1,4 +0,0 @@ -
- - -
diff --git a/templates/kobo_admin/edit.html.twig b/templates/kobo_admin/edit.html.twig deleted file mode 100644 index 086ce23d..00000000 --- a/templates/kobo_admin/edit.html.twig +++ /dev/null @@ -1,16 +0,0 @@ -{% extends 'base.html.twig' %} - -{% block title %}Edit Kobo{% endblock %} - -{% block body %} - {{ include('kobo_admin/_form.html.twig', {'button_label': 'Update'}) }} - - back to list - -
- {{ include('kobo_admin/_delete_form.html.twig') }} -
- - {{ include('kobo_admin/instructions.html.twig') }} - -{% endblock %} diff --git a/templates/kobo_admin/new.html.twig b/templates/kobo_admin/new.html.twig deleted file mode 100644 index 121e1055..00000000 --- a/templates/kobo_admin/new.html.twig +++ /dev/null @@ -1,14 +0,0 @@ -{% extends 'base.html.twig' %} - -{% block title %}Create new Kobo{% endblock %} - -{% block body %} - - {{ include('kobo_admin/_form.html.twig') }} - -
- back to list -
- - {{ include('kobo_admin/instructions.html.twig') }} -{% endblock %} diff --git a/templates/kobodevice_user/_delete_form.html.twig b/templates/kobodevice_user/_delete_form.html.twig new file mode 100644 index 00000000..72da122b --- /dev/null +++ b/templates/kobodevice_user/_delete_form.html.twig @@ -0,0 +1,6 @@ +{% if is_granted("DELETE", kobo) %} +
+ + +
+{% endif %} \ No newline at end of file diff --git a/templates/kobo_admin/_form.html.twig b/templates/kobodevice_user/_form.html.twig similarity index 100% rename from templates/kobo_admin/_form.html.twig rename to templates/kobodevice_user/_form.html.twig diff --git a/templates/kobodevice_user/edit.html.twig b/templates/kobodevice_user/edit.html.twig new file mode 100644 index 00000000..bfb7d3ac --- /dev/null +++ b/templates/kobodevice_user/edit.html.twig @@ -0,0 +1,16 @@ +{% extends 'base.html.twig' %} + +{% block title %}Edit Kobo{% endblock %} + +{% block body %} + {{ include('kobodevice_user/_form.html.twig', {'button_label': 'Update'}) }} + + back to list + +
+ {{ include('kobodevice_user/_delete_form.html.twig') }} +
+ + {{ include('kobodevice_user/instructions.html.twig') }} + +{% endblock %} diff --git a/templates/kobo_admin/index.html.twig b/templates/kobodevice_user/index.html.twig similarity index 59% rename from templates/kobo_admin/index.html.twig rename to templates/kobodevice_user/index.html.twig index 3c9f1adb..ab47191e 100644 --- a/templates/kobo_admin/index.html.twig +++ b/templates/kobodevice_user/index.html.twig @@ -19,8 +19,12 @@ {{ kobo.name }} {{ kobo.accessKey }} - show - edit + {% if is_granted('VIEW', kobo) %} + show + {% endif %} + {% if is_granted('EDIT', kobo) %} + edit + {% endif %} {% else %} @@ -31,8 +35,8 @@ - Create new + Create new - {{ include('kobo_admin/instructions.html.twig') }} + {{ include('kobodevice_user/instructions.html.twig') }} {% endblock %} diff --git a/templates/kobo_admin/instructions.html.twig b/templates/kobodevice_user/instructions.html.twig similarity index 100% rename from templates/kobo_admin/instructions.html.twig rename to templates/kobodevice_user/instructions.html.twig diff --git a/templates/kobodevice_user/new.html.twig b/templates/kobodevice_user/new.html.twig new file mode 100644 index 00000000..4f05f864 --- /dev/null +++ b/templates/kobodevice_user/new.html.twig @@ -0,0 +1,14 @@ +{% extends 'base.html.twig' %} + +{% block title %}Create new Kobo{% endblock %} + +{% block body %} + + {{ include('kobodevice_user/_form.html.twig') }} + +
+ back to list +
+ + {{ include('kobodevice_user/instructions.html.twig') }} +{% endblock %} diff --git a/templates/kobo_admin/show.html.twig b/templates/kobodevice_user/show.html.twig similarity index 62% rename from templates/kobo_admin/show.html.twig rename to templates/kobodevice_user/show.html.twig index feca1366..921892e2 100644 --- a/templates/kobo_admin/show.html.twig +++ b/templates/kobodevice_user/show.html.twig @@ -21,12 +21,12 @@ - back to list + back to list - edit + edit - {{ include('kobo_admin/_delete_form.html.twig') }} + {{ include('kobodevice_user/_delete_form.html.twig') }} - {% include('kobo_admin/instructions.html.twig') with {'token': kobo.accessKey} only %} + {% include('kobodevice_user/instructions.html.twig') with {'token': kobo.accessKey} only %} {% endblock %}