web/admin: add dashboard with user creation/login statistics

closes #1867

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2021-12-14 22:04:16 +01:00
parent 30386cd899
commit 3740e65906
11 changed files with 460 additions and 216 deletions

View File

@ -232,7 +232,7 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet):
app.save()
return Response({})
@permission_required("authentik_core.view_application")
@permission_required("authentik_core.view_application", ["authentik_events.view_event"])
@extend_schema(responses={200: CoordinateSerializer(many=True)})
@action(detail=True, pagination_class=None, filter_backends=[])
# pylint: disable=unused-argument

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,52 @@
import { ChartData, Tick } from "chart.js";
import { t } from "@lingui/macro";
import { customElement, property } from "lit/decorators.js";
import { Coordinate, EventActions, EventsApi } from "@goauthentik/api";
import { DEFAULT_CONFIG } from "../../api/Config";
import { AKChart } from "./Chart";
@customElement("ak-charts-admin-model-per-day")
export class AdminModelPerDay extends AKChart<Coordinate[]> {
@property()
action: EventActions = EventActions.ModelCreated;
@property({ attribute: false })
query?: { [key: string]: unknown } | undefined;
apiRequest(): Promise<Coordinate[]> {
return new EventsApi(DEFAULT_CONFIG).eventsEventsPerMonthList({
action: this.action,
query: JSON.stringify(this.query || {}),
});
}
timeTickCallback(tickValue: string | number, index: number, ticks: Tick[]): string {
const valueStamp = ticks[index];
const delta = Date.now() - valueStamp.value;
const ago = Math.round(delta / 1000 / 3600 / 24);
return t`${ago} days ago`;
}
getChartData(data: Coordinate[]): ChartData {
return {
datasets: [
{
label: t`Objects created`,
backgroundColor: "rgba(189, 229, 184, .5)",
spanGaps: true,
data:
data.map((cord) => {
return {
x: cord.xCord || 0,
y: cord.yCord || 0,
};
}) || [],
},
],
};
}
}

View File

