Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
This commit is contained in:
Marc 'risson' Schmitt
2025-06-04 18:54:28 +02:00
parent 0e0fb37dd7
commit 6c0b879b30
2 changed files with 39 additions and 50 deletions

View File

@ -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"""

View File

@ -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}