Allow to delete API tokens

This commit is contained in:
Jan Böhmer 2023-08-26 23:19:35 +02:00
parent 8fe3f4cf5c
commit 3e693642b6
3 changed files with 108 additions and 30 deletions

View file

@ -407,6 +407,8 @@ class UserSettingsController extends AbstractController
public function addApiToken(Request $request, EntityManagerInterface $entityManager): Response
{
$this->denyAccessUnlessGranted('@api.manage_tokens');
//When user change its settings, he should be logged in fully.
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
$token = new ApiToken();
$token->setUser($this->getUser());
@ -450,4 +452,43 @@ class UserSettingsController extends AbstractController
'secret' => $secret,
]);
}
#[Route(path: '/api_token/delete', name: 'user_api_tokens_delete', methods: ['DELETE'])]
public function apiTokenRemove(Request $request, EntityManagerInterface $entityManager): Response
{
$this->denyAccessUnlessGranted('@api.manage_tokens');
//When user change its settings, he should be logged in fully.
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
$user = $this->getUser();
if (!$user instanceof User) {
throw new RuntimeException('This controller only works only for Part-DB User objects!');
}
if (!$this->isCsrfTokenValid('delete'.$user->getID(), $request->request->get('_token'))) {
$this->addFlash('error', 'csfr_invalid');
return $this->redirectToRoute('user_settings');
}
//Extract the token id from the request
$token_id = $request->request->getInt('token_id');
$token = $entityManager->find(ApiToken::class, $token_id);
if ($token === null) {
$this->addFlash('error', 'tfa_u2f.u2f_delete.not_existing');
return $this->redirectToRoute('user_settings');
}
//User can only delete its own API tokens
if ($token->getUser() !== $user) {
$this->addFlash('error', 'tfa_u2f.u2f_delete.access_denied');
return $this->redirectToRoute('user_settings');
}
//Do the actual deletion
$entityManager->remove($token);
$entityManager->flush();
$this->addFlash('success', 'api_tokens.deleted');
return $this->redirectToRoute('user_settings');
}
}

View file

@ -21,38 +21,51 @@
<br><br>
<b>{% trans %}user.settings.api_tokens.no_api_tokens_yet{% endtrans %}</b>
{% else %}
<table class="table table-striped table-bordered table-hover table-sm mt-2">
<thead>
<tr>
<th>{% trans %}api_tokens.name{% endtrans %}</th>
<th>{% trans %}api_tokens.access_level{% endtrans %}</th>
<th>{% trans %}api_tokens.expiration_date{% endtrans %}</th>
<th>{% trans %}tfa_u2f.keys.added_date{% endtrans %}</th>
<th>{% trans %}api_tokens.last_time_used{% endtrans %}</th>
<th></th>
</tr>
</thead>
<tbody>
{% for api_token in user.apiTokens %}
{# @var api_token \App\Entity\UserSystem\ApiToken #}
<form action="{{ path('user_api_tokens_delete') }}" method="post"
{{ stimulus_controller('elements/delete_btn') }} {{ stimulus_action('elements/delete_btn', "submit", "submit") }}
data-delete-title="{% trans %}user.settings.api_tokens.delete.title{% endtrans %}"
data-delete-message="{% trans %}user.settings.api_tokens.delete.message{% endtrans %}">
<input type="hidden" name="_method" value="DELETE">
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ user.id) }}">
<table class="table table-striped table-bordered table-hover table-sm mt-2">
<thead>
<tr>
<td>{{ api_token.name }}</td>
<td>{{ api_token.level.translationKey|trans }}</td>
<td>
{{ _self.format_date(api_token.validUntil) }}
{% if api_token.valid %}
<span class="badge bg-success badge-success">{% trans %}api_token.valid{% endtrans %}</span>
{% else %}
<span class="badge bg-warning badge-warning">{% trans %}api_token.expired{% endtrans %}</span>
{% endif %}
</td>
<td>{{ _self.format_date(api_token.addedDate) }}</td>
<td>{{ _self.format_date(api_token.lastTimeUsed) }}</td>
<th>{% trans %}api_tokens.name{% endtrans %}</th>
<th>{% trans %}api_tokens.access_level{% endtrans %}</th>
<th>{% trans %}api_tokens.expiration_date{% endtrans %}</th>
<th>{% trans %}tfa_u2f.keys.added_date{% endtrans %}</th>
<th>{% trans %}api_tokens.last_time_used{% endtrans %}</th>
<th></th>
</tr>
{% endfor %}
</tbody>
</table>
</thead>
<tbody>
{% for api_token in user.apiTokens %}
{# @var api_token \App\Entity\UserSystem\ApiToken #}
<tr>
<td>{{ api_token.name }}</td>
<td>{{ api_token.level.translationKey|trans }}</td>
<td>
{{ _self.format_date(api_token.validUntil) }}
{% if api_token.valid %}
<span class="badge bg-success badge-success">{% trans %}api_token.valid{% endtrans %}</span>
{% else %}
<span class="badge bg-warning badge-warning">{% trans %}api_token.expired{% endtrans %}</span>
{% endif %}
</td>
<td>{{ _self.format_date(api_token.addedDate) }}</td>
<td>{{ _self.format_date(api_token.lastTimeUsed) }}</td>
<td>
<button type="submit" class="btn btn-danger btn-sm" name="token_id"
value="{{ api_token.id }}" {% if not is_granted('@api.manage_tokens') %}disabled="disabled"{% endif %}>
<i class="fas fa-trash-alt fa-fw"></i> {% trans %}user.settings.api_tokens.delete{% endtrans %}
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</form>
{% endif %}
<a href="{{ path('user_api_token_create') }}" class="btn btn-success" {% if not is_granted('@api.manage_tokens') %}disabled="disabled"{% endif %}>

View file

@ -11765,5 +11765,29 @@ Please note, that you can not impersonate a disabled user. If you try you will g
<target>Manage API tokens</target>
</segment>
</unit>
<unit id="dH8PTLd" name="user.settings.api_tokens.delete.title">
<segment>
<source>user.settings.api_tokens.delete.title</source>
<target>Do you really want to delete this API token?</target>
</segment>
</unit>
<unit id="1QAaz2a" name="user.settings.api_tokens.delete">
<segment>
<source>user.settings.api_tokens.delete</source>
<target>Delete</target>
</segment>
</unit>
<unit id="bE8wV70" name="user.settings.api_tokens.delete.message">
<segment>
<source>user.settings.api_tokens.delete.message</source>
<target>The application, which uses this API token, will no longer have access to Part-DB. This action can not be undone!</target>
</segment>
</unit>
<unit id="sLAzIhh" name="api_tokens.deleted">
<segment>
<source>api_tokens.deleted</source>
<target>API token deleted successfully!</target>
</segment>
</unit>
</file>
</xliff>