diff --git a/authentik/core/api/users.py b/authentik/core/api/users.py index d67e890a4..94e926247 100644 --- a/authentik/core/api/users.py +++ b/authentik/core/api/users.py @@ -61,23 +61,23 @@ class UserMetricsSerializer(PassiveSerializer): @swagger_serializer_method(serializer_or_field=CoordinateSerializer(many=True)) def get_logins_per_1h(self, _): """Get successful logins per hour for the last 24 hours""" - request = self.context["request"]._request - return get_events_per_1h(action=EventAction.LOGIN, user__pk=request.user.pk) + user = self.context["user"] + return get_events_per_1h(action=EventAction.LOGIN, user__pk=user.pk) @swagger_serializer_method(serializer_or_field=CoordinateSerializer(many=True)) def get_logins_failed_per_1h(self, _): """Get failed logins per hour for the last 24 hours""" - request = self.context["request"]._request + user = self.context["user"] return get_events_per_1h( - action=EventAction.LOGIN_FAILED, context__username=request.user.username + action=EventAction.LOGIN_FAILED, context__username=user.username ) @swagger_serializer_method(serializer_or_field=CoordinateSerializer(many=True)) def get_authorizations_per_1h(self, _): """Get failed logins per hour for the last 24 hours""" - request = self.context["request"]._request + user = self.context["user"] return get_events_per_1h( - action=EventAction.AUTHORIZE_APPLICATION, user__pk=request.user.pk + action=EventAction.AUTHORIZE_APPLICATION, user__pk=user.pk ) @@ -109,11 +109,13 @@ class UserViewSet(ModelViewSet): @permission_required("authentik_core.view_user", ["authentik_events.view_event"]) @swagger_auto_schema(responses={200: UserMetricsSerializer(many=False)}) - @action(detail=False, pagination_class=None, filter_backends=[]) - def metrics(self, request: Request) -> Response: + @action(detail=True, pagination_class=None, filter_backends=[]) + # pylint: disable=invalid-name, unused-argument + def metrics(self, request: Request, pk: int) -> Response: """User metrics per 1h""" + user: User = self.get_object() serializer = UserMetricsSerializer(True) - serializer.context["request"] = request + serializer.context["user"] = user return Response(serializer.data) @permission_required("authentik_core.reset_user_password") @@ -135,3 +137,4 @@ class UserViewSet(ModelViewSet): reverse_lazy("authentik_flows:default-recovery") + f"?{querystring}" ) return Response({"link": link}) + diff --git a/swagger.yaml b/swagger.yaml index 283d604f2..c364f0457 100755 --- a/swagger.yaml +++ b/swagger.yaml @@ -2087,23 +2087,6 @@ paths: tags: - core parameters: [] - /core/users/metrics/: - get: - operationId: core_users_metrics - description: User metrics per 1h - parameters: [] - responses: - '200': - description: '' - schema: - $ref: '#/definitions/UserMetrics' - '403': - description: Authentication credentials were invalid, absent or insufficient. - schema: - $ref: '#/definitions/GenericError' - tags: - - core - parameters: [] /core/users/{id}/: get: operationId: core_users_read @@ -2207,6 +2190,33 @@ paths: description: A unique integer value identifying this User. required: true type: integer + /core/users/{id}/metrics/: + get: + operationId: core_users_metrics + description: User metrics per 1h + parameters: [] + responses: + '200': + description: '' + schema: + $ref: '#/definitions/UserMetrics' + '403': + description: Authentication credentials were invalid, absent or insufficient. + schema: + $ref: '#/definitions/GenericError' + '404': + description: Object does not exist or caller has insufficient permissions + to access it. + schema: + $ref: '#/definitions/APIException' + tags: + - core + parameters: + - name: id + in: path + description: A unique integer value identifying this User. + required: true + type: integer /core/users/{id}/recovery/: get: operationId: core_users_recovery diff --git a/web/src/elements/charts/UserChart.ts b/web/src/elements/charts/UserChart.ts index 877b1da66..41a9d4407 100644 --- a/web/src/elements/charts/UserChart.ts +++ b/web/src/elements/charts/UserChart.ts @@ -1,4 +1,4 @@ -import { customElement } from "lit-element"; +import { customElement, property } from "lit-element"; import Chart from "chart.js"; import { CoreApi, UserMetrics } from "authentik-api"; import { AKChart } from "./Chart"; @@ -7,8 +7,13 @@ import { DEFAULT_CONFIG } from "../../api/Config"; @customElement("ak-charts-user") export class UserChart extends AKChart { + @property() + userId?: number; + apiRequest(): Promise { - return new CoreApi(DEFAULT_CONFIG).coreUsersMetrics(); + return new CoreApi(DEFAULT_CONFIG).coreUsersMetrics({ + id: this.userId || 0, + }); } getDatasets(data: UserMetrics): Chart.ChartDataSets[] { diff --git a/web/src/pages/users/UserViewPage.ts b/web/src/pages/users/UserViewPage.ts index 956117bfc..1b0c8791b 100644 --- a/web/src/pages/users/UserViewPage.ts +++ b/web/src/pages/users/UserViewPage.ts @@ -167,7 +167,7 @@ export class UserViewPage extends Page { @@ -177,7 +177,7 @@ export class UserViewPage extends Page {