admin: add authorisations metric (#3811)

add authorizations metric

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens L 2022-10-19 00:06:45 +02:00 committed by GitHub
parent 4fc21c3cc3
commit b06a3a8f9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 122 additions and 53 deletions

View File

@ -23,6 +23,7 @@ class LoginMetricsSerializer(PassiveSerializer):
logins_per_1h = SerializerMethodField() logins_per_1h = SerializerMethodField()
logins_failed_per_1h = SerializerMethodField() logins_failed_per_1h = SerializerMethodField()
authorizations_per_1h = SerializerMethodField()
@extend_schema_field(CoordinateSerializer(many=True)) @extend_schema_field(CoordinateSerializer(many=True))
def get_logins_per_1h(self, _): def get_logins_per_1h(self, _):
@ -44,6 +45,16 @@ class LoginMetricsSerializer(PassiveSerializer):
.get_events_per_hour() .get_events_per_hour()
) )
@extend_schema_field(CoordinateSerializer(many=True))
def get_authorizations_per_1h(self, _):
"""Get successful authorizations per hour for the last 24 hours"""
user = self.context["user"]
return (
get_objects_for_user(user, "authentik_events.view_event")
.filter(action=EventAction.AUTHORIZE_APPLICATION)
.get_events_per_hour()
)
class AdministrationMetricsViewSet(APIView): class AdministrationMetricsViewSet(APIView):
"""Login Metrics per 1h""" """Login Metrics per 1h"""

View File

@ -28967,7 +28967,13 @@ components:
items: items:
$ref: '#/components/schemas/Coordinate' $ref: '#/components/schemas/Coordinate'
readOnly: true readOnly: true
authorizations_per_1h:
type: array
items:
$ref: '#/components/schemas/Coordinate'
readOnly: true
required: required:
- authorizations_per_1h
- logins_failed_per_1h - logins_failed_per_1h
- logins_per_1h - logins_per_1h
LoginSource: LoginSource:

View File

