From 34a3d81eff5967ec5ce1b2fc843424d0b07a4ada Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Tue, 9 Mar 2021 10:21:53 +0100 Subject: [PATCH] stages/authenticator_*: add API for authenticator devices --- authentik/api/v2/urls.py | 44 +- authentik/core/api/groups.py | 1 + authentik/stages/authenticator_static/api.py | 41 +- authentik/stages/authenticator_totp/api.py | 43 +- .../stages/authenticator_webauthn/api.py | 47 +- swagger.yaml | 806 ++++++++++++++++++ 6 files changed, 963 insertions(+), 19 deletions(-) diff --git a/authentik/api/v2/urls.py b/authentik/api/v2/urls.py index 9f3fd39e7..180f47969 100644 --- a/authentik/api/v2/urls.py +++ b/authentik/api/v2/urls.py @@ -55,12 +55,24 @@ from authentik.providers.saml.api import SAMLPropertyMappingViewSet, SAMLProvide from authentik.sources.ldap.api import LDAPPropertyMappingViewSet, LDAPSourceViewSet from authentik.sources.oauth.api import OAuthSourceViewSet from authentik.sources.saml.api import SAMLSourceViewSet -from authentik.stages.authenticator_static.api import AuthenticatorStaticStageViewSet -from authentik.stages.authenticator_totp.api import AuthenticatorTOTPStageViewSet +from authentik.stages.authenticator_static.api import ( + AuthenticatorStaticStageViewSet, + StaticAdminDeviceViewSet, + StaticDeviceViewSet, +) +from authentik.stages.authenticator_totp.api import ( + AuthenticatorTOTPStageViewSet, + TOTPAdminDeviceViewSet, + TOTPDeviceViewSet, +) from authentik.stages.authenticator_validate.api import ( AuthenticatorValidateStageViewSet, ) -from authentik.stages.authenticator_webauthn.api import AuthenticateWebAuthnStageViewSet +from authentik.stages.authenticator_webauthn.api import ( + AuthenticateWebAuthnStageViewSet, + WebAuthnAdminDeviceViewSet, + WebAuthnDeviceViewSet, +) from authentik.stages.captcha.api import CaptchaStageViewSet from authentik.stages.consent.api import ConsentStageViewSet from authentik.stages.deny.api import DenyStageViewSet @@ -134,6 +146,13 @@ router.register("propertymappings/ldap", LDAPPropertyMappingViewSet) router.register("propertymappings/saml", SAMLPropertyMappingViewSet) router.register("propertymappings/scope", ScopeMappingViewSet) +router.register("authenticators/static", StaticDeviceViewSet) +router.register("authenticators/totp", TOTPDeviceViewSet) +router.register("authenticators/webauthn", WebAuthnDeviceViewSet) +router.register("authenticators/admin/static", StaticAdminDeviceViewSet) +router.register("authenticators/admin/totp", TOTPAdminDeviceViewSet) +router.register("authenticators/admin/webauthn", WebAuthnAdminDeviceViewSet) + router.register("stages/all", StageViewSet) router.register("stages/authenticator/static", AuthenticatorStaticStageViewSet) router.register("stages/authenticator/totp", AuthenticatorTOTPStageViewSet) @@ -157,14 +176,6 @@ router.register("stages/user_write", UserWriteStageViewSet) router.register("stages/dummy", DummyStageViewSet) router.register("policies/dummy", DummyPolicyViewSet) -api_urls = router.urls + [ - path( - "flows/executor//", - FlowExecutorView.as_view(), - name="flow-executor", - ), -] - info = openapi.Info( title="authentik API", default_version="v2", @@ -173,11 +184,14 @@ info = openapi.Info( name="GNU GPLv3", url="https://github.com/BeryJu/authentik/blob/master/LICENSE" ), ) -SchemaView = get_schema_view( - info, public=True, permission_classes=(AllowAny,), patterns=api_urls -) +SchemaView = get_schema_view(info, public=True, permission_classes=(AllowAny,)) -urlpatterns = api_urls + [ +urlpatterns = router.urls + [ + path( + "flows/executor//", + FlowExecutorView.as_view(), + name="flow-executor", + ), re_path( r"^swagger(?P\.json|\.yaml)$", SchemaView.without_ui(cache_timeout=0), diff --git a/authentik/core/api/groups.py b/authentik/core/api/groups.py index 152c1ea9f..2ca259fb4 100644 --- a/authentik/core/api/groups.py +++ b/authentik/core/api/groups.py @@ -21,3 +21,4 @@ class GroupViewSet(ModelViewSet): serializer_class = GroupSerializer search_fields = ["name", "is_superuser"] filterset_fields = ["name", "is_superuser"] + ordering = ["name"] diff --git a/authentik/stages/authenticator_static/api.py b/authentik/stages/authenticator_static/api.py index 8ac6fc8c6..c32ed5443 100644 --- a/authentik/stages/authenticator_static/api.py +++ b/authentik/stages/authenticator_static/api.py @@ -1,5 +1,8 @@ """AuthenticatorStaticStage API Views""" -from rest_framework.viewsets import ModelViewSet +from django_otp.plugins.otp_static.models import StaticDevice +from rest_framework.permissions import IsAdminUser +from rest_framework.serializers import ModelSerializer +from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet from authentik.flows.api.stages import StageSerializer from authentik.stages.authenticator_static.models import AuthenticatorStaticStage @@ -19,3 +22,39 @@ class AuthenticatorStaticStageViewSet(ModelViewSet): queryset = AuthenticatorStaticStage.objects.all() serializer_class = AuthenticatorStaticStageSerializer + + +class StaticDeviceSerializer(ModelSerializer): + """Serializer for static authenticator devices""" + + class Meta: + + model = StaticDevice + fields = ["name", "token_set"] + depth = 2 + + +class StaticDeviceViewSet(ModelViewSet): + """Viewset for static authenticator devices""" + + queryset = StaticDevice.objects.none() + serializer_class = StaticDeviceSerializer + search_fields = ["name"] + filterset_fields = ["name"] + ordering = ["name"] + + def get_queryset(self): + if not self.request: + return super().get_queryset() + return StaticDevice.objects.filter(user=self.request.user) + + +class StaticAdminDeviceViewSet(ReadOnlyModelViewSet): + """Viewset for static authenticator devices (for admins)""" + + permission_classes = [IsAdminUser] + queryset = StaticDevice.objects.all() + serializer_class = StaticDeviceSerializer + search_fields = ["name"] + filterset_fields = ["name"] + ordering = ["name"] diff --git a/authentik/stages/authenticator_totp/api.py b/authentik/stages/authenticator_totp/api.py index b7e40fc6c..c5a093221 100644 --- a/authentik/stages/authenticator_totp/api.py +++ b/authentik/stages/authenticator_totp/api.py @@ -1,5 +1,8 @@ """AuthenticatorTOTPStage API Views""" -from rest_framework.viewsets import ModelViewSet +from django_otp.plugins.otp_totp.models import TOTPDevice +from rest_framework.permissions import IsAdminUser +from rest_framework.serializers import ModelSerializer +from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet from authentik.flows.api.stages import StageSerializer from authentik.stages.authenticator_totp.models import AuthenticatorTOTPStage @@ -19,3 +22,41 @@ class AuthenticatorTOTPStageViewSet(ModelViewSet): queryset = AuthenticatorTOTPStage.objects.all() serializer_class = AuthenticatorTOTPStageSerializer + + +class TOTPDeviceSerializer(ModelSerializer): + """Serializer for totp authenticator devices""" + + class Meta: + + model = TOTPDevice + fields = [ + "name", + ] + depth = 2 + + +class TOTPDeviceViewSet(ModelViewSet): + """Viewset for totp authenticator devices""" + + queryset = TOTPDevice.objects.none() + serializer_class = TOTPDeviceSerializer + search_fields = ["name"] + filterset_fields = ["name"] + ordering = ["name"] + + def get_queryset(self): + if not self.request: + return super().get_queryset() + return TOTPDevice.objects.filter(user=self.request.user) + + +class TOTPAdminDeviceViewSet(ReadOnlyModelViewSet): + """Viewset for totp authenticator devices (for admins)""" + + permission_classes = [IsAdminUser] + queryset = TOTPDevice.objects.all() + serializer_class = TOTPDeviceSerializer + search_fields = ["name"] + filterset_fields = ["name"] + ordering = ["name"] diff --git a/authentik/stages/authenticator_webauthn/api.py b/authentik/stages/authenticator_webauthn/api.py index 6fcebd119..9ee607887 100644 --- a/authentik/stages/authenticator_webauthn/api.py +++ b/authentik/stages/authenticator_webauthn/api.py @@ -1,8 +1,13 @@ """AuthenticateWebAuthnStage API Views""" -from rest_framework.viewsets import ModelViewSet +from rest_framework.permissions import IsAdminUser +from rest_framework.serializers import ModelSerializer +from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet from authentik.flows.api.stages import StageSerializer -from authentik.stages.authenticator_webauthn.models import AuthenticateWebAuthnStage +from authentik.stages.authenticator_webauthn.models import ( + AuthenticateWebAuthnStage, + WebAuthnDevice, +) class AuthenticateWebAuthnStageSerializer(StageSerializer): @@ -19,3 +24,41 @@ class AuthenticateWebAuthnStageViewSet(ModelViewSet): queryset = AuthenticateWebAuthnStage.objects.all() serializer_class = AuthenticateWebAuthnStageSerializer + + +class WebAuthnDeviceSerializer(ModelSerializer): + """Serializer for WebAuthn authenticator devices""" + + class Meta: + + model = WebAuthnDevice + fields = [ + "name", + ] + depth = 2 + + +class WebAuthnDeviceViewSet(ModelViewSet): + """Viewset for WebAuthn authenticator devices""" + + queryset = WebAuthnDevice.objects.none() + serializer_class = WebAuthnDeviceSerializer + search_fields = ["name"] + filterset_fields = ["name"] + ordering = ["name"] + + def get_queryset(self): + if not self.request: + return super().get_queryset() + return WebAuthnDevice.objects.filter(user=self.request.user) + + +class WebAuthnAdminDeviceViewSet(ReadOnlyModelViewSet): + """Viewset for WebAuthn authenticator devices (for admins)""" + + permission_classes = [IsAdminUser] + queryset = WebAuthnDevice.objects.all() + serializer_class = WebAuthnDeviceSerializer + search_fields = ["name"] + filterset_fields = ["name"] + ordering = ["name"] diff --git a/swagger.yaml b/swagger.yaml index ffef5e83a..5f7d6e9d7 100755 --- a/swagger.yaml +++ b/swagger.yaml @@ -165,6 +165,711 @@ paths: tags: - admin parameters: [] + /authenticators/admin/static/: + get: + operationId: authenticators_admin_static_list + description: Viewset for static authenticator devices (for admins) + parameters: + - name: name + 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: '' + schema: + required: + - results + - pagination + type: object + properties: + pagination: + required: + - next + - previous + - count + - current + - total_pages + - start_index + - end_index + type: object + properties: + next: + type: number + previous: + type: number + count: + type: number + current: + type: number + total_pages: + type: number + start_index: + type: number + end_index: + type: number + results: + type: array + items: + $ref: '#/definitions/StaticDevice' + tags: + - authenticators + parameters: [] + /authenticators/admin/static/{id}/: + get: + operationId: authenticators_admin_static_read + description: Viewset for static authenticator devices (for admins) + parameters: [] + responses: + '200': + description: '' + schema: + $ref: '#/definitions/StaticDevice' + tags: + - authenticators + parameters: + - name: id + in: path + description: A unique integer value identifying this static device. + required: true + type: integer + /authenticators/admin/totp/: + get: + operationId: authenticators_admin_totp_list + description: Viewset for totp authenticator devices (for admins) + parameters: + - name: name + 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: '' + schema: + required: + - results + - pagination + type: object + properties: + pagination: + required: + - next + - previous + - count + - current + - total_pages + - start_index + - end_index + type: object + properties: + next: + type: number + previous: + type: number + count: + type: number + current: + type: number + total_pages: + type: number + start_index: + type: number + end_index: + type: number + results: + type: array + items: + $ref: '#/definitions/TOTPDevice' + tags: + - authenticators + parameters: [] + /authenticators/admin/totp/{id}/: + get: + operationId: authenticators_admin_totp_read + description: Viewset for totp authenticator devices (for admins) + parameters: [] + responses: + '200': + description: '' + schema: + $ref: '#/definitions/TOTPDevice' + tags: + - authenticators + parameters: + - name: id + in: path + description: A unique integer value identifying this TOTP device. + required: true + type: integer + /authenticators/admin/webauthn/: + get: + operationId: authenticators_admin_webauthn_list + description: Viewset for WebAuthn authenticator devices (for admins) + parameters: + - name: name + 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: '' + schema: + required: + - results + - pagination + type: object + properties: + pagination: + required: + - next + - previous + - count + - current + - total_pages + - start_index + - end_index + type: object + properties: + next: + type: number + previous: + type: number + count: + type: number + current: + type: number + total_pages: + type: number + start_index: + type: number + end_index: + type: number + results: + type: array + items: + $ref: '#/definitions/WebAuthnDevice' + tags: + - authenticators + parameters: [] + /authenticators/admin/webauthn/{id}/: + get: + operationId: authenticators_admin_webauthn_read + description: Viewset for WebAuthn authenticator devices (for admins) + parameters: [] + responses: + '200': + description: '' + schema: + $ref: '#/definitions/WebAuthnDevice' + tags: + - authenticators + parameters: + - name: id + in: path + description: A unique integer value identifying this WebAuthn Device. + required: true + type: integer + /authenticators/static/: + get: + operationId: authenticators_static_list + description: Viewset for static authenticator devices + parameters: + - name: name + 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: '' + schema: + required: + - results + - pagination + type: object + properties: + pagination: + required: + - next + - previous + - count + - current + - total_pages + - start_index + - end_index + type: object + properties: + next: + type: number + previous: + type: number + count: + type: number + current: + type: number + total_pages: + type: number + start_index: + type: number + end_index: + type: number + results: + type: array + items: + $ref: '#/definitions/StaticDevice' + tags: + - authenticators + post: + operationId: authenticators_static_create + description: Viewset for static authenticator devices + parameters: + - name: data + in: body + required: true + schema: + $ref: '#/definitions/StaticDevice' + responses: + '201': + description: '' + schema: + $ref: '#/definitions/StaticDevice' + tags: + - authenticators + parameters: [] + /authenticators/static/{id}/: + get: + operationId: authenticators_static_read + description: Viewset for static authenticator devices + parameters: [] + responses: + '200': + description: '' + schema: + $ref: '#/definitions/StaticDevice' + tags: + - authenticators + put: + operationId: authenticators_static_update + description: Viewset for static authenticator devices + parameters: + - name: data + in: body + required: true + schema: + $ref: '#/definitions/StaticDevice' + responses: + '200': + description: '' + schema: + $ref: '#/definitions/StaticDevice' + tags: + - authenticators + patch: + operationId: authenticators_static_partial_update + description: Viewset for static authenticator devices + parameters: + - name: data + in: body + required: true + schema: + $ref: '#/definitions/StaticDevice' + responses: + '200': + description: '' + schema: + $ref: '#/definitions/StaticDevice' + tags: + - authenticators + delete: + operationId: authenticators_static_delete + description: Viewset for static authenticator devices + parameters: [] + responses: + '204': + description: '' + tags: + - authenticators + parameters: + - name: id + in: path + description: A unique integer value identifying this static device. + required: true + type: integer + /authenticators/totp/: + get: + operationId: authenticators_totp_list + description: Viewset for totp authenticator devices + parameters: + - name: name + 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: '' + schema: + required: + - results + - pagination + type: object + properties: + pagination: + required: + - next + - previous + - count + - current + - total_pages + - start_index + - end_index + type: object + properties: + next: + type: number + previous: + type: number + count: + type: number + current: + type: number + total_pages: + type: number + start_index: + type: number + end_index: + type: number + results: + type: array + items: + $ref: '#/definitions/TOTPDevice' + tags: + - authenticators + post: + operationId: authenticators_totp_create + description: Viewset for totp authenticator devices + parameters: + - name: data + in: body + required: true + schema: + $ref: '#/definitions/TOTPDevice' + responses: + '201': + description: '' + schema: + $ref: '#/definitions/TOTPDevice' + tags: + - authenticators + parameters: [] + /authenticators/totp/{id}/: + get: + operationId: authenticators_totp_read + description: Viewset for totp authenticator devices + parameters: [] + responses: + '200': + description: '' + schema: + $ref: '#/definitions/TOTPDevice' + tags: + - authenticators + put: + operationId: authenticators_totp_update + description: Viewset for totp authenticator devices + parameters: + - name: data + in: body + required: true + schema: + $ref: '#/definitions/TOTPDevice' + responses: + '200': + description: '' + schema: + $ref: '#/definitions/TOTPDevice' + tags: + - authenticators + patch: + operationId: authenticators_totp_partial_update + description: Viewset for totp authenticator devices + parameters: + - name: data + in: body + required: true + schema: + $ref: '#/definitions/TOTPDevice' + responses: + '200': + description: '' + schema: + $ref: '#/definitions/TOTPDevice' + tags: + - authenticators + delete: + operationId: authenticators_totp_delete + description: Viewset for totp authenticator devices + parameters: [] + responses: + '204': + description: '' + tags: + - authenticators + parameters: + - name: id + in: path + description: A unique integer value identifying this TOTP device. + required: true + type: integer + /authenticators/webauthn/: + get: + operationId: authenticators_webauthn_list + description: Viewset for WebAuthn authenticator devices + parameters: + - name: name + 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: '' + schema: + required: + - results + - pagination + type: object + properties: + pagination: + required: + - next + - previous + - count + - current + - total_pages + - start_index + - end_index + type: object + properties: + next: + type: number + previous: + type: number + count: + type: number + current: + type: number + total_pages: + type: number + start_index: + type: number + end_index: + type: number + results: + type: array + items: + $ref: '#/definitions/WebAuthnDevice' + tags: + - authenticators + post: + operationId: authenticators_webauthn_create + description: Viewset for WebAuthn authenticator devices + parameters: + - name: data + in: body + required: true + schema: + $ref: '#/definitions/WebAuthnDevice' + responses: + '201': + description: '' + schema: + $ref: '#/definitions/WebAuthnDevice' + tags: + - authenticators + parameters: [] + /authenticators/webauthn/{id}/: + get: + operationId: authenticators_webauthn_read + description: Viewset for WebAuthn authenticator devices + parameters: [] + responses: + '200': + description: '' + schema: + $ref: '#/definitions/WebAuthnDevice' + tags: + - authenticators + put: + operationId: authenticators_webauthn_update + description: Viewset for WebAuthn authenticator devices + parameters: + - name: data + in: body + required: true + schema: + $ref: '#/definitions/WebAuthnDevice' + responses: + '200': + description: '' + schema: + $ref: '#/definitions/WebAuthnDevice' + tags: + - authenticators + patch: + operationId: authenticators_webauthn_partial_update + description: Viewset for WebAuthn authenticator devices + parameters: + - name: data + in: body + required: true + schema: + $ref: '#/definitions/WebAuthnDevice' + responses: + '200': + description: '' + schema: + $ref: '#/definitions/WebAuthnDevice' + tags: + - authenticators + delete: + operationId: authenticators_webauthn_delete + description: Viewset for WebAuthn authenticator devices + parameters: [] + responses: + '204': + description: '' + tags: + - authenticators + parameters: + - name: id + in: path + description: A unique integer value identifying this WebAuthn Device. + required: true + type: integer /core/applications/: get: operationId: core_applications_list @@ -9405,6 +10110,107 @@ definitions: title: Outdated type: boolean readOnly: true + StaticDevice: + description: Serializer for static authenticator devices + required: + - name + type: object + properties: + name: + title: Name + description: The human-readable name of this device. + type: string + maxLength: 64 + minLength: 1 + token_set: + description: '' + type: array + items: + description: 'A single token belonging to a :class:`StaticDevice`. .. attribute:: + device *ForeignKey*: A foreign key to :class:`StaticDevice`. .. attribute:: + token *CharField*: A random string up to 16 characters.' + required: + - token + type: object + properties: + id: + title: ID + type: integer + readOnly: true + token: + title: Token + type: string + maxLength: 16 + minLength: 1 + device: + description: "A static :class:`~django_otp.models.Device` simply consists\ + \ of random tokens shared by the database and the user. These are\ + \ frequently used as emergency tokens in case a user's normal device\ + \ is lost or unavailable. They can be consumed in any order; each\ + \ token will be removed from the database as soon as it is used. This\ + \ model has no fields of its own, but serves as a container for :class:`StaticToken`\ + \ objects. .. attribute:: token_set The RelatedManager for our tokens." + required: + - name + - user + type: object + properties: + id: + title: ID + type: integer + readOnly: true + name: + title: Name + description: The human-readable name of this device. + type: string + maxLength: 64 + minLength: 1 + confirmed: + title: Confirmed + description: Is this device ready for use? + type: boolean + throttling_failure_timestamp: + title: Throttling failure timestamp + description: A timestamp of the last failed verification attempt. + Null if last attempt succeeded. + type: string + format: date-time + x-nullable: true + throttling_failure_count: + title: Throttling failure count + description: Number of successive failed attempts. + type: integer + maximum: 2147483647 + minimum: 0 + user: + title: User + description: The user that this device belongs to. + type: integer + readOnly: true + readOnly: true + TOTPDevice: + description: Serializer for totp authenticator devices + required: + - name + type: object + properties: + name: + title: Name + description: The human-readable name of this device. + type: string + maxLength: 64 + minLength: 1 + WebAuthnDevice: + description: Serializer for WebAuthn authenticator devices + required: + - name + type: object + properties: + name: + title: Name + type: string + maxLength: 200 + minLength: 1 Provider: title: Provider description: Provider Serializer