diff --git a/authentik/admin/templates/administration/group/list.html b/authentik/admin/templates/administration/group/list.html deleted file mode 100644 index 3d1b8eb0f..000000000 --- a/authentik/admin/templates/administration/group/list.html +++ /dev/null @@ -1,114 +0,0 @@ -{% extends "administration/base.html" %} - -{% load i18n %} - -{% block content %} -
-
-

- - {% trans 'Groups' %} -

-

{% trans "Group users together and give them permissions based on the membership." %} -

-
-
-
-
- {% if object_list %} -
-
- {% include 'partials/toolbar_search.html' %} -
- - - {% trans 'Create' %} - -
-
- -
- {% include 'partials/pagination.html' %} -
-
- - - - - - - - - - - {% for group in object_list %} - - - - - - - {% endfor %} - -
{% trans 'Name' %}{% trans 'Parent' %}{% trans 'Members' %}
- - {{ group.name }} - - - - {{ group.parent }} - - - - {{ group.users.all|length }} - - - - - {% trans 'Edit' %} - -
-
- - - {% trans 'Delete' %} - -
-
-
-
- {% include 'partials/pagination.html' %} -
- {% else %} -
-
- {% include 'partials/toolbar_search.html' %} -
-
-
-
- -

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

-
- {% if request.GET.search != "" %} - {% trans "Your search query doesn't match any groups." %} - {% else %} - {% trans 'Currently no group exist. Click the button below to create one.' %} - {% endif %} -
- - - {% trans 'Create' %} - -
-
-
-
- {% endif %} -
-
-{% endblock %} diff --git a/authentik/admin/urls.py b/authentik/admin/urls.py index b0f0aece6..d2deda6e9 100644 --- a/authentik/admin/urls.py +++ b/authentik/admin/urls.py @@ -269,7 +269,6 @@ urlpatterns = [ name="user-password-reset", ), # Groups - path("groups/", groups.GroupListView.as_view(), name="groups"), path("groups/create/", groups.GroupCreateView.as_view(), name="group-create"), path( "groups//update/", diff --git a/authentik/admin/views/groups.py b/authentik/admin/views/groups.py index bebd3bdb1..462fd87ba 100644 --- a/authentik/admin/views/groups.py +++ b/authentik/admin/views/groups.py @@ -4,38 +4,16 @@ from django.contrib.auth.mixins import ( PermissionRequiredMixin as DjangoPermissionRequiredMixin, ) from django.contrib.messages.views import SuccessMessageMixin -from django.urls import reverse_lazy from django.utils.translation import gettext as _ -from django.views.generic import ListView, UpdateView -from guardian.mixins import PermissionListMixin, PermissionRequiredMixin +from django.views.generic import UpdateView +from guardian.mixins import PermissionRequiredMixin -from authentik.admin.views.utils import ( - BackSuccessUrlMixin, - DeleteMessageView, - SearchListMixin, - UserPaginateListMixin, -) +from authentik.admin.views.utils import BackSuccessUrlMixin, DeleteMessageView from authentik.core.forms.groups import GroupForm from authentik.core.models import Group from authentik.lib.views import CreateAssignPermView -class GroupListView( - LoginRequiredMixin, - PermissionListMixin, - UserPaginateListMixin, - SearchListMixin, - ListView, -): - """Show list of all groups""" - - model = Group - permission_required = "authentik_core.view_group" - ordering = "name" - template_name = "administration/group/list.html" - search_fields = ["name", "attributes"] - - class GroupCreateView( SuccessMessageMixin, BackSuccessUrlMixin, @@ -50,7 +28,7 @@ class GroupCreateView( permission_required = "authentik_core.add_group" template_name = "generic/create.html" - success_url = reverse_lazy("authentik_admin:groups") + success_url = "/" success_message = _("Successfully created Group") @@ -68,7 +46,7 @@ class GroupUpdateView( permission_required = "authentik_core.change_group" template_name = "generic/update.html" - success_url = reverse_lazy("authentik_admin:groups") + success_url = "/" success_message = _("Successfully updated Group") @@ -79,5 +57,5 @@ class GroupDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessage permission_required = "authentik_flows.delete_group" template_name = "generic/delete.html" - success_url = reverse_lazy("authentik_admin:groups") + success_url = "/" success_message = _("Successfully deleted Group") diff --git a/web/src/api/Groups.ts b/web/src/api/Groups.ts index 36b73b48a..1d85c05b0 100644 --- a/web/src/api/Groups.ts +++ b/web/src/api/Groups.ts @@ -1,15 +1,28 @@ +import { DefaultClient, QueryArguments, AKResponse } from "./Client"; import { EventContext } from "./Events"; export class Group { - group_uuid: string; + pk: string; name: string; is_superuser: boolean; attributes: EventContext; parent?: Group; + users: number[]; constructor() { throw Error(); } + static get(pk: string): Promise { + return DefaultClient.fetch(["core", "groups", pk]); + } + + static list(filter?: QueryArguments): Promise> { + return DefaultClient.fetch>(["core", "groups"], filter); + } + + static adminUrl(rest: string): string { + return `/administration/groups/${rest}`; + } } diff --git a/web/src/interfaces/AdminInterface.ts b/web/src/interfaces/AdminInterface.ts index a34ff9170..75d791835 100644 --- a/web/src/interfaces/AdminInterface.ts +++ b/web/src/interfaces/AdminInterface.ts @@ -48,7 +48,7 @@ export const SIDEBAR_ITEMS: SidebarItem[] = [ }), new SidebarItem("Identity & Cryptography").children( new SidebarItem("User", "/administration/users/"), - new SidebarItem("Groups", "/administration/groups/"), + new SidebarItem("Groups", "/groups"), new SidebarItem("Certificates", "/crypto/certificates"), new SidebarItem("Tokens", "/administration/tokens/"), ).when((): Promise => { diff --git a/web/src/pages/groups/GroupListPage.ts b/web/src/pages/groups/GroupListPage.ts new file mode 100644 index 000000000..5a1ce6063 --- /dev/null +++ b/web/src/pages/groups/GroupListPage.ts @@ -0,0 +1,80 @@ +import { gettext } from "django"; +import { customElement, html, property, TemplateResult } from "lit-element"; +import { AKResponse } from "../../api/Client"; +import { TablePage } from "../../elements/table/TablePage"; + +import "../../elements/buttons/ModalButton"; +import "../../elements/buttons/SpinnerButton"; +import { TableColumn } from "../../elements/table/Table"; +import { Group } from "../../api/Groups"; + +@customElement("ak-group-list") +export class GroupListPage extends TablePage { + searchEnabled(): boolean { + return true; + } + pageTitle(): string { + return gettext("Groups"); + } + pageDescription(): string { + return gettext("Group users together and give them permissions based on the membership."); + } + pageIcon(): string { + return gettext("pf-icon pf-icon-users"); + } + + @property() + order = "slug"; + + apiEndpoint(page: number): Promise> { + return Group.list({ + ordering: this.order, + page: page, + search: this.search || "", + }); + } + + columns(): TableColumn[] { + return [ + new TableColumn("Name", "name"), + new TableColumn("Parent", "parent"), + new TableColumn("Members"), + new TableColumn("Superuser privileges?"), + new TableColumn(""), + ]; + } + + row(item: Group): TemplateResult[] { + return [ + html`${item.name}`, + html`${item.parent || "-"}`, + html`${item.users.length}`, + html`${item.is_superuser ? "Yes" : "No"}`, + html` + + + ${gettext("Edit")} + +
+
+ + + ${gettext("Delete")} + +
+
`, + ]; + } + + renderToolbar(): TemplateResult { + return html` + + + ${gettext("Create")} + +
+
+ ${super.renderToolbar()} + `; + } +} diff --git a/web/src/routes.ts b/web/src/routes.ts index 63e114b96..0675d2ba2 100644 --- a/web/src/routes.ts +++ b/web/src/routes.ts @@ -19,6 +19,7 @@ import "./pages/providers/ProviderListPage"; import "./pages/providers/ProviderViewPage"; import "./pages/sources/SourcesListPage"; import "./pages/sources/SourceViewPage"; +import "./pages/groups/GroupListPage"; export const ROUTES: Route[] = [ // Prevent infinite Shell loops @@ -39,6 +40,7 @@ export const ROUTES: Route[] = [ return html``; }), new Route(new RegExp("^/policies$"), html``), + new Route(new RegExp("^/groups$"), html``), new Route(new RegExp("^/flows$"), html``), new Route(new RegExp(`^/flows/(?${SLUG_REGEX})$`)).then((args) => { return html``;