@ -3,6 +3,7 @@ import "@goauthentik/admin/admin-overview/cards/AdminStatusCard";
import "@goauthentik/admin/admin-overview/cards/SystemStatusCard"; import "@goauthentik/admin/admin-overview/cards/SystemStatusCard";
import "@goauthentik/admin/admin-overview/cards/VersionStatusCard"; import "@goauthentik/admin/admin-overview/cards/VersionStatusCard";
import "@goauthentik/admin/admin-overview/cards/WorkerStatusCard"; import "@goauthentik/admin/admin-overview/cards/WorkerStatusCard";
import "@goauthentik/admin/admin-overview/charts/AdminLoginAuthorizeChart";
import "@goauthentik/admin/admin-overview/charts/FlowStatusChart"; import "@goauthentik/admin/admin-overview/charts/FlowStatusChart";
import "@goauthentik/admin/admin-overview/charts/GroupCountStatusChart"; import "@goauthentik/admin/admin-overview/charts/GroupCountStatusChart";
import "@goauthentik/admin/admin-overview/charts/LDAPSyncStatusChart"; import "@goauthentik/admin/admin-overview/charts/LDAPSyncStatusChart";
@ -13,7 +14,6 @@ import { me } from "@goauthentik/common/users";
import { AKElement } from "@goauthentik/elements/Base"; import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/PageHeader"; import "@goauthentik/elements/PageHeader";
import "@goauthentik/elements/cards/AggregatePromiseCard"; import "@goauthentik/elements/cards/AggregatePromiseCard";
import "@goauthentik/elements/charts/AdminLoginsChart";
import { paramURL } from "@goauthentik/elements/router/RouterOutlet"; import { paramURL } from "@goauthentik/elements/router/RouterOutlet";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
@ -189,9 +189,9 @@ export class AdminOverviewPage extends AKElement {
> >
<ak-aggregate-card <ak-aggregate-card
icon="pf-icon pf-icon-server" icon="pf-icon pf-icon-server"
header=${t`Logins over the last 24 hours`} header=${t`Logins and authorizations over the last 24 hours`}
> >
<ak-charts-admin-login></ak-charts-admin-login> <ak-charts-admin-login-authorization></ak-charts-admin-login-authorization>
</ak-aggregate-card> </ak-aggregate-card>
</div> </div>
<div <div

View File

@ -0,0 +1,68 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { AKChart, RGBAColor } from "@goauthentik/elements/charts/Chart";
import { ChartData } from "chart.js";
import { t } from "@lingui/macro";
import { customElement } from "lit/decorators.js";
import { AdminApi, LoginMetrics } from "@goauthentik/api";
@customElement("ak-charts-admin-login-authorization")
export class AdminLoginAuthorizeChart extends AKChart<LoginMetrics> {
apiRequest(): Promise<LoginMetrics> {
return new AdminApi(DEFAULT_CONFIG).adminMetricsRetrieve();
}
getChartData(data: LoginMetrics): ChartData {
return {
datasets: [
{
label: t`Authorizations`,
backgroundColor: new RGBAColor(43, 154, 243, 0.5).toString(),
borderColor: new RGBAColor(43, 154, 243, 1).toString(),
spanGaps: true,
fill: "origin",
cubicInterpolationMode: "monotone",
tension: 0.4,
data: data.authorizationsPer1h.map((cord) => {
return {
x: cord.xCord,
y: cord.yCord,
};
}),
},
{
label: t`Failed Logins`,
backgroundColor: new RGBAColor(201, 24, 11, 0.5).toString(),
borderColor: new RGBAColor(201, 24, 11, 1).toString(),
spanGaps: true,
fill: "origin",
cubicInterpolationMode: "monotone",
tension: 0.4,
data: data.loginsFailedPer1h.map((cord) => {
return {
x: cord.xCord,
y: cord.yCord,
};
}),
},
{
label: t`Successful Logins`,
backgroundColor: new RGBAColor(62, 134, 53, 0.5).toString(),
borderColor: new RGBAColor(62, 134, 53, 1).toString(),
spanGaps: true,
fill: "origin",
cubicInterpolationMode: "monotone",
tension: 0.4,
data: data.loginsPer1h.map((cord) => {
return {
x: cord.xCord,
y: cord.yCord,
};
}),
},
],
};
}
}

View File

@ -1,47 +0,0 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { AKChart } from "@goauthentik/elements/charts/Chart";
import { ChartData } from "chart.js";
import { t } from "@lingui/macro";
import { customElement } from "lit/decorators.js";
import { AdminApi, LoginMetrics } from "@goauthentik/api";
@customElement("ak-charts-admin-login")
export class AdminLoginsChart extends AKChart<LoginMetrics> {
apiRequest(): Promise<LoginMetrics> {
return new AdminApi(DEFAULT_CONFIG).adminMetricsRetrieve();
}
getChartData(data: LoginMetrics): ChartData {
return {
datasets: [
{
label: t`Failed Logins`,
backgroundColor: "rgba(201, 25, 11, .5)",
spanGaps: true,
data:
data.loginsFailedPer1h?.map((cord) => {
return {
x: cord.xCord || 0,
y: cord.yCord || 0,
};
}) || [],
},
{
label: t`Successful Logins`,
backgroundColor: "rgba(189, 229, 184, .5)",
spanGaps: true,
data:
data.loginsPer1h?.map((cord) => {
return {
x: cord.xCord || 0,
y: cord.yCord || 0,
};
}) || [],
},
],
};
}
}

View File

@ -1,6 +1,16 @@
import { EVENT_REFRESH } from "@goauthentik/common/constants"; import { EVENT_REFRESH } from "@goauthentik/common/constants";
import { AKElement } from "@goauthentik/elements/Base"; import { AKElement } from "@goauthentik/elements/Base";
import { Chart, ChartConfiguration, ChartData, ChartOptions, Plugin, Tick } from "chart.js"; import {
Chart,
ChartConfiguration,
ChartData,
ChartOptions,
Filler,
LineElement,
Plugin,
PointElement,
Tick,
} from "chart.js";
import { Legend, Tooltip } from "chart.js"; import { Legend, Tooltip } from "chart.js";
import { BarController, DoughnutController, LineController } from "chart.js"; import { BarController, DoughnutController, LineController } from "chart.js";
import { ArcElement, BarElement } from "chart.js"; import { ArcElement, BarElement } from "chart.js";
@ -14,12 +24,33 @@ import { property } from "lit/decorators.js";
Chart.register(Legend, Tooltip); Chart.register(Legend, Tooltip);
Chart.register(LineController, BarController, DoughnutController); Chart.register(LineController, BarController, DoughnutController);
Chart.register(ArcElement, BarElement); Chart.register(ArcElement, BarElement, PointElement, LineElement);
Chart.register(TimeScale, LinearScale); Chart.register(TimeScale, LinearScale, Filler);
export const FONT_COLOUR_DARK_MODE = "#fafafa"; export const FONT_COLOUR_DARK_MODE = "#fafafa";
export const FONT_COLOUR_LIGHT_MODE = "#151515"; export const FONT_COLOUR_LIGHT_MODE = "#151515";
export class RGBAColor {
constructor(public r: number, public g: number, public b: number, public a: number = 1) {}
toString(): string {
return `rgba(${this.r}, ${this.g}, ${this.b}, ${this.a})`;
}
}
export function getColorFromString(stringInput: string): RGBAColor {
let hash = 0;
for (let i = 0; i < stringInput.length; i++) {
hash = stringInput.charCodeAt(i) + ((hash << 5) - hash);
hash = hash & hash;
}
const rgb = [0, 0, 0];
for (let i = 0; i < 3; i++) {
const value = (hash >> (i * 8)) & 255;
rgb[i] = value;
}
return new RGBAColor(rgb[0], rgb[1], rgb[2]);
}
export abstract class AKChart<T> extends AKElement { export abstract class AKChart<T> extends AKElement {
abstract apiRequest(): Promise<T>; abstract apiRequest(): Promise<T>;
abstract getChartData(data: T): ChartData; abstract getChartData(data: T): ChartData;