Skip to content

Commit

Permalink
new: Revoke users authorizations
Browse files Browse the repository at this point in the history
  • Loading branch information
marien-probesys committed Feb 3, 2023
1 parent 79cfab2 commit f8a355a
Show file tree
Hide file tree
Showing 7 changed files with 216 additions and 1 deletion.
49 changes: 49 additions & 0 deletions src/Controller/Users/AuthorizationsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
namespace App\Controller\Users;

use App\Controller\BaseController;
use App\Entity\Authorization;
use App\Entity\User;
use App\Repository\AuthorizationRepository;
use App\Repository\OrganizationRepository;
Expand Down Expand Up @@ -188,4 +189,52 @@ public function create(
'uid' => $user->getUid(),
]);
}

#[Route('/authorizations/{uid}/deletion', name: 'delete user authorization', methods: ['POST'])]
public function delete(
Authorization $authorization,
Request $request,
AuthorizationRepository $authorizationRepository,
Security $security,
): Response {
$this->denyAccessUnlessGranted('admin:manage:users');

/** @var \App\Entity\User $currentUser */
$currentUser = $this->getUser();

/** @var string $csrfToken */
$csrfToken = $request->request->get('_csrf_token', '');

$user = $authorization->getHolder();
$role = $authorization->getRole();

if (!$this->isCsrfTokenValid('delete user authorization', $csrfToken)) {
$this->addFlash('error', $this->csrfError());
return $this->redirectToRoute('user authorizations', [
'uid' => $user->getUid(),
]);
}

if (
$role->getType() === 'super' && (
!$security->isGranted('admin:*') ||
$currentUser->getId() === $user->getId()
)
) {
$this->addFlash('error', new TranslatableMessage(
'You can’t revoke this authorization.',
[],
'validators'
));
return $this->redirectToRoute('user authorizations', [
'uid' => $user->getUid(),
]);
}

$authorizationRepository->remove($authorization, true);

return $this->redirectToRoute('user authorizations', [
'uid' => $user->getUid(),
]);
}
}
24 changes: 23 additions & 1 deletion templates/users/authorizations/index.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
</a>

{% for authorization in authorizations %}
<div class="card" data-test="authorization-item">
<div class="card flow" data-test="authorization-item">
<div class="card__title">
{% if authorization.role.type == 'super' %}
{{ icon('crown') }}
Expand Down Expand Up @@ -61,6 +61,28 @@
</div>
{% endif %}
</div>

{% if (
authorization.role.type == 'super' and (
not is_granted('admin:*') or
app.user.id == authorization.holder.id
)
) %}
<p class="text--secondary text--center text--small">
{{ 'users.authorizations.index.revoke.disabled' | trans }}
</p>
{% else %}
<form
class="text--center"
method="post"
action="{{ path('delete user authorization', { uid: authorization.uid }) }}"
>
<input type="hidden" name="_csrf_token" value="{{ csrf_token('delete user authorization') }}">
<button type="submit" data-turbo-confirm="{{ 'users.authorizations.index.revoke.confirm' | trans }}">
{{ 'users.authorizations.index.revoke' | trans }}
</button>
</form>
{% endif %}
</div>
{% endfor %}
</div>
Expand Down
136 changes: 136 additions & 0 deletions tests/Controller/Users/AuthorizationsControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -330,4 +330,140 @@ public function testPostCreateFailsIfAccessIsForbidden(): void
'role' => $role->getUid(),
]);
}

public function testPostDeleteDeletesAuthorizationAndRedirects(): void
{
$client = static::createClient();
$currentUser = UserFactory::createOne();
$client->loginUser($currentUser->object());
$this->grantAdmin($currentUser->object(), ['admin:manage:users']);
$user = UserFactory::createOne();
$role = RoleFactory::createOne([
'type' => 'admin',
]);
$authorization = AuthorizationFactory::createOne([
'holder' => $user,
'role' => $role,
]);

$client->request('POST', "/authorizations/{$authorization->getUid()}/deletion", [
'_csrf_token' => $this->generateCsrfToken($client, 'delete user authorization'),
]);

$this->assertResponseRedirects("/users/{$user->getUid()}/authorizations", 302);
AuthorizationFactory::assert()->notExists(['id' => $authorization->getId()]);
}

public function testPostDeleteCanDeleteSuperRole(): void
{
$client = static::createClient();
$currentUser = UserFactory::createOne();
$client->loginUser($currentUser->object());
$this->grantAdmin($currentUser->object(), ['admin:*']);
$user = UserFactory::createOne();
$role = RoleFactory::createOne([
'type' => 'super',
]);
$authorization = AuthorizationFactory::createOne([
'holder' => $user,
'role' => $role,
]);

$client->request('POST', "/authorizations/{$authorization->getUid()}/deletion", [
'_csrf_token' => $this->generateCsrfToken($client, 'delete user authorization'),
]);

$this->assertResponseRedirects("/users/{$user->getUid()}/authorizations", 302);
AuthorizationFactory::assert()->notExists(['id' => $authorization->getId()]);
}

