diff --git a/config/packages/security.yaml b/config/packages/security.yaml index d0c65fb..7400747 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -21,6 +21,7 @@ security: provider: bpn_user_provider custom_authenticators: - App\Security\BpnAuthenticator + user_checker: App\Security\UserChecker switch_user: role: CAN_IMPERSONATE logout: diff --git a/migrations/Version20250722142834.php b/migrations/Version20250722142834.php new file mode 100644 index 0000000..0b7cfe6 --- /dev/null +++ b/migrations/Version20250722142834.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE user ADD disabled_at DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\', ADD disabled_reason LONGTEXT DEFAULT NULL, ADD disabled_reason_internal LONGTEXT DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE user DROP disabled_at, DROP disabled_reason, DROP disabled_reason_internal'); + } +} diff --git a/src/Controller/Admin/Teamer/DisableUserController.php b/src/Controller/Admin/Teamer/DisableUserController.php new file mode 100644 index 0000000..aa9d331 --- /dev/null +++ b/src/Controller/Admin/Teamer/DisableUserController.php @@ -0,0 +1,81 @@ +getUser(); + $form = $this->createForm(DisableUserType::class, $user, ['hx_post' => $request->getUri()]); + $form->handleRequest($request); + if ($form->isSubmitted() && $form->isValid()) { + $user->setDisabledAt(new \DateTimeImmutable()); + + $this->entityManager->flush(); + + $this->addFlash('success', 'Der Benutzeraccount wurde gesperrt'); + + $this->logger->info('Disable user', [ + 'teamer' => $teamer->getFullName(), + 'user' => $user->getEmail(), + ]); + + return new HxRedirectResponse($this->generateUrl('app_administrative_teamer_index')); + } + + return $this->render('admin/teamer/modal_disable_user.html.twig', [ + 'form' => $form->createView(), + 'teamer' => $teamer, + ]); + } + + #[Route('/administrative/teamer/enable-user/{uuid}', name: 'app_admin_teamer_enable_user')] + #[IsGranted('ROLE_ADMIN')] + public function enable(Teamer $teamer, Request $request): Response + { + $user = $teamer->getUser(); + + if (true === $request->isMethod(Request::METHOD_POST)) { + $user + ->setDisabledAt(null) + ->setDisabledReason(null) + ->setDisabledReasonInternal(null) + ; + + $this->entityManager->flush(); + + $this->addFlash('success', 'Der Benutzeraccount wurde reaktiviert'); + + $this->logger->info('Enable user', [ + 'teamer' => $teamer->getFullName(), + 'user' => $user->getEmail(), + ]); + + return new HxRedirectResponse($this->generateUrl('app_administrative_teamer_index')); + } + + return $this->render('admin/teamer/modal_enable_user.html.twig', [ + 'teamer' => $teamer, + ]); + } +} diff --git a/src/Entity/User.php b/src/Entity/User.php index dce5872..385eff1 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -4,6 +4,7 @@ namespace App\Entity; use App\Entity\Traits\TimestampableEntity; use App\Repository\UserRepository; +use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Uid\Uuid; @@ -57,6 +58,15 @@ class User implements UserInterface, TimestampableEntityInterface #[ORM\Column] private bool $muteNotifications = false; + #[ORM\Column(type: 'datetime_immutable', nullable: true)] + private ?\DateTimeImmutable $disabledAt = null; + + #[ORM\Column(type: Types::TEXT, nullable: true)] + private ?string $disabledReason = null; + + #[ORM\Column(type: Types::TEXT, nullable: true)] + private ?string $disabledReasonInternal = null; + public function __construct() { $this->uuid = Uuid::v4(); @@ -169,7 +179,7 @@ class User implements UserInterface, TimestampableEntityInterface $labels = []; foreach ($this->roles as $role) { - $labels[] = match($role) { + $labels[] = match ($role) { 'ROLE_ADMIN' => 'Admin', 'ROLE_MANAGER' => 'Reisemanager', 'ROLE_HOUSE_MANAGER' => 'Hausleitung', @@ -300,4 +310,45 @@ class User implements UserInterface, TimestampableEntityInterface return false; } + + public function getDisabledAt(): ?\DateTimeImmutable + { + return $this->disabledAt; + } + + public function setDisabledAt(?\DateTimeImmutable $disabledAt): static + { + $this->disabledAt = $disabledAt; + + return $this; + } + + public function isDisabled(): bool + { + return null !== $this->disabledAt; + } + + public function getDisabledReason(): ?string + { + return $this->disabledReason; + } + + public function setDisabledReason(?string $disabledReason): static + { + $this->disabledReason = $disabledReason; + + return $this; + } + + public function getDisabledReasonInternal(): ?string + { + return $this->disabledReasonInternal; + } + + public function setDisabledReasonInternal(?string $disabledReasonInternal): static + { + $this->disabledReasonInternal = $disabledReasonInternal; + + return $this; + } } diff --git a/src/Form/DisableUserType.php b/src/Form/DisableUserType.php new file mode 100644 index 0000000..f537869 --- /dev/null +++ b/src/Form/DisableUserType.php @@ -0,0 +1,46 @@ +add('disabledReason', TextareaType::class, [ + 'label' => 'Begründung', + 'attr' => [ + 'data-controller' => 'textarea-autosize', + 'data-action' => 'textarea-autosize#resize', + ], + 'constraints' => [ + new NotBlank([ + 'message' => 'Bitte gib die Begründung ein', + ]), + ], + ]) + ->add('disabledReasonInternal', TextareaType::class, [ + 'label' => 'Begründung intern', + 'required' => false, + 'attr' => [ + 'data-controller' => 'textarea-autosize', + 'data-action' => 'textarea-autosize#resize', + ], + ]) + ; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => User::class, + ]); + } +} diff --git a/src/Security/UserChecker.php b/src/Security/UserChecker.php new file mode 100644 index 0000000..93baa21 --- /dev/null +++ b/src/Security/UserChecker.php @@ -0,0 +1,26 @@ +isDisabled()) { + throw new CustomUserMessageAccountStatusException('Dein Account wurde gesperrt: '.$user->getDisabledReason()); + } + } + + public function checkPostAuth(UserInterface $user): void + { + } +} diff --git a/templates/admin/teamer/modal_disable_user.html.twig b/templates/admin/teamer/modal_disable_user.html.twig new file mode 100644 index 0000000..7b43483 --- /dev/null +++ b/templates/admin/teamer/modal_disable_user.html.twig @@ -0,0 +1,19 @@ +{% extends 'htmx_modal.html.twig' %} + +{% block title %}Benutzeraccount sperren{% endblock %} + +{% block content %} + {{ form_start(form) }} +