diff --git a/app/config/security.yml b/app/config/security.yml index 474774d41..76f10db70 100644 --- a/app/config/security.yml +++ b/app/config/security.yml @@ -76,6 +76,5 @@ security: - { path: ^/settings, roles: ROLE_SUPER_ADMIN } - { path: ^/annotations, roles: ROLE_USER } - { path: ^/2fa, role: IS_AUTHENTICATED_2FA_IN_PROGRESS } - - { path: ^/users, roles: ROLE_SUPER_ADMIN } - { path: ^/ignore-origin-instance-rules, roles: ROLE_SUPER_ADMIN } - { path: ^/, roles: ROLE_USER } diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index 49b914215..f9fba7b07 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -10,6 +10,7 @@ use Pagerfanta\Doctrine\ORM\QueryAdapter as DoctrineORMAdapter; use Pagerfanta\Exception\OutOfRangeCurrentPageException; use Pagerfanta\Pagerfanta; use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Google\GoogleAuthenticatorInterface; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Form\Form; use Symfony\Component\Form\FormInterface; @@ -41,6 +42,7 @@ class UserController extends AbstractController * Creates a new User entity. * * @Route("/users/new", name="user_new", methods={"GET", "POST"}) + * @IsGranted("CREATE_USERS") */ public function newAction(Request $request, UserManagerInterface $userManager, EventDispatcherInterface $eventDispatcher) { @@ -77,6 +79,7 @@ class UserController extends AbstractController * Displays a form to edit an existing User entity. * * @Route("/users/{id}/edit", name="user_edit", methods={"GET", "POST"}) + * @IsGranted("EDIT", subject="user") */ public function editAction(Request $request, User $user, UserManagerInterface $userManager, GoogleAuthenticatorInterface $googleAuthenticator) { @@ -119,6 +122,7 @@ class UserController extends AbstractController * Deletes a User entity. * * @Route("/users/{id}", name="user_delete", methods={"DELETE"}) + * @IsGranted("DELETE", subject="user") */ public function deleteAction(Request $request, User $user) { @@ -142,6 +146,7 @@ class UserController extends AbstractController * @param int $page * * @Route("/users/list/{page}", name="user_index", defaults={"page" = 1}) + * @IsGranted("LIST_USERS") * * Default parameter for page is hardcoded (in duplication of the defaults from the Route) * because this controller is also called inside the layout template without any page as argument diff --git a/src/Security/Voter/AdminVoter.php b/src/Security/Voter/AdminVoter.php new file mode 100644 index 000000000..2351ec4cc --- /dev/null +++ b/src/Security/Voter/AdminVoter.php @@ -0,0 +1,47 @@ +security = $security; + } + + protected function supports(string $attribute, $subject): bool + { + if (!\in_array($attribute, [self::LIST_USERS, self::CREATE_USERS], true)) { + return false; + } + + return true; + } + + protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool + { + $user = $token->getUser(); + + if (!$user instanceof User) { + return false; + } + + switch ($attribute) { + case self::LIST_USERS: + case self::CREATE_USERS: + return $this->security->isGranted('ROLE_SUPER_ADMIN'); + } + + return false; + } +} diff --git a/src/Security/Voter/UserVoter.php b/src/Security/Voter/UserVoter.php new file mode 100644 index 000000000..876f29aff --- /dev/null +++ b/src/Security/Voter/UserVoter.php @@ -0,0 +1,53 @@ +security = $security; + } + + protected function supports(string $attribute, $subject): bool + { + if (!$subject instanceof User) { + return false; + } + + if (!\in_array($attribute, [self::EDIT, self::DELETE], true)) { + return false; + } + + return true; + } + + protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool + { + $user = $token->getUser(); + \assert($user instanceof User); + + switch ($attribute) { + case self::EDIT: + return $this->security->isGranted('ROLE_SUPER_ADMIN'); + case self::DELETE: + if ($user === $subject) { + return false; + } + + return $this->security->isGranted('ROLE_SUPER_ADMIN'); + } + + return false; + } +} diff --git a/templates/User/edit.html.twig b/templates/User/edit.html.twig index 1181c60bc..7bf52f77a 100644 --- a/templates/User/edit.html.twig +++ b/templates/User/edit.html.twig @@ -66,9 +66,13 @@ {{ form_widget(edit_form._token) }}
- {{ form_start(delete_form) }} - - {{ form_end(delete_form) }} + {% if is_granted('DELETE', user) %} + {{ form_start(delete_form) }} + + {{ form_end(delete_form) }} + {% else %} + + {% endif %}
{{ 'user.form.back_to_list'|trans }}
diff --git a/templates/User/index.html.twig b/templates/User/index.html.twig index 91ab41abd..20c037590 100644 --- a/templates/User/index.html.twig +++ b/templates/User/index.html.twig @@ -15,21 +15,23 @@{{ 'user.description'|trans|raw }}
{{ user.email }} | {% if user.lastLogin %}{{ user.lastLogin|date('Y-m-d H:i:s') }}{% endif %} | - {{ 'user.list.edit_action'|trans }} + {% if is_granted('EDIT', user) %} + {{ 'user.list.edit_action'|trans }} + {% endif %} | {% endfor %}
- {{ 'user.list.create_new_one'|trans }} -
+ {% if is_granted('CREATE_USERS') %} ++ {{ 'user.list.create_new_one'|trans }} +
+ {% endif %} {% if users.getNbPages > 1 %} {{ pagerfanta(users, 'default_wallabag') }} {% endif %} diff --git a/templates/layout.html.twig b/templates/layout.html.twig index bf2753049..5acca264a 100644 --- a/templates/layout.html.twig +++ b/templates/layout.html.twig @@ -124,8 +124,10 @@