stages/authenticator_duo: add API to "import" devices from duo
closes #1371 Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
5fd4f56fa2
commit
7dfbcdbb81
|
@ -31,7 +31,7 @@ VALIDATION_ERROR = build_object_type(
|
||||||
"non_field_errors": build_array_type(build_standard_type(OpenApiTypes.STR)),
|
"non_field_errors": build_array_type(build_standard_type(OpenApiTypes.STR)),
|
||||||
"code": build_standard_type(OpenApiTypes.STR),
|
"code": build_standard_type(OpenApiTypes.STR),
|
||||||
},
|
},
|
||||||
required=["detail"],
|
required=[],
|
||||||
additionalProperties={},
|
additionalProperties={},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
"""AuthenticatorDuoStage API Views"""
|
"""AuthenticatorDuoStage API Views"""
|
||||||
from django_filters.rest_framework.backends import DjangoFilterBackend
|
from django_filters.rest_framework.backends import DjangoFilterBackend
|
||||||
from drf_spectacular.types import OpenApiTypes
|
from drf_spectacular.types import OpenApiTypes
|
||||||
from drf_spectacular.utils import OpenApiResponse, extend_schema
|
from drf_spectacular.utils import OpenApiParameter, OpenApiResponse, extend_schema
|
||||||
|
from guardian.shortcuts import get_objects_for_user
|
||||||
from rest_framework import mixins
|
from rest_framework import mixins
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.filters import OrderingFilter, SearchFilter
|
from rest_framework.filters import OrderingFilter, SearchFilter
|
||||||
|
@ -12,6 +13,7 @@ from rest_framework.serializers import ModelSerializer
|
||||||
from rest_framework.viewsets import GenericViewSet, ModelViewSet
|
from rest_framework.viewsets import GenericViewSet, ModelViewSet
|
||||||
|
|
||||||
from authentik.api.authorization import OwnerFilter, OwnerPermissions
|
from authentik.api.authorization import OwnerFilter, OwnerPermissions
|
||||||
|
from authentik.api.decorators import permission_required
|
||||||
from authentik.core.api.used_by import UsedByMixin
|
from authentik.core.api.used_by import UsedByMixin
|
||||||
from authentik.flows.api.stages import StageSerializer
|
from authentik.flows.api.stages import StageSerializer
|
||||||
from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice
|
from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice
|
||||||
|
@ -71,6 +73,43 @@ class AuthenticatorDuoStageViewSet(UsedByMixin, ModelViewSet):
|
||||||
return Response(status=204)
|
return Response(status=204)
|
||||||
return Response(status=420)
|
return Response(status=420)
|
||||||
|
|
||||||
|
@permission_required(
|
||||||
|
"", ["authentik_stages_authenticator_duo.add_duodevice", "authentik_core.view_user"]
|
||||||
|
)
|
||||||
|
@extend_schema(
|
||||||
|
parameters=[
|
||||||
|
OpenApiParameter(
|
||||||
|
name="duo_user_id", type=OpenApiTypes.STR, location=OpenApiParameter.QUERY
|
||||||
|
),
|
||||||
|
OpenApiParameter(
|
||||||
|
name="username", type=OpenApiTypes.STR, location=OpenApiParameter.QUERY
|
||||||
|
),
|
||||||
|
],
|
||||||
|
responses={
|
||||||
|
204: OpenApiResponse(description="Enrollment successful"),
|
||||||
|
400: OpenApiResponse(description="Device exists already"),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
@action(methods=["POST"], detail=True)
|
||||||
|
# pylint: disable=invalid-name,unused-argument
|
||||||
|
def import_devices(self, request: Request, pk: str) -> Response:
|
||||||
|
"""Import duo devices into authentik"""
|
||||||
|
stage: AuthenticatorDuoStage = self.get_object()
|
||||||
|
users = get_objects_for_user(request.user, "authentik_core.view_user").filter(
|
||||||
|
username=request.query_params.get("username", "")
|
||||||
|
)
|
||||||
|
if not users.exists():
|
||||||
|
return Response(data={"non_field_errors": ["user does not exist"]}, status=400)
|
||||||
|
devices = DuoDevice.objects.filter(
|
||||||
|
duo_user_id=request.query_params.get("duo_user_id"), user=users.first(), stage=stage
|
||||||
|
)
|
||||||
|
if devices.exists():
|
||||||
|
return Response(data={"non_field_errors": ["device exists already"]}, status=400)
|
||||||
|
DuoDevice.objects.create(
|
||||||
|
duo_user_id=request.query_params.get("duo_user_id"), user=users.first(), stage=stage
|
||||||
|
)
|
||||||
|
return Response(status=204)
|
||||||
|
|
||||||
|
|
||||||
class DuoDeviceSerializer(ModelSerializer):
|
class DuoDeviceSerializer(ModelSerializer):
|
||||||
"""Serializer for Duo authenticator devices"""
|
"""Serializer for Duo authenticator devices"""
|
||||||
|
|
39
schema.yml
39
schema.yml
|
@ -13585,6 +13585,43 @@ paths:
|
||||||
$ref: '#/components/schemas/ValidationError'
|
$ref: '#/components/schemas/ValidationError'
|
||||||
'403':
|
'403':
|
||||||
$ref: '#/components/schemas/GenericError'
|
$ref: '#/components/schemas/GenericError'
|
||||||
|
/stages/authenticator/duo/{stage_uuid}/import_devices/:
|
||||||
|
post:
|
||||||
|
operationId: stages_authenticator_duo_import_devices_create
|
||||||
|
description: Import duo devices into authentik
|
||||||
|
parameters:
|
||||||
|
- in: query
|
||||||
|
name: duo_user_id
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- in: path
|
||||||
|
name: stage_uuid
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
description: A UUID string identifying this Duo Authenticator Setup Stage.
|
||||||
|
required: true
|
||||||
|
- in: query
|
||||||
|
name: username
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
tags:
|
||||||
|
- stages
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/AuthenticatorDuoStageRequest'
|
||||||
|
required: true
|
||||||
|
security:
|
||||||
|
- authentik: []
|
||||||
|
responses:
|
||||||
|
'204':
|
||||||
|
description: Enrollment successful
|
||||||
|
'400':
|
||||||
|
description: Device exists already
|
||||||
|
'403':
|
||||||
|
$ref: '#/components/schemas/GenericError'
|
||||||
/stages/authenticator/duo/{stage_uuid}/used_by/:
|
/stages/authenticator/duo/{stage_uuid}/used_by/:
|
||||||
get:
|
get:
|
||||||
operationId: stages_authenticator_duo_used_by_list
|
operationId: stages_authenticator_duo_used_by_list
|
||||||
|
@ -29004,8 +29041,6 @@ components:
|
||||||
code:
|
code:
|
||||||
type: string
|
type: string
|
||||||
additionalProperties: {}
|
additionalProperties: {}
|
||||||
required:
|
|
||||||
- detail
|
|
||||||
Version:
|
Version:
|
||||||
type: object
|
type: object
|
||||||
description: Get running and latest version.
|
description: Get running and latest version.
|
||||||
|
|
|
@ -9,3 +9,20 @@ Go to Applications, click on Protect an Application and search for "Auth API". C
|
||||||
Copy all of the integration key, secret key and API hostname, and paste them in the Stage form.
|
Copy all of the integration key, secret key and API hostname, and paste them in the Stage form.
|
||||||
|
|
||||||
Devices created reference the stage they were created with, since the API credentials are needed to authenticate. This also means when the stage is deleted, all devices are removed.
|
Devices created reference the stage they were created with, since the API credentials are needed to authenticate. This also means when the stage is deleted, all devices are removed.
|
||||||
|
|
||||||
|
## Importing users
|
||||||
|
|
||||||
|
:::info
|
||||||
|
Due to the way the Duo API works, authentik cannot automatically import existing Duo users.
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::info
|
||||||
|
This API requires version 2021.10.1 or later
|
||||||
|
:::
|
||||||
|
|
||||||
|
You can call the `/api/v3/stages/authenticator/duo/{stage_uuid}/import_devices/` endpoint ([see here](https://goauthentik.io/api/#post-/stages/authenticator/duo/-stage_uuid-/import_devices/)) using the following parameters:
|
||||||
|
|
||||||
|
- `duo_user_id`: The Duo User's ID. This can be found in the Duo Admin Portal, navigating to the user list and clicking on a single user. Their ID is shown in th URL.
|
||||||
|
- `username`: The authentik user's username to assign the device to.
|
||||||
|
|
||||||
|
Additionally, you need to pass `stage_uuid` which is the `authenticator_duo` stage, in which you entered your API credentials.
|
||||||
|
|
Reference in New Issue