From 6597d5bd28c3175c8c2e297bdac3eee74b211698 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Fri, 19 Feb 2021 18:59:24 +0100 Subject: [PATCH] web: migrate Token List to web --- .../templates/administration/token/list.html | 102 ----------- authentik/admin/urls.py | 1 - authentik/admin/views/tokens.py | 33 +--- authentik/admin/views/users.py | 11 +- authentik/core/api/groups.py | 2 + authentik/core/api/tokens.py | 27 ++- authentik/core/api/users.py | 3 + authentik/flows/api.py | 2 + .../api/outpost_service_connections.py | 2 + authentik/policies/api.py | 1 + swagger.yaml | 172 +++++++++++++----- web/src/api/Tokens.ts | 46 ++++- web/src/elements/buttons/TokenCopyButton.ts | 4 +- web/src/elements/table/TablePagination.ts | 2 +- web/src/interfaces/AdminInterface.ts | 2 +- web/src/pages/tokens/TokenListPage.ts | 67 +++++++ web/src/routes.ts | 2 + 17 files changed, 285 insertions(+), 194 deletions(-) delete mode 100644 authentik/admin/templates/administration/token/list.html create mode 100644 web/src/pages/tokens/TokenListPage.ts diff --git a/authentik/admin/templates/administration/token/list.html b/authentik/admin/templates/administration/token/list.html deleted file mode 100644 index 9eb7667a4..000000000 --- a/authentik/admin/templates/administration/token/list.html +++ /dev/null @@ -1,102 +0,0 @@ -{% extends "administration/base.html" %} - -{% load i18n %} -{% load authentik_utils %} - -{% block content %} -
-
-

- - {% trans 'Tokens' %} -

-

{% trans "Tokens are used throughout authentik for Email validation stages, Recovery keys and API access." %}

-
-
-
-
- {% if object_list %} -
-
- {% include 'partials/toolbar_search.html' %} - {% include 'partials/pagination.html' %} -
-
- - - - - - - - - - - - {% for token in object_list %} - - - - - - - - {% endfor %} - -
{% trans 'Identifier' %}{% trans 'User' %}{% trans 'Expires?' %}{% trans 'Expiry Date' %}
-
{{ token.identifier }}
-
- - {{ token.user }} - - - - {{ token.expiring|yesno:"Yes,No" }} - - - - {% if not token.expiring %} - - - {% else %} - {{ token.expires }} - {% endif %} - - - - - {% trans 'Delete' %} - -
-
- - {% trans 'Copy token' %} - -
-
- {% include 'partials/pagination.html' %} -
- {% else %} -
-
- {% include 'partials/toolbar_search.html' %} -
-
-
-
- -

- {% trans 'No Tokens.' %} -

