core: add user metrics API
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
3668850e8f
commit
4c49209f71
|
@ -13,7 +13,7 @@ from rest_framework.permissions import IsAdminUser
|
|||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.serializers import Serializer
|
||||
from rest_framework.viewsets import ViewSet
|
||||
from rest_framework.viewsets import ReadOnlyModelViewSet
|
||||
|
||||
from authentik.events.models import Event, EventAction
|
||||
|
||||
|
@ -81,7 +81,7 @@ class LoginMetricsSerializer(Serializer):
|
|||
raise NotImplementedError
|
||||
|
||||
|
||||
class AdministrationMetricsViewSet(ViewSet):
|
||||
class AdministrationMetricsViewSet(ReadOnlyModelViewSet):
|
||||
"""Login Metrics per 1h"""
|
||||
|
||||
permission_classes = [IsAdminUser]
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
<!doctype html> <!-- Important: must specify -->
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"> <!-- Important: rapi-doc uses utf8 charecters -->
|
||||
<script type="module" src="{% static 'dist/rapidoc-min.js' %}"></script>
|
||||
<title>{% block title %}{% trans title|default:config.authentik.branding.title %}{% endblock %}</title>
|
||||
</head>
|
||||
<body>
|
||||
<rapi-doc
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
"""User API Views"""
|
||||
from drf_yasg2.utils import swagger_auto_schema
|
||||
from django.db.models.base import Model
|
||||
from drf_yasg2.utils import swagger_auto_schema, swagger_serializer_method
|
||||
from guardian.utils import get_anonymous_user
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.fields import CharField
|
||||
from rest_framework.fields import CharField, SerializerMethodField
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.serializers import BooleanField, ModelSerializer
|
||||
from rest_framework.serializers import BooleanField, ModelSerializer, Serializer
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from authentik.admin.api.metrics import CoordinateSerializer, get_events_per_1h
|
||||
from authentik.core.models import User
|
||||
from authentik.events.models import EventAction
|
||||
|
||||
|
||||
class UserSerializer(ModelSerializer):
|
||||
|
@ -33,6 +36,42 @@ class UserSerializer(ModelSerializer):
|
|||
]
|
||||
|
||||
|
||||
class UserMetricsSerializer(Serializer):
|
||||
"""User Metrics"""
|
||||
|
||||
logins_per_1h = SerializerMethodField()
|
||||
logins_failed_per_1h = SerializerMethodField()
|
||||
authorizations_per_1h = SerializerMethodField()
|
||||
|
||||
@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)
|
||||
|
||||
@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
|
||||
return get_events_per_1h(
|
||||
action=EventAction.LOGIN_FAILED, context__username=request.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
|
||||
return get_events_per_1h(
|
||||
action=EventAction.AUTHORIZE_APPLICATION, user__pk=request.user.pk
|
||||
)
|
||||
|
||||
def create(self, validated_data: dict) -> Model:
|
||||
raise NotImplementedError
|
||||
|
||||
def update(self, instance: Model, validated_data: dict) -> Model:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class UserViewSet(ModelViewSet):
|
||||
"""User Viewset"""
|
||||
|
||||
|
@ -50,3 +89,11 @@ class UserViewSet(ModelViewSet):
|
|||
def me(self, request: Request) -> Response:
|
||||
"""Get information about current user"""
|
||||
return Response(UserSerializer(request.user).data)
|
||||
|
||||
@swagger_auto_schema(responses={200: UserMetricsSerializer(many=False)})
|
||||
@action(detail=False)
|
||||
def metrics(self, request: Request) -> Response:
|
||||
"""User metrics per 1h"""
|
||||
serializer = UserMetricsSerializer(True)
|
||||
serializer.context["request"] = request
|
||||
return Response(serializer.data)
|
||||
|
|
107
swagger.yaml
107
swagger.yaml
|
@ -24,7 +24,27 @@ paths:
|
|||
get:
|
||||
operationId: admin_metrics_list
|
||||
description: Login Metrics per 1h
|
||||
parameters: []
|
||||
parameters:
|
||||
- name: ordering
|
||||
in: query
|
||||
description: Which field to use when ordering the results.
|
||||
required: false
|
||||
type: string
|
||||
- name: search
|
||||
in: query
|
||||
description: A search term.
|
||||
required: false
|
||||
type: string
|
||||
- name: page
|
||||
in: query
|
||||
description: Page Index
|
||||
required: false
|
||||
type: integer
|
||||
- name: page_size
|
||||
in: query
|
||||
description: Page Size
|
||||
required: false
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: Login Metrics per 1h
|
||||
|
@ -33,6 +53,21 @@ paths:
|
|||
tags:
|
||||
- admin
|
||||
parameters: []
|
||||
/admin/metrics/{id}/:
|
||||
get:
|
||||
operationId: admin_metrics_read
|
||||
description: Login Metrics per 1h
|
||||
parameters: []
|
||||
responses:
|
||||
'200':
|
||||
description: ''
|
||||
tags:
|
||||
- admin
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
type: string
|
||||
/admin/system_tasks/:
|
||||
get:
|
||||
operationId: admin_system_tasks_list
|
||||
|
@ -1617,6 +1652,54 @@ paths:
|
|||
tags:
|
||||
- core
|
||||
parameters: []
|
||||
/core/users/metrics/:
|
||||
get:
|
||||
operationId: core_users_metrics
|
||||
description: User metrics per 1h
|
||||
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.
|
||||
required: false
|
||||
type: string
|
||||
- name: search
|
||||
in: query
|
||||
description: A search term.
|
||||
required: false
|
||||
type: string
|
||||
- name: page
|
||||
in: query
|
||||
description: Page Index
|
||||
required: false
|
||||
type: integer
|
||||
- name: page_size
|
||||
in: query
|
||||
description: Page Size
|
||||
required: false
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: User Metrics
|
||||
schema:
|
||||
$ref: '#/definitions/UserMetrics'
|
||||
tags:
|
||||
- core
|
||||
parameters: []
|
||||
/core/users/{id}/:
|
||||
get:
|
||||
operationId: core_users_read
|
||||
|
@ -10981,6 +11064,28 @@ definitions:
|
|||
$ref: '#/definitions/User'
|
||||
application:
|
||||
$ref: '#/definitions/Application'
|
||||
UserMetrics:
|
||||
description: User Metrics
|
||||
type: object
|
||||
properties:
|
||||
logins_per_1h:
|
||||
description: ''
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/Coordinate'
|
||||
readOnly: true
|
||||
logins_failed_per_1h:
|
||||
description: ''
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/Coordinate'
|
||||
readOnly: true
|
||||
authorizations_per_1h:
|
||||
description: ''
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/Coordinate'
|
||||
readOnly: true
|
||||
CertificateKeyPair:
|
||||
description: CertificateKeyPair Serializer
|
||||
required:
|
||||
|
|
|
@ -197,6 +197,7 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
|
|||
self.container = self.setup_client()
|
||||
|
||||
self.driver.get("http://localhost:9009/implicit/")
|
||||
sleep(2)
|
||||
self.login()
|
||||
|
||||
self.wait.until(
|
||||
|
|
Reference in a new issue