@ -5,6 +5,8 @@ import { ArcElement, BarElement } from "chart.js";
import { LinearScale, TimeScale } from "chart.js";
import "chartjs-adapter-moment";
import { t } from "@lingui/macro";
import { CSSResult, LitElement, TemplateResult, css, html } from "lit";
import { property } from "lit/decorators.js";
@ -114,6 +116,13 @@ export abstract class AKChart<T> extends LitElement {
];
}
timeTickCallback(tickValue: string | number, index: number, ticks: Tick[]): string {
const valueStamp = ticks[index];
const delta = Date.now() - valueStamp.value;
const ago = Math.round(delta / 1000 / 3600);
return t`${ago} hours ago`;
}
getOptions(): ChartOptions {
return {
maintainAspectRatio: false,
@ -122,15 +131,8 @@ export abstract class AKChart<T> extends LitElement {
type: "time",
display: true,
ticks: {
callback: function (
tickValue: string | number,
index: number,
ticks: Tick[],
): string {
const valueStamp = ticks[index];
const delta = Date.now() - valueStamp.value;
const ago = Math.round(delta / 1000 / 3600);
return `${ago} Hours ago`;
callback: (tickValue: string | number, index: number, ticks: Tick[]) => {
return this.timeTickCallback(tickValue, index, ticks);
},
autoSkip: true,
maxTicksLimit: 8,

View File

@ -194,6 +194,9 @@ export class AdminInterface extends LitElement {
<ak-sidebar-item path="/administration/overview">
<span slot="label">${t`Overview`}</span>
</ak-sidebar-item>
<ak-sidebar-item path="/administration/dashboard/users">
<span slot="label">${t`Users`}</span>
</ak-sidebar-item>
<ak-sidebar-item path="/administration/system-tasks">
<span slot="label">${t`System Tasks`}</span>
</ak-sidebar-item>

View File

@ -1902,6 +1902,10 @@ msgstr "External host"
msgid "Failed Logins"
msgstr "Failed Logins"
#: src/pages/admin-overview/DashboardUserPage.ts
msgid "Failed Logins per day in the last month"
msgstr "Failed Logins per day in the last month"
#: src/pages/stages/password/PasswordStageForm.ts
msgid "Failed attempts before cancel"
msgstr "Failed attempts before cancel"
@ -2124,7 +2128,6 @@ msgid "General system exception"
msgstr "General system exception"
#: src/pages/admin-overview/AdminOverviewPage.ts
#: src/pages/admin-overview/UserDashboardPage.ts
msgid "General system status"
msgstr "General system status"
@ -2739,6 +2742,10 @@ msgstr "Logins"
msgid "Logins over the last 24 hours"
msgstr "Logins over the last 24 hours"
#: src/pages/admin-overview/DashboardUserPage.ts
msgid "Logins per day in the last month"
msgstr "Logins per day in the last month"
#: src/pages/tenants/TenantForm.ts
msgid "Logo"
msgstr "Logo"
@ -3197,6 +3204,10 @@ msgstr "Object field"
msgid "Object uniqueness field"
msgstr "Object uniqueness field"
#: src/elements/charts/AdminModelPerDay.ts
msgid "Objects created"
msgstr "Objects created"
#: src/pages/stages/consent/ConsentStageForm.ts
msgid "Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
msgstr "Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
@ -4774,7 +4785,6 @@ msgid "Superuser-groups"
msgstr "Superuser-groups"
#: src/pages/admin-overview/charts/UserCountStatusChart.ts
#: src/pages/admin-overview/charts/UserOverTimeStatusChart.ts
msgid "Superusers"
msgstr "Superusers"
@ -5138,7 +5148,6 @@ msgid "Total policies"
msgstr "Total policies"
#: src/pages/admin-overview/charts/UserCountStatusChart.ts
#: src/pages/admin-overview/charts/UserOverTimeStatusChart.ts
msgid "Total users"
msgstr "Total users"
@ -5549,8 +5558,8 @@ msgid "User matching mode"
msgstr "User matching mode"
#: src/pages/admin-overview/UserDashboardPage.ts
msgid "User metrics"
msgstr "User metrics"
#~ msgid "User metrics"
#~ msgstr "User metrics"
#: src/pages/sources/ldap/LDAPSourceForm.ts
msgid "User object filter"
@ -5560,6 +5569,10 @@ msgstr "User object filter"
msgid "User password writeback"
msgstr "User password writeback"
#: src/pages/admin-overview/DashboardUserPage.ts
msgid "User statistics"
msgstr "User statistics"
#: src/pages/users/UserListPage.ts
msgid "User status"
msgstr "User status"
@ -5641,6 +5654,10 @@ msgstr "Users"
msgid "Users added to this group will be superusers."
msgstr "Users added to this group will be superusers."
#: src/pages/admin-overview/DashboardUserPage.ts
msgid "Users created per day in the last month"
msgstr "Users created per day in the last month"
#: src/pages/providers/ldap/LDAPProviderForm.ts
msgid "Users in the selected group can do search queries. If no group is selected, no LDAP Searches are allowed."
msgstr "Users in the selected group can do search queries. If no group is selected, no LDAP Searches are allowed."
@ -5972,3 +5989,11 @@ msgstr "{0}, should be {1}"
#: src/elements/forms/ConfirmationForm.ts
msgid "{0}: {1}"
msgstr "{0}: {1}"
#: src/elements/charts/AdminModelPerDay.ts
msgid "{ago} days ago"
msgstr "{ago} days ago"
#: src/elements/charts/Chart.ts
msgid "{ago} hours ago"
msgstr "{ago} hours ago"

View File

@ -1888,6 +1888,10 @@ msgstr "Hôte externe"
msgid "Failed Logins"
msgstr "Connexions échouées"
#: src/pages/admin-overview/DashboardUserPage.ts
msgid "Failed Logins per day in the last month"
msgstr ""
#: src/pages/stages/password/PasswordStageForm.ts
msgid "Failed attempts before cancel"
msgstr "Échecs avant annulation"
@ -2109,7 +2113,6 @@ msgid "General system exception"
msgstr "Exception générale du systèm"
#: src/pages/admin-overview/AdminOverviewPage.ts
#: src/pages/admin-overview/UserDashboardPage.ts
msgid "General system status"
msgstr "État général du système"
@ -2718,6 +2721,10 @@ msgstr "Connexions"
msgid "Logins over the last 24 hours"
msgstr "Connexions ces dernières 24 heures"
#: src/pages/admin-overview/DashboardUserPage.ts
msgid "Logins per day in the last month"
msgstr ""
#: src/pages/tenants/TenantForm.ts
msgid "Logo"
msgstr "Logo"
@ -3173,6 +3180,10 @@ msgstr "Champ d'objet"
msgid "Object uniqueness field"
msgstr "Champ d'unicité de l'objet"
#: src/elements/charts/AdminModelPerDay.ts
msgid "Objects created"
msgstr ""
#: src/pages/stages/consent/ConsentStageForm.ts
msgid "Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
msgstr "Durée d'expiration du consentement (Format : hours=1;minutes=2;seconds=3)."
@ -4730,7 +4741,6 @@ msgid "Superuser-groups"
msgstr "Groupes de super-utilisateur"
#: src/pages/admin-overview/charts/UserCountStatusChart.ts
#: src/pages/admin-overview/charts/UserOverTimeStatusChart.ts
msgid "Superusers"
msgstr "Super-utilisateurs"
@ -5079,7 +5089,6 @@ msgid "Total policies"
msgstr "Politiques totales"
#: src/pages/admin-overview/charts/UserCountStatusChart.ts
#: src/pages/admin-overview/charts/UserOverTimeStatusChart.ts
msgid "Total users"
msgstr "Utilisateurs totaux"
@ -5487,8 +5496,8 @@ msgid "User matching mode"
msgstr "Mode de correspondance utilisateur"
#: src/pages/admin-overview/UserDashboardPage.ts
msgid "User metrics"
msgstr ""
#~ msgid "User metrics"
#~ msgstr ""
#: src/pages/sources/ldap/LDAPSourceForm.ts
msgid "User object filter"
@ -5498,6 +5507,10 @@ msgstr "Filtre des objets utilisateur"
msgid "User password writeback"
msgstr "Réécriture du mot de passe utilisateur"
#: src/pages/admin-overview/DashboardUserPage.ts
msgid "User statistics"
msgstr ""
#: src/pages/users/UserListPage.ts
msgid "User status"
msgstr "Statut utilisateur"
@ -5579,6 +5592,10 @@ msgstr "Utilisateurs"
msgid "Users added to this group will be superusers."
msgstr "Les utilisateurs ajoutés à ce groupe seront des super-utilisateurs."
#: src/pages/admin-overview/DashboardUserPage.ts
msgid "Users created per day in the last month"
msgstr ""
#: src/pages/providers/ldap/LDAPProviderForm.ts
msgid "Users in the selected group can do search queries. If no group is selected, no LDAP Searches are allowed."
msgstr "Les utilisateurs de ce groupe peuvent effectuer des recherches. Si aucun groupe n'est sélectionné, aucune recherche LDAP n'est autorisée."
@ -5906,3 +5923,11 @@ msgstr "{0}, devrait être {1}"
#: src/elements/forms/ConfirmationForm.ts
msgid "{0}: {1}"
msgstr "{0} : {1}"
#: src/elements/charts/AdminModelPerDay.ts
msgid "{ago} days ago"
msgstr ""
#: src/elements/charts/Chart.ts
msgid "{ago} hours ago"
msgstr ""

View File

@ -1894,6 +1894,10 @@ msgstr ""
msgid "Failed Logins"
msgstr ""
#: src/pages/admin-overview/DashboardUserPage.ts
msgid "Failed Logins per day in the last month"
msgstr ""
#: src/pages/stages/password/PasswordStageForm.ts
msgid "Failed attempts before cancel"
msgstr ""
@ -2116,7 +2120,6 @@ msgid "General system exception"
msgstr ""
#: src/pages/admin-overview/AdminOverviewPage.ts
#: src/pages/admin-overview/UserDashboardPage.ts
msgid "General system status"
msgstr ""
@ -2729,6 +2732,10 @@ msgstr ""
msgid "Logins over the last 24 hours"
msgstr ""
#: src/pages/admin-overview/DashboardUserPage.ts
msgid "Logins per day in the last month"
msgstr ""
#: src/pages/tenants/TenantForm.ts
msgid "Logo"
msgstr ""
@ -3187,6 +3194,10 @@ msgstr ""
msgid "Object uniqueness field"
msgstr ""
#: src/elements/charts/AdminModelPerDay.ts
msgid "Objects created"
msgstr ""
#: src/pages/stages/consent/ConsentStageForm.ts
msgid "Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
msgstr ""
@ -4764,7 +4775,6 @@ msgid "Superuser-groups"
msgstr ""
#: src/pages/admin-overview/charts/UserCountStatusChart.ts
#: src/pages/admin-overview/charts/UserOverTimeStatusChart.ts
msgid "Superusers"
msgstr ""
@ -5118,7 +5128,6 @@ msgid "Total policies"
msgstr ""
#: src/pages/admin-overview/charts/UserCountStatusChart.ts
#: src/pages/admin-overview/charts/UserOverTimeStatusChart.ts
msgid "Total users"
msgstr ""
@ -5529,8 +5538,8 @@ msgid "User matching mode"
msgstr ""
#: src/pages/admin-overview/UserDashboardPage.ts
msgid "User metrics"
msgstr ""
#~ msgid "User metrics"
#~ msgstr ""
#: src/pages/sources/ldap/LDAPSourceForm.ts
msgid "User object filter"
@ -5540,6 +5549,10 @@ msgstr ""
msgid "User password writeback"
msgstr ""
#: src/pages/admin-overview/DashboardUserPage.ts
msgid "User statistics"
msgstr ""
#: src/pages/users/UserListPage.ts
msgid "User status"
msgstr ""
@ -5621,6 +5634,10 @@ msgstr ""
msgid "Users added to this group will be superusers."
msgstr ""
#: src/pages/admin-overview/DashboardUserPage.ts
msgid "Users created per day in the last month"
msgstr ""
#: src/pages/providers/ldap/LDAPProviderForm.ts
msgid "Users in the selected group can do search queries. If no group is selected, no LDAP Searches are allowed."
msgstr ""
@ -5950,3 +5967,11 @@ msgstr ""
#: src/elements/forms/ConfirmationForm.ts
msgid "{0}: {1}"
msgstr ""
#: src/elements/charts/AdminModelPerDay.ts
msgid "{ago} days ago"
msgstr ""
#: src/elements/charts/Chart.ts
msgid "{ago} hours ago"
msgstr ""

View File

@ -91,9 +91,7 @@ export class AdminOverviewPage extends LitElement {
>
</li>
<li>
<a
class="pf-u-mb-xl"
href=${paramURL("/events/log")}
<a class="pf-u-mb-xl" href=${paramURL("/events/log")}
>${t`Check the logs`}</a
>
</li>

View File

@ -0,0 +1,86 @@
import { t } from "@lingui/macro";
import { CSSResult, LitElement, TemplateResult, css, html } from "lit";
import { customElement } from "lit/decorators.js";
import AKGlobal from "../../authentik.css";
import PFContent from "@patternfly/patternfly/components/Content/content.css";
import PFList from "@patternfly/patternfly/components/List/list.css";
import PFPage from "@patternfly/patternfly/components/Page/page.css";
import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
import { EventActions } from "@goauthentik/api";
import "../../elements/PageHeader";
import "../../elements/cards/AggregatePromiseCard";
import "../../elements/charts/AdminModelPerDay";
@customElement("ak-admin-dashboard-users")
export class DashboardUserPage extends LitElement {
static get styles(): CSSResult[] {
return [
PFGrid,
PFPage,
PFContent,
PFList,
AKGlobal,
css`
.row-divider {
margin-top: -4px;
margin-bottom: -4px;
}
.graph-container {
height: 20em;
}
.big-graph-container {
height: 35em;
}
.card-container {
max-height: 10em;
}
`,
];
}
render(): TemplateResult {
return html`<ak-page-header icon="pf-icon pf-icon-user" header=${t`User statistics`}>
</ak-page-header>
<section class="pf-c-page__main-section">
<div class="pf-l-grid pf-m-gutter">
<div
class="pf-l-grid__item pf-m-12-col pf-m-12-col-on-xl pf-m-12-col-on-2xl big-graph-container"
>
<ak-aggregate-card header=${t`Users created per day in the last month`}>
<ak-charts-admin-model-per-day
.query=${{
context__model__app: "authentik_core",
context__model__model_name: "user",
}}
>
</ak-charts-admin-model-per-day>
</ak-aggregate-card>
</div>
<div class="pf-l-grid__item pf-m-12-col row-divider">
<hr />
</div>
<!-- row 2 -->
<div
class="pf-l-grid__item pf-m-12-col pf-m-6-col-on-xl pf-m-6-col-on-2xl big-graph-container"
>
<ak-aggregate-card header=${t`Logins per day in the last month`}>
<ak-charts-admin-model-per-day action=${EventActions.Login}>
</ak-charts-admin-model-per-day>
</ak-aggregate-card>
</div>
<div
class="pf-l-grid__item pf-m-12-col pf-m-6-col-on-xl pf-m-6-col-on-2xl big-graph-container"
>
<ak-aggregate-card header=${t`Failed Logins per day in the last month`}>
<ak-charts-admin-model-per-day action=${EventActions.LoginFailed}>
</ak-charts-admin-model-per-day>
</ak-aggregate-card>
</div>
</div>
</section> `;
}
}

View File

@ -2,6 +2,7 @@ import { html } from "lit";
import { ID_REGEX, Route, SLUG_REGEX, UUID_REGEX } from "./elements/router/Route";
import "./pages/admin-overview/AdminOverviewPage";
import "./pages/admin-overview/DashboardUserPage";
import "./pages/applications/ApplicationListPage";
import "./pages/applications/ApplicationViewPage";
import "./pages/crypto/CertificateKeyPairListPage";
@ -40,6 +41,10 @@ export const ROUTES: Route[] = [
new RegExp("^/administration/overview$"),
html`<ak-admin-overview></ak-admin-overview>`,
),
new Route(
new RegExp("^/administration/dashboard/users$"),
html`<ak-admin-dashboard-users></ak-admin-dashboard-users>`,
),
new Route(
new RegExp("^/administration/system-tasks$"),
html`<ak-system-task-list></ak-system-task-list>`,