public function testPostDeleteFailsIfSuperRoleAndNotCorrectAuthorization(): void
{
$client = static::createClient();
$currentUser = UserFactory::createOne();
$client->loginUser($currentUser->object());
$this->grantAdmin($currentUser->object(), ['admin:manage:users']);
$user = UserFactory::createOne();
$role = RoleFactory::createOne([
'type' => 'super',
]);
$authorization = AuthorizationFactory::createOne([
'holder' => $user,
'role' => $role,
]);

$client->request('POST', "/authorizations/{$authorization->getUid()}/deletion", [
'_csrf_token' => $this->generateCsrfToken($client, 'delete user authorization'),
]);

$this->assertResponseRedirects("/users/{$user->getUid()}/authorizations", 302);
$client->followRedirect();
$this->assertSelectorTextContains('#notifications', 'You can’t revoke this authorization.');
AuthorizationFactory::assert()->exists(['id' => $authorization->getId()]);
}

public function testPostDeleteFailsIfSuperRoleAndCurrentUser(): void
{
$client = static::createClient();
$currentUser = UserFactory::createOne();
$client->loginUser($currentUser->object());
$this->grantAdmin($currentUser->object(), ['admin:*']);
$authorization = AuthorizationFactory::last();

$client->request('POST', "/authorizations/{$authorization->getUid()}/deletion", [
'_csrf_token' => $this->generateCsrfToken($client, 'delete user authorization'),
]);

$this->assertResponseRedirects("/users/{$currentUser->getUid()}/authorizations", 302);
$client->followRedirect();
$this->assertSelectorTextContains('#notifications', 'You can’t revoke this authorization.');
AuthorizationFactory::assert()->exists(['id' => $authorization->getId()]);
}

public function testPostDeleteFailsIfCsrfTokenIsInvalid(): void
{
$client = static::createClient();
$currentUser = UserFactory::createOne();
$client->loginUser($currentUser->object());
$this->grantAdmin($currentUser->object(), ['admin:manage:users']);
$user = UserFactory::createOne();
$role = RoleFactory::createOne([
'type' => 'admin',
]);
$authorization = AuthorizationFactory::createOne([
'holder' => $user,
'role' => $role,
]);

$client->request('POST', "/authorizations/{$authorization->getUid()}/deletion", [
'_csrf_token' => 'not a token',
]);

$this->assertResponseRedirects("/users/{$user->getUid()}/authorizations", 302);
$client->followRedirect();
$this->assertSelectorTextContains('#notifications', 'Invalid CSRF token.');
AuthorizationFactory::assert()->exists(['id' => $authorization->getId()]);
}

public function testPostDeleteFailsIfAccessIsForbidden(): void
{
$this->expectException(AccessDeniedException::class);

$client = static::createClient();
$currentUser = UserFactory::createOne();
$client->loginUser($currentUser->object());
$user = UserFactory::createOne();
$role = RoleFactory::createOne([
'type' => 'admin',
]);
$authorization = AuthorizationFactory::createOne([
'holder' => $user,
'role' => $role,
]);

$client->catchExceptions(false);
$client->request('POST', "/authorizations/{$authorization->getUid()}/deletion", [
'_csrf_token' => $this->generateCsrfToken($client, 'delete user authorization'),
]);
}
}
3 changes: 3 additions & 0 deletions translations/messages+intl-icu.en_GB.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ users.color_scheme.dark: Dark
users.email: 'Email address'
users.authorizations.index.new_authorization: 'Give an authorization'
users.authorizations.index.no_authorizations: 'No authorizations'
users.authorizations.index.revoke: Revoke
users.authorizations.index.revoke.confirm: 'Are you sure that you want to revoke this authorization?'
users.authorizations.index.revoke.disabled: 'You can’t revoke this authorization.'
users.authorizations.index.title: 'Authorizations ({name})'
users.authorizations.new.global: Global
users.authorizations.new.organization: 'Choose an organization'
Expand Down
3 changes: 3 additions & 0 deletions translations/messages+intl-icu.fr_FR.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ users.color_scheme.dark: Sombre
users.email: 'Adresse email'
users.authorizations.index.new_authorization: 'Donner une autorisation'
users.authorizations.index.no_authorizations: 'Aucune autorisation'
users.authorizations.index.revoke: Révoquer
users.authorizations.index.revoke.confirm: "Êtes-vous sûr de vouloir révoquer cette autorisation\_?"
users.authorizations.index.revoke.disabled: 'Vous ne pouvez pas révoquer cette autorisation.'
users.authorizations.index.title: 'Autorisations ({name})'
users.authorizations.new.global: Global
users.authorizations.new.organization: 'Choisir une organisation'
Expand Down
1 change: 1 addition & 0 deletions translations/validators+intl-icu.en_GB.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,4 @@ Error: Error
'This user already has an admin role.': 'This user already has an admin role.'
'This user already has an orga role for this organization.': 'This user already has an orga role for this organization.'
'You can’t grant super-admin authorization.': 'You can’t grant super-admin authorization.'
'You can’t revoke this authorization.': 'You can’t revoke this authorization.'
1 change: 1 addition & 0 deletions translations/validators+intl-icu.fr_FR.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,4 @@ Error: Erreur
'This user already has an admin role.': 'Cet utilisateur possède déjà un rôle admin.'
'This user already has an orga role for this organization.': 'Cet utilisateur possède déjà un rôle orga pour cette organisation.'
'You can’t grant super-admin authorization.': 'Vous ne pouvez pas accorder de droit super-admin.'
'You can’t revoke this authorization.': 'Vous ne pouvez pas révoquer cette autorisation.'

0 comments on commit f8a355a

Please sign in to comment.