From 6c0b879b30cad10bc2eeee71ad212464450987f2 Mon Sep 17 00:00:00 2001 From: Marc 'risson' Schmitt Date: Wed, 4 Jun 2025 18:54:28 +0200 Subject: [PATCH] wip Signed-off-by: Marc 'risson' Schmitt --- authentik/stages/authenticator_duo/api.py | 40 ++++++++++++++++- authentik/stages/authenticator_duo/tasks.py | 49 --------------------- 2 files changed, 39 insertions(+), 50 deletions(-) delete mode 100644 authentik/stages/authenticator_duo/tasks.py diff --git a/authentik/stages/authenticator_duo/api.py b/authentik/stages/authenticator_duo/api.py index 0ee526ad9d..f9601684fd 100644 --- a/authentik/stages/authenticator_duo/api.py +++ b/authentik/stages/authenticator_duo/api.py @@ -1,5 +1,6 @@ """AuthenticatorDuoStage API Views""" +from typing import Any from django.http import Http404 from drf_spectacular.types import OpenApiTypes from drf_spectacular.utils import OpenApiResponse, extend_schema, inline_serializer @@ -15,6 +16,7 @@ from structlog.stdlib import get_logger from authentik.core.api.groups import GroupMemberSerializer from authentik.core.api.used_by import UsedByMixin from authentik.core.api.utils import ModelSerializer +from authentik.core.models import User from authentik.flows.api.stages import StageSerializer from authentik.rbac.decorators import permission_required from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice @@ -159,9 +161,45 @@ class AuthenticatorDuoStageViewSet(UsedByMixin, ModelViewSet): }, status=400, ) - result = duo_import_devices.send(stage.pk).get_result() + result = self._duo_import_devices(stage) return Response(data=result, status=200 if result["error"] == "" else 400) + def _duo_import_devices(self, stage: AuthenticatorDuoStage) -> dict[str, Any]: + """ + Import duo devices. This used to be a blocking task. + """ + created = 0 + if stage.admin_integration_key == "": + LOGGER.info("Stage does not have admin integration configured", stage=stage) + return {"error": "Stage does not have admin integration configured", "count": created} + client = stage.admin_client() + try: + for duo_user in client.get_users_iterator(): + user_id = duo_user.get("user_id") + username = duo_user.get("username") + + user = User.objects.filter(username=username).first() + if not user: + LOGGER.debug("User not found", username=username) + continue + device = DuoDevice.objects.filter( + duo_user_id=user_id, user=user, stage=stage + ).first() + if device: + LOGGER.debug("User already has a device with ID", id=user_id) + continue + DuoDevice.objects.create( + duo_user_id=user_id, + user=user, + stage=stage, + name="Imported Duo Authenticator", + ) + created += 1 + return {"error": "", "count": created} + except RuntimeError as exc: + LOGGER.warning("failed to get users from duo", exc=exc) + return {"error": str(exc), "count": created} + class DuoDeviceSerializer(ModelSerializer): """Serializer for Duo authenticator devices""" diff --git a/authentik/stages/authenticator_duo/tasks.py b/authentik/stages/authenticator_duo/tasks.py deleted file mode 100644 index aa9421a298..0000000000 --- a/authentik/stages/authenticator_duo/tasks.py +++ /dev/null @@ -1,49 +0,0 @@ -"""duo tasks""" - -from uuid import UUID - -from dramatiq.actor import actor -from structlog.stdlib import get_logger - -from authentik.core.models import User -from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice - -LOGGER = get_logger() - - -@actor(store_results=True) -def duo_import_devices(stage_pk: UUID): - """Import duo devices""" - created = 0 - stage: AuthenticatorDuoStage = AuthenticatorDuoStage.objects.filter(pk=stage_pk).first() - if not stage: - LOGGER.info("No stage found", pk=stage_pk) - return {"error": "No stage found", "count": created} - if stage.admin_integration_key == "": - LOGGER.info("Stage does not have admin integration configured", stage=stage) - return {"error": "Stage does not have admin integration configured", "count": created} - client = stage.admin_client() - try: - for duo_user in client.get_users_iterator(): - user_id = duo_user.get("user_id") - username = duo_user.get("username") - - user = User.objects.filter(username=username).first() - if not user: - LOGGER.debug("User not found", username=username) - continue - device = DuoDevice.objects.filter(duo_user_id=user_id, user=user, stage=stage).first() - if device: - LOGGER.debug("User already has a device with ID", id=user_id) - continue - DuoDevice.objects.create( - duo_user_id=user_id, - user=user, - stage=stage, - name="Imported Duo Authenticator", - ) - created += 1 - return {"error": "", "count": created} - except RuntimeError as exc: - LOGGER.warning("failed to get users from duo", exc=exc) - return {"error": str(exc), "count": created}