-
- {% if request.GET.search != "" %} - {% trans "Your search query doesn't match any token." %} - {% else %} - {% trans 'Currently no tokens exist.' %} - {% endif %} -
-
-
- {% endif %} -
-
-{% endblock %} diff --git a/authentik/admin/urls.py b/authentik/admin/urls.py index dc6516f18..60596e00a 100644 --- a/authentik/admin/urls.py +++ b/authentik/admin/urls.py @@ -53,7 +53,6 @@ urlpatterns = [ name="application-delete", ), # Tokens - path("tokens/", tokens.TokenListView.as_view(), name="tokens"), path( "tokens//delete/", tokens.TokenDeleteView.as_view(), diff --git a/authentik/admin/views/tokens.py b/authentik/admin/views/tokens.py index 126dac064..0dc6ce311 100644 --- a/authentik/admin/views/tokens.py +++ b/authentik/admin/views/tokens.py @@ -1,39 +1,12 @@ """authentik Token administration""" from django.contrib.auth.mixins import LoginRequiredMixin -from django.urls import reverse_lazy from django.utils.translation import gettext as _ -from django.views.generic import ListView -from guardian.mixins import PermissionListMixin, PermissionRequiredMixin +from guardian.mixins import PermissionRequiredMixin -from authentik.admin.views.utils import ( - DeleteMessageView, - SearchListMixin, - UserPaginateListMixin, -) +from authentik.admin.views.utils import DeleteMessageView from authentik.core.models import Token -class TokenListView( - LoginRequiredMixin, - PermissionListMixin, - UserPaginateListMixin, - SearchListMixin, - ListView, -): - """Show list of all tokens""" - - model = Token - permission_required = "authentik_core.view_token" - ordering = "expires" - template_name = "administration/token/list.html" - search_fields = [ - "identifier", - "intent", - "user__username", - "description", - ] - - class TokenDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessageView): """Delete token""" @@ -41,5 +14,5 @@ class TokenDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessage permission_required = "authentik_core.delete_token" template_name = "generic/delete.html" - success_url = reverse_lazy("authentik_admin:tokens") + success_url = "/" success_message = _("Successfully deleted Token") diff --git a/authentik/admin/views/users.py b/authentik/admin/views/users.py index bc6d673cc..daea36fe9 100644 --- a/authentik/admin/views/users.py +++ b/authentik/admin/views/users.py @@ -7,19 +7,14 @@ from django.contrib.auth.mixins import ( from django.contrib.messages.views import SuccessMessageMixin from django.http import HttpRequest, HttpResponse from django.http.response import HttpResponseRedirect -from django.shortcuts import redirect +from django.shortcuts import redirect, reverse from django.utils.http import urlencode from django.utils.translation import gettext as _ from django.views.generic import DetailView, UpdateView -from guardian.mixins import ( - PermissionRequiredMixin, -) +from guardian.mixins import PermissionRequiredMixin from authentik.admin.forms.users import UserForm -from authentik.admin.views.utils import ( - BackSuccessUrlMixin, - DeleteMessageView, -) +from authentik.admin.views.utils import BackSuccessUrlMixin, DeleteMessageView from authentik.core.models import Token, User from authentik.lib.views import CreateAssignPermView diff --git a/authentik/core/api/groups.py b/authentik/core/api/groups.py index fa1b8953d..152c1ea9f 100644 --- a/authentik/core/api/groups.py +++ b/authentik/core/api/groups.py @@ -19,3 +19,5 @@ class GroupViewSet(ModelViewSet): queryset = Group.objects.all() serializer_class = GroupSerializer + search_fields = ["name", "is_superuser"] + filterset_fields = ["name", "is_superuser"] diff --git a/authentik/core/api/tokens.py b/authentik/core/api/tokens.py index f30ba4f4b..aef414bff 100644 --- a/authentik/core/api/tokens.py +++ b/authentik/core/api/tokens.py @@ -9,6 +9,7 @@ from rest_framework.response import Response from rest_framework.serializers import ModelSerializer, Serializer from rest_framework.viewsets import ModelViewSet +from authentik.core.api.users import UserSerializer from authentik.core.models import Token from authentik.events.models import Event, EventAction @@ -16,10 +17,21 @@ from authentik.events.models import Event, EventAction class TokenSerializer(ModelSerializer): """Token Serializer""" + user = UserSerializer() + class Meta: model = Token - fields = ["pk", "identifier", "intent", "user", "description"] + fields = [ + "pk", + "identifier", + "intent", + "user", + "description", + "expires", + "expiring", + ] + depth = 2 class TokenViewSerializer(Serializer): @@ -40,6 +52,19 @@ class TokenViewSet(ModelViewSet): lookup_field = "identifier" queryset = Token.filter_not_expired() serializer_class = TokenSerializer + search_fields = [ + "identifier", + "intent", + "user__username", + "description", + ] + filterset_fields = [ + "identifier", + "intent", + "user__username", + "description", + ] + ordering = ["expires"] @swagger_auto_schema(responses={200: TokenViewSerializer(many=False)}) @action(detail=True) diff --git a/authentik/core/api/users.py b/authentik/core/api/users.py index 1159b3a7d..573e04cd1 100644 --- a/authentik/core/api/users.py +++ b/authentik/core/api/users.py @@ -37,6 +37,7 @@ class UserSerializer(ModelSerializer): "is_superuser", "email", "avatar", + "attributes", ] @@ -45,6 +46,8 @@ class UserViewSet(ModelViewSet): queryset = User.objects.none() serializer_class = UserSerializer + search_fields = ["username", "name", "is_active"] + filterset_fields = ["username", "name", "is_active"] def get_queryset(self): return User.objects.all().exclude(pk=get_anonymous_user().pk) diff --git a/authentik/flows/api.py b/authentik/flows/api.py index 4ca0ff982..7a88df19e 100644 --- a/authentik/flows/api.py +++ b/authentik/flows/api.py @@ -186,6 +186,8 @@ class StageViewSet(ReadOnlyModelViewSet): queryset = Stage.objects.all() serializer_class = StageSerializer + search_fields = ["name"] + filterset_fields = ["name"] def get_queryset(self): return Stage.objects.select_subclasses() diff --git a/authentik/outposts/api/outpost_service_connections.py b/authentik/outposts/api/outpost_service_connections.py index 986002b4d..1a5c7d947 100644 --- a/authentik/outposts/api/outpost_service_connections.py +++ b/authentik/outposts/api/outpost_service_connections.py @@ -61,6 +61,8 @@ class ServiceConnectionViewSet(ModelViewSet): queryset = OutpostServiceConnection.objects.select_subclasses() serializer_class = ServiceConnectionSerializer + search_fields = ["name"] + filterset_fields = ["name"] @swagger_auto_schema(responses={200: TypeCreateSerializer(many=True)}) @action(detail=False) diff --git a/authentik/policies/api.py b/authentik/policies/api.py index 703d4bf0f..9b480d6b6 100644 --- a/authentik/policies/api.py +++ b/authentik/policies/api.py @@ -107,6 +107,7 @@ class PolicyViewSet(ReadOnlyModelViewSet): "bindings": ["isnull"], "promptstage": ["isnull"], } + search_fields = ["name"] def get_queryset(self): return Policy.objects.select_subclasses().prefetch_related( diff --git a/swagger.yaml b/swagger.yaml index d4d762204..2cc4d87bd 100755 --- a/swagger.yaml +++ b/swagger.yaml @@ -309,6 +309,16 @@ paths: operationId: core_groups_list description: Group Viewset parameters: + - name: name + in: query + description: '' + required: false + type: string + - name: is_superuser + in: query + description: '' + required: false + type: string - name: ordering in: query description: Which field to use when ordering the results. @@ -436,6 +446,26 @@ paths: operationId: core_tokens_list description: Token Viewset parameters: + - name: identifier + in: query + description: '' + required: false + type: string + - name: intent + in: query + description: '' + required: false + type: string + - name: user__username + in: query + description: '' + required: false + type: string + - name: description + in: query + description: '' + required: false + type: string - name: ordering in: query description: Which field to use when ordering the results. @@ -582,6 +612,21 @@ paths: operationId: core_users_list description: User Viewset parameters: + - name: username + in: query + description: '' + required: false + type: string + - name: name + in: query + description: '' + required: false + type: string + - name: is_active + in: query + description: '' + required: false + type: string - name: ordering in: query description: Which field to use when ordering the results. @@ -649,6 +694,21 @@ paths: operationId: core_users_me description: Get information about current user parameters: + - name: username + in: query + description: '' + required: false + type: string + - name: name + in: query + description: '' + required: false + type: string + - name: is_active + in: query + description: '' + required: false + type: string - name: ordering in: query description: Which field to use when ordering the results. @@ -2107,6 +2167,11 @@ paths: operationId: outposts_service_connections_all_list description: ServiceConnection Viewset parameters: + - name: name + in: query + description: '' + required: false + type: string - name: ordering in: query description: Which field to use when ordering the results. @@ -2174,6 +2239,11 @@ paths: operationId: outposts_service_connections_all_types description: Get all creatable service connection types parameters: + - name: name + in: query + description: '' + required: false + type: string - name: ordering in: query description: Which field to use when ordering the results. @@ -5506,6 +5576,11 @@ paths: operationId: stages_all_list description: Stage Viewset parameters: + - name: name + in: query + description: '' + required: false + type: string - name: ordering in: query description: Which field to use when ordering the results. @@ -5557,6 +5632,11 @@ paths: operationId: stages_all_types description: Get all creatable stage types parameters: + - name: name + in: query + description: '' + required: false + type: string - name: ordering in: query description: Which field to use when ordering the results. @@ -8091,48 +8171,8 @@ definitions: attributes: title: Attributes type: object - Token: - description: Token Serializer - required: - - identifier - - user - type: object - properties: - pk: - title: Token uuid - type: string - format: uuid - readOnly: true - identifier: - title: Identifier - type: string - format: slug - pattern: ^[-a-zA-Z0-9_]+$ - maxLength: 255 - minLength: 1 - intent: - title: Intent - type: string - enum: - - verification - - api - - recovery - user: - title: User - type: integer - description: - title: Description - type: string - TokenView: - description: Show token's current key - type: object - properties: - key: - title: Key - type: string - readOnly: true - minLength: 1 User: + title: User description: User Serializer required: - username @@ -8179,6 +8219,56 @@ definitions: title: Avatar type: string readOnly: true + attributes: + title: Attributes + type: object + Token: + description: Token Serializer + required: + - identifier + - user + type: object + properties: + pk: + title: Token uuid + type: string + format: uuid + readOnly: true + identifier: + title: Identifier + type: string + format: slug + pattern: ^[-a-zA-Z0-9_]+$ + maxLength: 255 + minLength: 1 + intent: + title: Intent + type: string + enum: + - verification + - api + - recovery + user: + $ref: '#/definitions/User' + description: + title: Description + type: string + expires: + title: Expires + type: string + format: date-time + expiring: + title: Expiring + type: boolean + TokenView: + description: Show token's current key + type: object + properties: + key: + title: Key + type: string + readOnly: true + minLength: 1 CertificateKeyPair: description: CertificateKeyPair Serializer required: diff --git a/web/src/api/Tokens.ts b/web/src/api/Tokens.ts index 7a5e75d49..e1c6ab1b0 100644 --- a/web/src/api/Tokens.ts +++ b/web/src/api/Tokens.ts @@ -1,11 +1,43 @@ -import { DefaultClient } from "./Client"; +import { AKResponse, DefaultClient, QueryArguments } from "./Client"; +import { User } from "./Users"; -interface TokenResponse { - key: string; +export enum TokenIntent { + INTENT_VERIFICATION = "verification", + INTENT_API = "api", + INTENT_RECOVERY = "recovery", } -export function tokenByIdentifier(identifier: string): Promise { - return DefaultClient.fetch(["core", "tokens", identifier, "view_key"]).then( - (r) => r.key - ); +export class Token { + + pk: string; + identifier: string; + intent: TokenIntent; + user: User; + description: string; + + expires: number; + expiring: boolean; + + constructor() { + throw Error(); + } + + static get(pk: string): Promise { + return DefaultClient.fetch(["core", "tokens", pk]); + } + + static list(filter?: QueryArguments): Promise> { + return DefaultClient.fetch>(["core", "tokens"], filter); + } + + static adminUrl(rest: string): string { + return `/administration/tokens/${rest}`; + } + + static getKey(identifier: string): Promise { + return DefaultClient.fetch<{ key: string }>(["core", "tokens", identifier, "view_key"]).then( + (r) => r.key + ); + } + } diff --git a/web/src/elements/buttons/TokenCopyButton.ts b/web/src/elements/buttons/TokenCopyButton.ts index 5b68818e0..075469812 100644 --- a/web/src/elements/buttons/TokenCopyButton.ts +++ b/web/src/elements/buttons/TokenCopyButton.ts @@ -3,7 +3,7 @@ import { css, CSSResult, customElement, html, LitElement, property, TemplateResu import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css"; // @ts-ignore import ButtonStyle from "@patternfly/patternfly/components/Button/button.css"; -import { tokenByIdentifier } from "../../api/Tokens"; +import { Token } from "../../api/Tokens"; import { ColorStyles, ERROR_CLASS, PRIMARY_CLASS, SUCCESS_CLASS } from "../../constants"; @customElement("ak-token-copy-button") @@ -35,7 +35,7 @@ export class TokenCopyButton extends LitElement { }, 1500); return; } - tokenByIdentifier(this.identifier).then((token) => { + Token.getKey(this.identifier).then((token) => { navigator.clipboard.writeText(token).then(() => { this.buttonClass = SUCCESS_CLASS; setTimeout(() => { diff --git a/web/src/elements/table/TablePagination.ts b/web/src/elements/table/TablePagination.ts index 5ceea8604..fbcae2f27 100644 --- a/web/src/elements/table/TablePagination.ts +++ b/web/src/elements/table/TablePagination.ts @@ -43,7 +43,7 @@ export class TablePagination extends LitElement {