mirror of
https://github.com/eitchtee/WYGIWYH.git
synced 2026-07-10 23:02:47 +02:00
feat: add delete button for revoked API tokens
Revoked tokens previously stayed in the list with no way to remove them. Adds a delete action (hard delete, scoped to the owner, gated behind demo mode) shown on revoked rows, alongside the existing revoke action on active ones. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
from apps.users.models import APIToken
|
from apps.users.models import APIToken
|
||||||
|
|
||||||
@@ -43,3 +44,30 @@ class UserAPITokenViewsTests(TestCase):
|
|||||||
token.refresh_from_db()
|
token.refresh_from_db()
|
||||||
self.assertIsNotNone(token.revoked_at)
|
self.assertIsNotNone(token.revoked_at)
|
||||||
self.assertContains(response, "Revoked")
|
self.assertContains(response, "Revoked")
|
||||||
|
|
||||||
|
def test_can_delete_revoked_api_token(self):
|
||||||
|
token, _ = APIToken.objects.create_token(user=self.user, name="n8n")
|
||||||
|
token.revoked_at = timezone.now()
|
||||||
|
token.save(update_fields=["revoked_at"])
|
||||||
|
|
||||||
|
response = self.client.delete(
|
||||||
|
reverse("user_api_token_delete", kwargs={"token_id": token.id}),
|
||||||
|
**self.htmx_headers,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertFalse(APIToken.objects.filter(id=token.id).exists())
|
||||||
|
|
||||||
|
def test_cannot_delete_other_users_api_token(self):
|
||||||
|
other = get_user_model().objects.create_user(
|
||||||
|
email="other@example.com", password="test-password"
|
||||||
|
)
|
||||||
|
token, _ = APIToken.objects.create_token(user=other, name="theirs")
|
||||||
|
|
||||||
|
response = self.client.delete(
|
||||||
|
reverse("user_api_token_delete", kwargs={"token_id": token.id}),
|
||||||
|
**self.htmx_headers,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 404)
|
||||||
|
self.assertTrue(APIToken.objects.filter(id=token.id).exists())
|
||||||
|
|||||||
@@ -42,6 +42,11 @@ urlpatterns = [
|
|||||||
views.api_token_revoke,
|
views.api_token_revoke,
|
||||||
name="user_api_token_revoke",
|
name="user_api_token_revoke",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"user/api-tokens/<int:token_id>/delete/",
|
||||||
|
views.api_token_delete,
|
||||||
|
name="user_api_token_delete",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"users/",
|
"users/",
|
||||||
views.users_index,
|
views.users_index,
|
||||||
|
|||||||
@@ -168,6 +168,17 @@ def api_token_revoke(request, token_id):
|
|||||||
return _render_api_tokens(request)
|
return _render_api_tokens(request)
|
||||||
|
|
||||||
|
|
||||||
|
@only_htmx
|
||||||
|
@htmx_login_required
|
||||||
|
@disabled_on_demo
|
||||||
|
@require_http_methods(["DELETE"])
|
||||||
|
def api_token_delete(request, token_id):
|
||||||
|
token = get_object_or_404(APIToken, id=token_id, user=request.user)
|
||||||
|
token.delete()
|
||||||
|
messages.success(request, _("API token deleted successfully"))
|
||||||
|
return _render_api_tokens(request)
|
||||||
|
|
||||||
|
|
||||||
@only_htmx
|
@only_htmx
|
||||||
@htmx_login_required
|
@htmx_login_required
|
||||||
@require_http_methods(["GET"])
|
@require_http_methods(["GET"])
|
||||||
|
|||||||
@@ -87,6 +87,21 @@
|
|||||||
_="install prompt_swal">
|
_="install prompt_swal">
|
||||||
<i class="fa-solid fa-ban fa-fw"></i>
|
<i class="fa-solid fa-ban fa-fw"></i>
|
||||||
</a>
|
</a>
|
||||||
|
{% else %}
|
||||||
|
<a class="btn btn-error btn-sm"
|
||||||
|
role="button"
|
||||||
|
data-tippy-content="{% translate 'Delete' %}"
|
||||||
|
hx-delete="{% url 'user_api_token_delete' token_id=token.id %}"
|
||||||
|
hx-target="#api-token-settings"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
hx-trigger="confirmed"
|
||||||
|
data-bypass-on-ctrl="true"
|
||||||
|
data-title="{% translate 'Delete token?' %}"
|
||||||
|
data-text="{% translate 'This permanently removes the token from the list. It cannot be undone.' %}"
|
||||||
|
data-confirm-text="{% translate 'Yes, delete it!' %}"
|
||||||
|
_="install prompt_swal">
|
||||||
|
<i class="fa-solid fa-trash fa-fw"></i>
|
||||||
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
Reference in New Issue
Block a user