diff --git a/assets/controllers/elements/link_confirm_controller.js b/assets/controllers/elements/link_confirm_controller.js new file mode 100644 index 00000000..3d59b492 --- /dev/null +++ b/assets/controllers/elements/link_confirm_controller.js @@ -0,0 +1,72 @@ +/* + * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony). + * + * Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import {Controller} from "@hotwired/stimulus"; + +import * as bootbox from "bootbox"; +import "../../css/components/bootbox_extensions.css"; + +export default class extends Controller +{ + + static values = { + message: String, + title: String + } + + + + connect() + { + this._confirmed = false; + + this.element.addEventListener('click', this._onClick.bind(this)); + } + + _onClick(event) + { + + //If a user has not already confirmed the deletion, just let turbo do its work + if (this._confirmed) { + this._confirmed = false; + return; + } + + event.preventDefault(); + event.stopPropagation(); + + const that = this; + + bootbox.confirm({ + title: this.titleValue, + message: this.messageValue, + callback: (result) => { + if (result) { + //Set a flag to prevent the dialog from popping up again and allowing turbo to submit the form + that._confirmed = true; + + //Click the link + that.element.click(); + } else { + that._confirmed = false; + } + } + }); + } +} \ No newline at end of file diff --git a/src/EventSubscriber/UserSystem/PasswordChangeNeededSubscriber.php b/src/EventSubscriber/UserSystem/PasswordChangeNeededSubscriber.php index 0f12c6d9..b74c8c5f 100644 --- a/src/EventSubscriber/UserSystem/PasswordChangeNeededSubscriber.php +++ b/src/EventSubscriber/UserSystem/PasswordChangeNeededSubscriber.php @@ -78,6 +78,11 @@ final class PasswordChangeNeededSubscriber implements EventSubscriberInterface return; } + //If the user is impersonated, we don't need to redirect him + if ($this->security->isGranted('IS_IMPERSONATOR')) { + return; + } + //Abort if we dont need to redirect the user. if (!$user->isNeedPwChange() && !static::TFARedirectNeeded($user)) { return; diff --git a/src/Twig/UserExtension.php b/src/Twig/UserExtension.php index 1d5c6588..0a06ef2d 100644 --- a/src/Twig/UserExtension.php +++ b/src/Twig/UserExtension.php @@ -47,7 +47,9 @@ use App\Entity\LogSystem\AbstractLogEntry; use App\Repository\LogEntryRepository; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\SecurityBundle\Security; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken; +use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Twig\Extension\AbstractExtension; use Twig\TwigFilter; use Twig\TwigFunction; @@ -59,7 +61,9 @@ final class UserExtension extends AbstractExtension { private readonly LogEntryRepository $repo; - public function __construct(EntityManagerInterface $em, private readonly Security $security) + public function __construct(EntityManagerInterface $em, + private readonly Security $security, + private readonly UrlGeneratorInterface $urlGenerator) { $this->repo = $em->getRepository(AbstractLogEntry::class); } @@ -80,6 +84,7 @@ final class UserExtension extends AbstractExtension new TwigFunction('creating_user', fn(AbstractDBElement $element): ?User => $this->repo->getCreatingUser($element)), new TwigFunction('impersonator_user', $this->getImpersonatorUser(...)), new TwigFunction('impersonation_active', $this->isImpersonationActive(...)), + new TwigFunction('impersonation_path', $this->getImpersonationPath(...)), ]; } @@ -103,6 +108,15 @@ final class UserExtension extends AbstractExtension return $this->security->isGranted('IS_IMPERSONATOR'); } + public function getImpersonationPath(User $user, string $route_name = 'homepage'): string + { + if (! $this->security->isGranted('CAN_SWITCH_USER', $user)) { + throw new AccessDeniedException('You are not allowed to impersonate this user!'); + } + + return $this->urlGenerator->generate($route_name, ['_switch_user' => $user->getUsername()]); + } + /** * This function/filter generates a path. */ diff --git a/templates/admin/base_admin.html.twig b/templates/admin/base_admin.html.twig index 72a6ec60..51790c3c 100644 --- a/templates/admin/base_admin.html.twig +++ b/templates/admin/base_admin.html.twig @@ -77,21 +77,22 @@ {{ form_start(form) }} - - + {% endblock %}
@@ -183,12 +184,12 @@
-
-
-

{% trans %}mass_creation.help{% endtrans %}

-
- {{ form(mass_creation_form) }} +
+
+

{% trans %}mass_creation.help{% endtrans %}

+ {{ form(mass_creation_form) }} +
{% endif %} diff --git a/templates/admin/user_admin.html.twig b/templates/admin/user_admin.html.twig index 2ab08c97..772b42d9 100644 --- a/templates/admin/user_admin.html.twig +++ b/templates/admin/user_admin.html.twig @@ -16,6 +16,20 @@ {% endblock %} +{% block nav_pills_container %} + +
+ {{ parent() }} +
+ {% if entity.id is not null and is_granted('CAN_SWITCH_USER', entity) %} + {% trans %}user.impersonate.btn{% endtrans %} + {% endif %} +
+
+{% endblock %} + {% block additional_controls %} {{ form_row(form.group) }} {{ form_row(form.first_name) }} @@ -24,12 +38,6 @@ {{ form_row(form.showEmailOnProfile) }} {{ form_row(form.department) }} {{ form_row(form.aboutMe) }} - - - - {% endblock %} {% block additional_panes %} diff --git a/translations/messages.en.xlf b/translations/messages.en.xlf index e6533ba7..c00b68c9 100644 --- a/translations/messages.en.xlf +++ b/translations/messages.en.xlf @@ -11409,5 +11409,25 @@ Element 3 Stop impersonation + + + user.impersonate.btn + Impersonate + + + + + user.impersonate.confirm.title + Do you really want to impersonate this user? + + + + + user.impersonate.confirm.message + This will be logged. You should only do this with a good reason. + +Please note, that you can not impersonate a disabled user. If you try you will get an "Access Denied" message. + +