diff --git a/authentik/root/settings.py b/authentik/root/settings.py index 3b92967cb..1041b1cda 100644 --- a/authentik/root/settings.py +++ b/authentik/root/settings.py @@ -312,6 +312,7 @@ LOCALE_PATHS = ["./locale"] # Add a 10 minute timeout to all Celery tasks. CELERY_TASK_SOFT_TIME_LIMIT = 600 CELERY_WORKER_MAX_TASKS_PER_CHILD = 50 +CELERY_WORKER_CONCURRENCY = 2 CELERY_BEAT_SCHEDULE = { "clean_expired_models": { "task": "authentik.core.tasks.clean_expired_models", diff --git a/authentik/stages/authenticator_duo/api.py b/authentik/stages/authenticator_duo/api.py index 6b89913b9..4f93dee39 100644 --- a/authentik/stages/authenticator_duo/api.py +++ b/authentik/stages/authenticator_duo/api.py @@ -2,32 +2,28 @@ from django.http import Http404 from django_filters.rest_framework.backends import DjangoFilterBackend from drf_spectacular.types import OpenApiTypes -from drf_spectacular.utils import ( - OpenApiParameter, - OpenApiResponse, - extend_schema, - inline_serializer, -) +from drf_spectacular.utils import OpenApiResponse, extend_schema, inline_serializer from guardian.shortcuts import get_objects_for_user from rest_framework import mixins from rest_framework.decorators import action -from rest_framework.fields import ChoiceField +from rest_framework.fields import CharField, ChoiceField, IntegerField from rest_framework.filters import OrderingFilter, SearchFilter from rest_framework.permissions import IsAdminUser from rest_framework.request import Request from rest_framework.response import Response from rest_framework.serializers import ModelSerializer from rest_framework.viewsets import GenericViewSet, ModelViewSet +from structlog.stdlib import get_logger from authentik.api.authorization import OwnerFilter, OwnerPermissions from authentik.api.decorators import permission_required from authentik.core.api.used_by import UsedByMixin from authentik.flows.api.stages import StageSerializer from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice -from authentik.stages.authenticator_duo.stage import ( - SESSION_KEY_DUO_ACTIVATION_CODE, - SESSION_KEY_DUO_USER_ID, -) +from authentik.stages.authenticator_duo.stage import SESSION_KEY_DUO_ENROLL +from authentik.stages.authenticator_duo.tasks import duo_import_devices + +LOGGER = get_logger() class AuthenticatorDuoStageSerializer(StageSerializer): @@ -41,9 +37,12 @@ class AuthenticatorDuoStageSerializer(StageSerializer): "client_id", "client_secret", "api_hostname", + "admin_integration_key", + "admin_secret_key", ] extra_kwargs = { "client_secret": {"write_only": True}, + "admin_secret_key": {"write_only": True}, } @@ -85,57 +84,90 @@ class AuthenticatorDuoStageViewSet(UsedByMixin, ModelViewSet): stage: AuthenticatorDuoStage = AuthenticatorDuoStage.objects.filter(pk=pk).first() if not stage: raise Http404 - client = stage.client - user_id = self.request.session.get(SESSION_KEY_DUO_USER_ID) - activation_code = self.request.session.get(SESSION_KEY_DUO_ACTIVATION_CODE) - if not user_id or not activation_code: + client = stage.auth_client() + enroll = self.request.session.get(SESSION_KEY_DUO_ENROLL) + if not enroll: return Response(status=400) - status = client.enroll_status(user_id, activation_code) + status = client.enroll_status(enroll["user_id"], enroll["activation_code"]) return Response({"duo_response": status}) @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 - ), - ], - request=None, + request=inline_serializer( + "AuthenticatorDuoStageManualDeviceImport", + { + "duo_user_id": CharField(required=True), + "username": CharField(required=True), + }, + ), responses={ 204: OpenApiResponse(description="Enrollment successful"), - 400: OpenApiResponse(description="Device exists already"), + 400: OpenApiResponse(description="Bad request"), }, ) @action(methods=["POST"], detail=True) # pylint: disable=invalid-name,unused-argument - def import_devices(self, request: Request, pk: str) -> Response: + def import_device_manual(self, request: Request, pk: str) -> Response: """Import duo devices into authentik""" stage: AuthenticatorDuoStage = self.get_object() user = ( get_objects_for_user(request.user, "authentik_core.view_user") - .filter(username=request.query_params.get("username", "")) + .filter(username=request.data.get("username", "")) .first() ) if not user: return Response(data={"non_field_errors": ["user does not exist"]}, status=400) device = DuoDevice.objects.filter( - duo_user_id=request.query_params.get("duo_user_id"), user=user, stage=stage + duo_user_id=request.data.get("duo_user_id"), user=user, stage=stage ).first() if device: return Response(data={"non_field_errors": ["device exists already"]}, status=400) DuoDevice.objects.create( - duo_user_id=request.query_params.get("duo_user_id"), + duo_user_id=request.data.get("duo_user_id"), user=user, stage=stage, name="Imported Duo Authenticator", ) return Response(status=204) + @permission_required( + "", ["authentik_stages_authenticator_duo.add_duodevice", "authentik_core.view_user"] + ) + @extend_schema( + request=None, + responses={ + 200: inline_serializer( + "AuthenticatorDuoStageDeviceImportResponse", + fields={ + "count": IntegerField(read_only=True), + "error": CharField(read_only=True), + }, + ), + 400: OpenApiResponse(description="Bad request"), + }, + ) + @action(methods=["POST"], detail=True) + # pylint: disable=invalid-name,unused-argument + def import_devices_automatic(self, request: Request, pk: str) -> Response: + """Import duo devices into authentik""" + stage: AuthenticatorDuoStage = self.get_object() + if stage.admin_integration_key == "": + return Response( + data={ + "non_field_errors": [ + ( + "Stage does not have Admin API configured, " + "which is required for automatic imports." + ) + ] + }, + status=400, + ) + result = duo_import_devices.delay(str(stage.pk)).get() + return Response(data=result, status=200 if result["error"] == "" else 400) + class DuoDeviceSerializer(ModelSerializer): """Serializer for Duo authenticator devices""" diff --git a/authentik/stages/authenticator_duo/apps.py b/authentik/stages/authenticator_duo/apps.py index a97979865..edb7c7a12 100644 --- a/authentik/stages/authenticator_duo/apps.py +++ b/authentik/stages/authenticator_duo/apps.py @@ -1,10 +1,16 @@ """authentik duo app config""" -from django.apps import AppConfig + +from authentik.blueprints.apps import ManagedAppConfig -class AuthentikStageAuthenticatorDuoConfig(AppConfig): +class AuthentikStageAuthenticatorDuoConfig(ManagedAppConfig): """authentik duo config""" name = "authentik.stages.authenticator_duo" label = "authentik_stages_authenticator_duo" verbose_name = "authentik Stages.Authenticator.Duo" + default = True + + def reconcile_load_tasks(self): + """Load tasks""" + self.import_module("authentik.stages.authenticator_duo.tasks") diff --git a/authentik/stages/authenticator_duo/migrations/0004_authenticatorduostage_admin_integration_key_and_more.py b/authentik/stages/authenticator_duo/migrations/0004_authenticatorduostage_admin_integration_key_and_more.py new file mode 100644 index 000000000..9934ed96c --- /dev/null +++ b/authentik/stages/authenticator_duo/migrations/0004_authenticatorduostage_admin_integration_key_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 4.1.1 on 2022-09-16 15:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("authentik_stages_authenticator_duo", "0003_duodevice_last_t"), + ] + + operations = [ + migrations.AddField( + model_name="authenticatorduostage", + name="admin_integration_key", + field=models.TextField(blank=True, default=""), + ), + migrations.AddField( + model_name="authenticatorduostage", + name="admin_secret_key", + field=models.TextField(blank=True, default=""), + ), + ] diff --git a/authentik/stages/authenticator_duo/models.py b/authentik/stages/authenticator_duo/models.py index f24580787..2c43197a4 100644 --- a/authentik/stages/authenticator_duo/models.py +++ b/authentik/stages/authenticator_duo/models.py @@ -6,6 +6,7 @@ from django.db import models from django.utils.translation import gettext_lazy as _ from django.views import View from django_otp.models import Device +from duo_client.admin import Admin from duo_client.auth import Auth from rest_framework.serializers import BaseSerializer, Serializer @@ -13,14 +14,19 @@ from authentik import __version__ from authentik.core.types import UserSettingSerializer from authentik.flows.models import ConfigurableStage, Stage from authentik.lib.models import SerializerModel +from authentik.lib.utils.http import authentik_user_agent class AuthenticatorDuoStage(ConfigurableStage, Stage): """Setup Duo authenticator devices""" + api_hostname = models.TextField() + client_id = models.TextField() client_secret = models.TextField() - api_hostname = models.TextField() + + admin_integration_key = models.TextField(blank=True, default="") + admin_secret_key = models.TextField(blank=True, default="") @property def serializer(self) -> type[BaseSerializer]: @@ -34,14 +40,24 @@ class AuthenticatorDuoStage(ConfigurableStage, Stage): return AuthenticatorDuoStageView - @property - def client(self) -> Auth: + def auth_client(self) -> Auth: """Get an API Client to talk to duo""" - client = Auth( + return Auth( self.client_id, self.client_secret, self.api_hostname, - user_agent=f"authentik {__version__}", + user_agent=authentik_user_agent(), + ) + + def admin_client(self) -> Admin: + """Get an API Client to talk to duo""" + if self.admin_integration_key == "" or self.admin_secret_key == "": # nosec + raise ValueError("Admin credentials not configured") + client = Admin( + self.admin_integration_key, + self.admin_secret_key, + self.api_hostname, + user_agent=authentik_user_agent(), ) return client diff --git a/authentik/stages/authenticator_duo/stage.py b/authentik/stages/authenticator_duo/stage.py index 7f1b40225..3e77b3507 100644 --- a/authentik/stages/authenticator_duo/stage.py +++ b/authentik/stages/authenticator_duo/stage.py @@ -15,8 +15,7 @@ from authentik.flows.stage import ChallengeStageView from authentik.flows.views.executor import InvalidStageError from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice -SESSION_KEY_DUO_USER_ID = "authentik/stages/authenticator_duo/user_id" -SESSION_KEY_DUO_ACTIVATION_CODE = "authentik/stages/authenticator_duo/activation_code" +SESSION_KEY_DUO_ENROLL = "authentik/stages/authenticator_duo/enroll" class AuthenticatorDuoChallenge(WithUserInfoChallenge): @@ -39,11 +38,12 @@ class AuthenticatorDuoStageView(ChallengeStageView): response_class = AuthenticatorDuoChallengeResponse - def get_challenge(self, *args, **kwargs) -> Challenge: + def duo_enroll(self): + """Enroll User with Duo API and save results""" user = self.get_pending_user() stage: AuthenticatorDuoStage = self.executor.current_stage try: - enroll = stage.client.enroll(user.username) + enroll = stage.auth_client().enroll(user.username) except RuntimeError as exc: Event.new( EventAction.CONFIGURATION_ERROR, @@ -51,9 +51,14 @@ class AuthenticatorDuoStageView(ChallengeStageView): user=user, ).from_http(self.request, user) raise InvalidStageError(str(exc)) from exc - user_id = enroll["user_id"] - self.request.session[SESSION_KEY_DUO_USER_ID] = user_id - self.request.session[SESSION_KEY_DUO_ACTIVATION_CODE] = enroll["activation_code"] + self.request.session[SESSION_KEY_DUO_ENROLL] = enroll + return enroll + + def get_challenge(self, *args, **kwargs) -> Challenge: + stage: AuthenticatorDuoStage = self.executor.current_stage + if SESSION_KEY_DUO_ENROLL not in self.request.session: + self.duo_enroll() + enroll = self.request.session[SESSION_KEY_DUO_ENROLL] return AuthenticatorDuoChallenge( data={ "type": ChallengeTypes.NATIVE.value, @@ -73,19 +78,19 @@ class AuthenticatorDuoStageView(ChallengeStageView): def challenge_valid(self, response: ChallengeResponse) -> HttpResponse: # Duo Challenge has already been validated stage: AuthenticatorDuoStage = self.executor.current_stage - user_id = self.request.session.get(SESSION_KEY_DUO_USER_ID) - activation_code = self.request.session.get(SESSION_KEY_DUO_ACTIVATION_CODE) - enroll_status = stage.client.enroll_status(user_id, activation_code) + enroll = self.request.session.get(SESSION_KEY_DUO_ENROLL) + enroll_status = stage.auth_client().enroll_status( + enroll["user_id"], enroll["activation_code"] + ) if enroll_status != "success": - return HttpResponse(status=420) - existing_device = DuoDevice.objects.filter(duo_user_id=user_id).first() - self.request.session.pop(SESSION_KEY_DUO_USER_ID) - self.request.session.pop(SESSION_KEY_DUO_ACTIVATION_CODE) + return self.executor.stage_invalid(f"Invalid enrollment status: {enroll_status}.") + existing_device = DuoDevice.objects.filter(duo_user_id=enroll["user_id"]).first() + self.request.session.pop(SESSION_KEY_DUO_ENROLL) if not existing_device: DuoDevice.objects.create( name="Duo Authenticator", user=self.get_pending_user(), - duo_user_id=user_id, + duo_user_id=enroll["user_id"], stage=stage, last_t=now(), ) @@ -94,5 +99,4 @@ class AuthenticatorDuoStageView(ChallengeStageView): return self.executor.stage_ok() def cleanup(self): - self.request.session.pop(SESSION_KEY_DUO_USER_ID, None) - self.request.session.pop(SESSION_KEY_DUO_ACTIVATION_CODE, None) + self.request.session.pop(SESSION_KEY_DUO_ENROLL, None) diff --git a/authentik/stages/authenticator_duo/tasks.py b/authentik/stages/authenticator_duo/tasks.py new file mode 100644 index 000000000..338f6d8f0 --- /dev/null +++ b/authentik/stages/authenticator_duo/tasks.py @@ -0,0 +1,46 @@ +"""duo tasks""" +from structlog.stdlib import get_logger + +from authentik.core.models import User +from authentik.root.celery import CELERY_APP +from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice + +LOGGER = get_logger() + + +@CELERY_APP.task() +def duo_import_devices(stage_pk: str): + """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} diff --git a/authentik/stages/authenticator_duo/tests.py b/authentik/stages/authenticator_duo/tests.py new file mode 100644 index 000000000..a11bbd584 --- /dev/null +++ b/authentik/stages/authenticator_duo/tests.py @@ -0,0 +1,290 @@ +"""Test duo stage""" +from unittest.mock import MagicMock, patch +from uuid import uuid4 + +from django.test.client import RequestFactory +from django.urls import reverse + +from authentik.core.tests.utils import create_test_admin_user, create_test_flow +from authentik.flows.models import FlowStageBinding +from authentik.flows.tests import FlowTestCase +from authentik.lib.generators import generate_id +from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice +from authentik.stages.authenticator_duo.stage import SESSION_KEY_DUO_ENROLL +from authentik.stages.identification.models import IdentificationStage, UserFields + + +class AuthenticatorDuoStageTests(FlowTestCase): + """Test duo stage""" + + def setUp(self) -> None: + self.user = create_test_admin_user() + self.request_factory = RequestFactory() + + def test_client(self): + """Test Duo client setup""" + stage = AuthenticatorDuoStage( + name=generate_id(), + client_id=generate_id(), + client_secret=generate_id(), + admin_integration_key=generate_id(), + admin_secret_key=generate_id(), + api_hostname=generate_id(), + ) + self.assertEqual(stage.auth_client().ikey, stage.client_id) + self.assertEqual(stage.admin_client().ikey, stage.admin_integration_key) + stage.admin_integration_key = "" + with self.assertRaises(ValueError): + self.assertEqual(stage.admin_client().ikey, stage.admin_integration_key) + + def test_api_enrollment_invalid(self): + """Test `enrollment_status`""" + response = self.client.post( + reverse( + "authentik_api:authenticatorduostage-enrollment-status", + kwargs={ + "pk": str(uuid4()), + }, + ) + ) + self.assertEqual(response.status_code, 404) + + def test_api_enrollment(self): + """Test `enrollment_status`""" + stage = AuthenticatorDuoStage.objects.create( + name=generate_id(), + client_id=generate_id(), + client_secret=generate_id(), + api_hostname=generate_id(), + ) + + response = self.client.post( + reverse( + "authentik_api:authenticatorduostage-enrollment-status", + kwargs={ + "pk": str(stage.pk), + }, + ) + ) + self.assertEqual(response.status_code, 400) + + session = self.client.session + session[SESSION_KEY_DUO_ENROLL] = {"user_id": "foo", "activation_code": "bar"} + session.save() + + with patch("duo_client.auth.Auth.enroll_status", MagicMock(return_value="foo")): + response = self.client.post( + reverse( + "authentik_api:authenticatorduostage-enrollment-status", + kwargs={ + "pk": str(stage.pk), + }, + ) + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.content.decode(), '{"duo_response":"foo"}') + + def test_api_import_manual_invalid_username(self): + """Test `import_device_manual`""" + self.client.force_login(self.user) + stage = AuthenticatorDuoStage.objects.create( + name=generate_id(), + client_id=generate_id(), + client_secret=generate_id(), + api_hostname=generate_id(), + ) + response = self.client.post( + reverse( + "authentik_api:authenticatorduostage-import-device-manual", + kwargs={ + "pk": str(stage.pk), + }, + ), + data={ + "username": generate_id(), + }, + ) + self.assertEqual(response.status_code, 400) + + def test_api_import_manual_duplicate_device(self): + """Test `import_device_manual`""" + self.client.force_login(self.user) + stage = AuthenticatorDuoStage.objects.create( + name=generate_id(), + client_id=generate_id(), + client_secret=generate_id(), + api_hostname=generate_id(), + ) + device = DuoDevice.objects.create( + name="foo", + duo_user_id=generate_id(), + user=self.user, + stage=stage, + ) + response = self.client.post( + reverse( + "authentik_api:authenticatorduostage-import-device-manual", + kwargs={ + "pk": str(stage.pk), + }, + ), + data={ + "username": self.user.username, + "duo_user_id": device.duo_user_id, + }, + ) + self.assertEqual(response.status_code, 400) + + def test_api_import_manual(self): + """Test `import_device_manual`""" + self.client.force_login(self.user) + stage = AuthenticatorDuoStage.objects.create( + name=generate_id(), + client_id=generate_id(), + client_secret=generate_id(), + api_hostname=generate_id(), + ) + response = self.client.post( + reverse( + "authentik_api:authenticatorduostage-import-device-manual", + kwargs={ + "pk": str(stage.pk), + }, + ), + data={ + "username": self.user.username, + "duo_user_id": "foo", + }, + ) + self.assertEqual(response.status_code, 204) + + def test_api_import_automatic_invalid(self): + """test `import_devices_automatic`""" + self.client.force_login(self.user) + stage = AuthenticatorDuoStage.objects.create( + name=generate_id(), + client_id=generate_id(), + client_secret=generate_id(), + api_hostname=generate_id(), + ) + response = self.client.post( + reverse( + "authentik_api:authenticatorduostage-import-devices-automatic", + kwargs={ + "pk": str(stage.pk), + }, + ), + ) + self.assertEqual(response.status_code, 400) + + def test_api_import_automatic(self): + """test `import_devices_automatic`""" + self.client.force_login(self.user) + stage = AuthenticatorDuoStage.objects.create( + name=generate_id(), + client_id=generate_id(), + client_secret=generate_id(), + admin_integration_key=generate_id(), + admin_secret_key=generate_id(), + api_hostname=generate_id(), + ) + device = DuoDevice.objects.create( + name="foo", + duo_user_id=generate_id(), + user=self.user, + stage=stage, + ) + with patch( + "duo_client.admin.Admin.get_users_iterator", + MagicMock( + return_value=[ + { + "user_id": "foo", + "username": "bar", + }, + { + "user_id": device.duo_user_id, + "username": self.user.username, + }, + { + "user_id": generate_id(), + "username": self.user.username, + }, + ] + ), + ): + response = self.client.post( + reverse( + "authentik_api:authenticatorduostage-import-devices-automatic", + kwargs={ + "pk": str(stage.pk), + }, + ), + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.content.decode(), '{"error":"","count":1}') + + def test_stage_enroll_basic(self): + """Test stage""" + conf_stage = IdentificationStage.objects.create( + name=generate_id(), + user_fields=[ + UserFields.USERNAME, + ], + ) + stage = AuthenticatorDuoStage.objects.create( + name=generate_id(), + client_id=generate_id(), + client_secret=generate_id(), + api_hostname=generate_id(), + ) + flow = create_test_flow() + FlowStageBinding.objects.create(target=flow, stage=conf_stage, order=0) + FlowStageBinding.objects.create(target=flow, stage=stage, order=1) + + response = self.client.post( + reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), + {"uid_field": self.user.username}, + ) + self.assertEqual(response.status_code, 302) + + enroll_mock = MagicMock( + return_value={ + "user_id": "foo", + "activation_barcode": "bar", + "activation_code": "bar", + } + ) + with patch("duo_client.auth.Auth.enroll", enroll_mock): + response = self.client.get( + reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), + follow=True, + ) + self.assertStageResponse( + response, + flow, + component="ak-stage-authenticator-duo", + pending_user=self.user.username, + activation_barcode="bar", + activation_code="bar", + ) + + response = self.client.get( + reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), + follow=True, + ) + self.assertStageResponse( + response, + flow, + component="ak-stage-authenticator-duo", + pending_user=self.user.username, + activation_barcode="bar", + activation_code="bar", + ) + self.assertEqual(enroll_mock.call_count, 1) + + with patch("duo_client.auth.Auth.enroll_status", MagicMock(return_value="success")): + response = self.client.post( + reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), {} + ) + self.assertStageRedirects(response, reverse("authentik_core:root-redirect")) diff --git a/authentik/stages/authenticator_validate/challenge.py b/authentik/stages/authenticator_validate/challenge.py index c1556f5b0..0d5c7fff0 100644 --- a/authentik/stages/authenticator_validate/challenge.py +++ b/authentik/stages/authenticator_validate/challenge.py @@ -175,7 +175,7 @@ def validate_challenge_duo(device_pk: int, stage_view: StageView, user: User) -> ).name try: - response = stage.client.auth( + response = stage.auth_client().auth( "auto", user_id=device.duo_user_id, ipaddr=get_client_ip(stage_view.request), diff --git a/authentik/stages/authenticator_validate/tests/test_duo.py b/authentik/stages/authenticator_validate/tests/test_duo.py index 1c793d986..07c66b4c9 100644 --- a/authentik/stages/authenticator_validate/tests/test_duo.py +++ b/authentik/stages/authenticator_validate/tests/test_duo.py @@ -44,14 +44,18 @@ class AuthenticatorValidateStageDuoTests(FlowTestCase): stage=stage, ) with patch( - "authentik.stages.authenticator_duo.models.AuthenticatorDuoStage.client", + "authentik.stages.authenticator_duo.models.AuthenticatorDuoStage.auth_client", MagicMock( - auth=MagicMock( - return_value={ - "result": "allow", - "status": "allow", - "status_msg": "Success. Logging you in...", - } + MagicMock( + return_value=MagicMock( + auth=MagicMock( + return_value={ + "result": "allow", + "status": "allow", + "status_msg": "Success. Logging you in...", + } + ) + ) ) ), ): @@ -70,8 +74,8 @@ class AuthenticatorValidateStageDuoTests(FlowTestCase): ), ) with patch( - "authentik.stages.authenticator_duo.models.AuthenticatorDuoStage.client", - MagicMock(auth=MagicMock(return_value={"result": "deny"})), + "authentik.stages.authenticator_duo.models.AuthenticatorDuoStage.auth_client", + MagicMock(return_value=MagicMock(auth=MagicMock(return_value={"result": "deny"}))), ): with self.assertRaises(ValidationError): validate_challenge_duo( diff --git a/locale/en/LC_MESSAGES/django.po b/locale/en/LC_MESSAGES/django.po index 1e44b2ead..d4be33665 100644 --- a/locale/en/LC_MESSAGES/django.po +++ b/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-09-16 15:57+0000\n" +"POT-Creation-Date: 2022-09-16 21:25+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -1328,19 +1328,19 @@ msgstr "" msgid "SAML Sources" msgstr "" -#: authentik/stages/authenticator_duo/models.py:65 +#: authentik/stages/authenticator_duo/models.py:80 msgid "Duo Authenticator Setup Stage" msgstr "" -#: authentik/stages/authenticator_duo/models.py:66 +#: authentik/stages/authenticator_duo/models.py:81 msgid "Duo Authenticator Setup Stages" msgstr "" -#: authentik/stages/authenticator_duo/models.py:90 +#: authentik/stages/authenticator_duo/models.py:105 msgid "Duo Device" msgstr "" -#: authentik/stages/authenticator_duo/models.py:91 +#: authentik/stages/authenticator_duo/models.py:106 msgid "Duo Devices" msgstr "" diff --git a/schema.yml b/schema.yml index b90de7dac..bdcfff2d2 100644 --- a/schema.yml +++ b/schema.yml @@ -18650,15 +18650,11 @@ paths: schema: $ref: '#/components/schemas/GenericError' description: '' - /stages/authenticator/duo/{stage_uuid}/import_devices/: + /stages/authenticator/duo/{stage_uuid}/import_device_manual/: post: - operationId: stages_authenticator_duo_import_devices_create + operationId: stages_authenticator_duo_import_device_manual_create description: Import duo devices into authentik parameters: - - in: query - name: duo_user_id - schema: - type: string - in: path name: stage_uuid schema: @@ -18666,19 +18662,52 @@ paths: 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/AuthenticatorDuoStageManualDeviceImportRequest' + required: true security: - authentik: [] responses: '204': description: Enrollment successful '400': - description: Device exists already + description: Bad request + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + /stages/authenticator/duo/{stage_uuid}/import_devices_automatic/: + post: + operationId: stages_authenticator_duo_import_devices_automatic_create + description: Import duo devices into authentik + parameters: + - in: path + name: stage_uuid + schema: + type: string + format: uuid + description: A UUID string identifying this Duo Authenticator Setup Stage. + required: true + tags: + - stages + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/AuthenticatorDuoStageDeviceImportResponse' + description: '' + '400': + description: Bad request '403': content: application/json: @@ -24983,6 +25012,8 @@ components: type: string api_hostname: type: string + admin_integration_key: + type: string required: - api_hostname - client_id @@ -24992,6 +25023,30 @@ components: - pk - verbose_name - verbose_name_plural + AuthenticatorDuoStageDeviceImportResponse: + type: object + properties: + count: + type: integer + readOnly: true + error: + type: string + readOnly: true + required: + - count + - error + AuthenticatorDuoStageManualDeviceImportRequest: + type: object + properties: + duo_user_id: + type: string + minLength: 1 + username: + type: string + minLength: 1 + required: + - duo_user_id + - username AuthenticatorDuoStageRequest: type: object description: AuthenticatorDuoStage Serializer @@ -25019,6 +25074,11 @@ components: api_hostname: type: string minLength: 1 + admin_integration_key: + type: string + admin_secret_key: + type: string + writeOnly: true required: - api_hostname - client_id @@ -32518,6 +32578,11 @@ components: api_hostname: type: string minLength: 1 + admin_integration_key: + type: string + admin_secret_key: + type: string + writeOnly: true PatchedAuthenticatorSMSStageRequest: type: object description: AuthenticatorSMSStage Serializer diff --git a/web/src/admin/policies/PolicyTestForm.ts b/web/src/admin/policies/PolicyTestForm.ts index f7286f576..62438867d 100644 --- a/web/src/admin/policies/PolicyTestForm.ts +++ b/web/src/admin/policies/PolicyTestForm.ts @@ -141,8 +141,10 @@ export class PolicyTestForm extends Form { - > +

${t`Set custom attributes using YAML or JSON.`} diff --git a/web/src/admin/stages/StageListPage.ts b/web/src/admin/stages/StageListPage.ts index 039063092..b85cff250 100644 --- a/web/src/admin/stages/StageListPage.ts +++ b/web/src/admin/stages/StageListPage.ts @@ -33,6 +33,7 @@ import { t } from "@lingui/macro"; import { TemplateResult, html } from "lit"; import { customElement, property } from "lit/decorators.js"; import { ifDefined } from "lit/directives/if-defined.js"; +import { until } from "lit/directives/until.js"; import { Stage, StagesApi } from "@goauthentik/api"; @@ -99,6 +100,22 @@ export class StageListPage extends TablePage { `; } + async renderStageActions(stage: Stage): Promise { + if (stage.component === "ak-stage-authenticator-duo-form") { + await import("@goauthentik/admin/stages/authenticator_duo/DuoDeviceImportForm"); + return html` + ${t`Import`} + ${t`Import Duo device`} + + + + `; + } + return html``; + } + row(item: Stage): TemplateResult[] { return [ html`

@@ -114,21 +131,22 @@ export class StageListPage extends TablePage { `; })} `, - html` - ${t`Update`} - ${t`Update ${item.verboseName}`} - - - - `, + html` + ${t`Update`} + ${t`Update ${item.verboseName}`} + + + + + ${until(this.renderStageActions(item))}`, ]; } diff --git a/web/src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts b/web/src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts index aad30e781..fd19b8451 100644 --- a/web/src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +++ b/web/src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts @@ -61,8 +61,20 @@ export class AuthenticatorDuoStageForm extends ModelForm + + + - ${t`Stage-specific settings`} + ${t`Duo Auth API`}
+
+
+ + ${t`Duo Admin API (optional)`} + + ${t`When using a Duo MFA, Access or Beyond plan, an Admin API application can be created. + This will allow authentik to import devices automatically.`} + +
+ + + +
+
+ + ${t`Stage-specific settings`} +
+ ${until( + new CoreApi(DEFAULT_CONFIG) + .coreUsersList({ + ordering: "username", + }) + .then((users) => { + return users.results.map((user) => { + return html``; + }); + }), + html``, + )} + +

+ ${t`The user in authentik this device will be assigned to.`} +

+
+ + +

+ ${t`The user ID in Duo.`} + ${t`Can be either the username (found in the Users list) or the ID (can be found in the URL after clicking on a user).`} +

+
+ `; + } + + renderFormAutomatic(): TemplateResult { + return html` +
+ + { + return new StagesApi(DEFAULT_CONFIG) + .stagesAuthenticatorDuoImportDevicesAutomaticCreate({ + stageUuid: this.instance?.pk || "", + }) + .then((res) => { + showMessage({ + level: MessageLevel.info, + message: t`Successfully imported ${res.count} devices.`, + }); + const modal = this.parentElement as ModalForm; + modal.open = false; + }); + }} + > + ${t`Start automatic import`} + + +
+ ${t`Or manually import`} +
+ `; + } +} diff --git a/web/src/elements/forms/ModalForm.ts b/web/src/elements/forms/ModalForm.ts index 25658b829..9f6cfdf8e 100644 --- a/web/src/elements/forms/ModalForm.ts +++ b/web/src/elements/forms/ModalForm.ts @@ -23,7 +23,7 @@ export class ModalForm extends ModalButton { @property({ type: String }) cancelText = t`Cancel`; - confirm(): Promise { + async confirm(): Promise { const form = this.querySelector>("[slot=form]"); if (!form) { return Promise.reject(t`No form found`); diff --git a/web/src/locales/de.po b/web/src/locales/de.po index 70461a4eb..c6308952a 100644 --- a/web/src/locales/de.po +++ b/web/src/locales/de.po @@ -832,6 +832,10 @@ msgstr "" msgid "Callback URL" msgstr "Callback URL" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Can be either the username (found in the Users list) or the ID (can be found in the URL after clicking on a user)." +msgstr "" + #~ msgid "Can be in the format of 'unix://' when connecting to a local docker daemon, or 'https://:2376' when connecting to a remote system." #~ msgstr "Kann das Format \"unix://\" haben, wenn eine Verbindung zu einem lokalen Docker-Daemon besteht, oder \"https://:2376\", wenn eine Verbindung zu einem entfernten System besteht." @@ -1837,10 +1841,22 @@ msgstr "Dummy-Stage zum Testen verwendet. Zeigt eine einfache Schaltfläche zum #~ msgid "Duo" #~ msgstr "Duo" +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "Duo Admin API (optional)" +msgstr "" + +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "Duo Auth API" +msgstr "" + #: src/admin/stages/authenticator_validate/AuthenticatorValidateStageForm.ts msgid "Duo Authenticators" msgstr "Duo-Authentifikatoren" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Duo User ID" +msgstr "" + #: src/flow/stages/authenticator_duo/AuthenticatorDuoStage.ts msgid "Duo activation" msgstr "Duo-Aktivierung" @@ -2772,9 +2788,16 @@ msgstr "Identitätswechsel gestarted" #: src/admin/flows/FlowListPage.ts #: src/admin/flows/FlowListPage.ts +#: src/admin/stages/StageListPage.ts +#: src/admin/stages/StageListPage.ts msgid "Import" msgstr "Importieren" +#: src/admin/stages/StageListPage.ts +#: src/admin/stages/StageListPage.ts +msgid "Import Duo device" +msgstr "" + #: src/admin/flows/FlowListPage.ts msgid "Import Flow" msgstr "Ablauf importieren" @@ -2821,6 +2844,7 @@ msgstr "Ansprüche in id_token berücksichtigen" msgid "Integration" msgstr "Integration" +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts msgid "Integration key" msgstr "Integrationsschlüssel" @@ -3138,6 +3162,7 @@ msgstr "Wird geladen" #: src/admin/sources/saml/SAMLSourceForm.ts #: src/admin/sources/saml/SAMLSourceForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts #: src/admin/stages/authenticator_sms/AuthenticatorSMSStageForm.ts #: src/admin/stages/authenticator_static/AuthenticatorStaticStageForm.ts #: src/admin/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts @@ -4756,6 +4781,7 @@ msgstr "Suchmodus" msgid "Search..." msgstr "Suche..." +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts msgid "Secret key" msgstr "Geheimer Schlüssel" @@ -5438,6 +5464,10 @@ msgstr "Zertifikat-Schlüsselpaar erfolgreich generiert." msgid "Successfully generated recovery link" msgstr "Erfolgreich generierter Wiederherstellungslink" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Successfully imported device." +msgstr "" + #: src/admin/flows/FlowImportForm.ts msgid "Successfully imported flow." msgstr "Ablauf erfolgreich importiert." @@ -5826,6 +5856,14 @@ msgstr "Der Anfang für gidNumbers, diese Zahl wird zu einer aus der group.Pk ge msgid "The start for uidNumbers, this number is added to the user.Pk to make sure that the numbers aren't too low for POSIX users. Default is 2000 to ensure that we don't collide with local users uidNumber" msgstr "Der Anfang für uidNumbers, diese Zahl wird zu user.Pk hinzugefügt, um sicherzustellen, dass die Zahlen für POSIX-Benutzer nicht zu niedrig sind. Standardwert ist 2000, um sicherzustellen, dass wir nicht mit lokalen uidNumbers der Benutzer kollidieren" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "The user ID in Duo." +msgstr "" + +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "The user in authentik this device will be assigned to." +msgstr "" + #: src/admin/flows/BoundStagesList.ts msgid "These bindings control if this stage will be applied to the flow." msgstr "Diese Bindungen steuern, ob diese Stufe auf den Ablauf angewendet wird." @@ -6430,6 +6468,7 @@ msgstr "Nutze diese Umgebung für jede Domain, die keine eigene Umgebung hat." #: src/admin/policies/PolicyBindingForm.ts #: src/admin/policies/PolicyTestForm.ts #: src/admin/property-mappings/PropertyMappingTestForm.ts +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts #: src/admin/tokens/TokenForm.ts #: src/admin/tokens/TokenListPage.ts #: src/admin/users/RelatedUserList.ts @@ -6845,6 +6884,12 @@ msgstr "Wenn diese Option aktiviert ist, werden alle Ausführungen dieser Richtl msgid "When used in conjunction with a User Write stage, use attributes.foo to write attributes." msgstr "Bei Verwendung in Verbindung mit einer User Write-Phase verwenden Sie attributes.foo zum Schreiben von Attributen." +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "" +"When using a Duo MFA, Access or Beyond plan, an Admin API application can be created.\n" +"This will allow authentik to import devices automatically." +msgstr "" + #: src/admin/tenants/TenantForm.ts msgid "When using an external logging solution for archiving, this can be set to \"minutes=5\"." msgstr "Falls eine externe Protokollierlösung zum archivieren genutzt wird, könnte dies auf „minutes=5“ gesetzt werden." diff --git a/web/src/locales/en.po b/web/src/locales/en.po index f17af3aa2..d0d3c8cbe 100644 --- a/web/src/locales/en.po +++ b/web/src/locales/en.po @@ -829,6 +829,10 @@ msgstr "Caddy (Standalone)" msgid "Callback URL" msgstr "Callback URL" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Can be either the username (found in the Users list) or the ID (can be found in the URL after clicking on a user)." +msgstr "Can be either the username (found in the Users list) or the ID (can be found in the URL after clicking on a user)." + #: src/pages/outposts/ServiceConnectionDockerForm.ts #~ msgid "Can be in the format of 'unix://' when connecting to a local docker daemon, or 'https://:2376' when connecting to a remote system." #~ msgstr "Can be in the format of 'unix://' when connecting to a local docker daemon, or 'https://:2376' when connecting to a remote system." @@ -1863,10 +1867,22 @@ msgstr "Dummy stage used for testing. Shows a simple continue button and always #~ msgid "Duo" #~ msgstr "Duo" +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "Duo Admin API (optional)" +msgstr "Duo Admin API (optional)" + +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "Duo Auth API" +msgstr "Duo Auth API" + #: src/admin/stages/authenticator_validate/AuthenticatorValidateStageForm.ts msgid "Duo Authenticators" msgstr "Duo Authenticators" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Duo User ID" +msgstr "Duo User ID" + #: src/flow/stages/authenticator_duo/AuthenticatorDuoStage.ts msgid "Duo activation" msgstr "Duo activation" @@ -2817,9 +2833,16 @@ msgstr "Impersonation started" #: src/admin/flows/FlowListPage.ts #: src/admin/flows/FlowListPage.ts +#: src/admin/stages/StageListPage.ts +#: src/admin/stages/StageListPage.ts msgid "Import" msgstr "Import" +#: src/admin/stages/StageListPage.ts +#: src/admin/stages/StageListPage.ts +msgid "Import Duo device" +msgstr "Import Duo device" + #: src/admin/flows/FlowListPage.ts msgid "Import Flow" msgstr "Import Flow" @@ -2866,6 +2889,7 @@ msgstr "Include claims in id_token" msgid "Integration" msgstr "Integration" +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts msgid "Integration key" msgstr "Integration key" @@ -3190,6 +3214,7 @@ msgstr "Loading" #: src/admin/sources/saml/SAMLSourceForm.ts #: src/admin/sources/saml/SAMLSourceForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts #: src/admin/stages/authenticator_sms/AuthenticatorSMSStageForm.ts #: src/admin/stages/authenticator_static/AuthenticatorStaticStageForm.ts #: src/admin/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts @@ -4836,6 +4861,7 @@ msgstr "Search mode" msgid "Search..." msgstr "Search..." +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts msgid "Secret key" msgstr "Secret key" @@ -5544,6 +5570,10 @@ msgstr "Successfully generated certificate-key pair." msgid "Successfully generated recovery link" msgstr "Successfully generated recovery link" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Successfully imported device." +msgstr "Successfully imported device." + #: src/admin/flows/FlowImportForm.ts msgid "Successfully imported flow." msgstr "Successfully imported flow." @@ -5942,6 +5972,14 @@ msgstr "The start for gidNumbers, this number is added to a number generated fro msgid "The start for uidNumbers, this number is added to the user.Pk to make sure that the numbers aren't too low for POSIX users. Default is 2000 to ensure that we don't collide with local users uidNumber" msgstr "The start for uidNumbers, this number is added to the user.Pk to make sure that the numbers aren't too low for POSIX users. Default is 2000 to ensure that we don't collide with local users uidNumber" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "The user ID in Duo." +msgstr "The user ID in Duo." + +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "The user in authentik this device will be assigned to." +msgstr "The user in authentik this device will be assigned to." + #: src/admin/flows/BoundStagesList.ts msgid "These bindings control if this stage will be applied to the flow." msgstr "These bindings control if this stage will be applied to the flow." @@ -6551,6 +6589,7 @@ msgstr "Use this tenant for each domain that doesn't have a dedicated tenant." #: src/admin/policies/PolicyBindingForm.ts #: src/admin/policies/PolicyTestForm.ts #: src/admin/property-mappings/PropertyMappingTestForm.ts +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts #: src/admin/tokens/TokenForm.ts #: src/admin/tokens/TokenListPage.ts #: src/admin/users/RelatedUserList.ts @@ -6975,6 +7014,14 @@ msgstr "When this option is enabled, all executions of this policy will be logge msgid "When used in conjunction with a User Write stage, use attributes.foo to write attributes." msgstr "When used in conjunction with a User Write stage, use attributes.foo to write attributes." +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "" +"When using a Duo MFA, Access or Beyond plan, an Admin API application can be created.\n" +"This will allow authentik to import devices automatically." +msgstr "" +"When using a Duo MFA, Access or Beyond plan, an Admin API application can be created.\n" +"This will allow authentik to import devices automatically." + #: src/admin/tenants/TenantForm.ts msgid "When using an external logging solution for archiving, this can be set to \"minutes=5\"." msgstr "When using an external logging solution for archiving, this can be set to \"minutes=5\"." diff --git a/web/src/locales/es.po b/web/src/locales/es.po index b0d4d4895..893d90b66 100644 --- a/web/src/locales/es.po +++ b/web/src/locales/es.po @@ -822,6 +822,10 @@ msgstr "" msgid "Callback URL" msgstr "URL de devolución de llamada" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Can be either the username (found in the Users list) or the ID (can be found in the URL after clicking on a user)." +msgstr "" + #~ msgid "Can be in the format of 'unix://' when connecting to a local docker daemon, or 'https://:2376' when connecting to a remote system." #~ msgstr "Puede tener el formato «unix://» cuando se conecta a un demonio de docker local, o «https://:2376» cuando se conecta a un sistema remoto." @@ -1828,10 +1832,22 @@ msgstr "Escenario ficticio utilizado para las pruebas. Muestra un botón de cont #~ msgid "Duo" #~ msgstr "Dúo" +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "Duo Admin API (optional)" +msgstr "" + +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "Duo Auth API" +msgstr "" + #: src/admin/stages/authenticator_validate/AuthenticatorValidateStageForm.ts msgid "Duo Authenticators" msgstr "Autenticadores duo" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Duo User ID" +msgstr "" + #: src/flow/stages/authenticator_duo/AuthenticatorDuoStage.ts msgid "Duo activation" msgstr "Activación dúo" @@ -2763,9 +2779,16 @@ msgstr "Se ha iniciado la suplantación" #: src/admin/flows/FlowListPage.ts #: src/admin/flows/FlowListPage.ts +#: src/admin/stages/StageListPage.ts +#: src/admin/stages/StageListPage.ts msgid "Import" msgstr "Importación" +#: src/admin/stages/StageListPage.ts +#: src/admin/stages/StageListPage.ts +msgid "Import Duo device" +msgstr "" + #: src/admin/flows/FlowListPage.ts msgid "Import Flow" msgstr "Flujo de importación" @@ -2812,6 +2835,7 @@ msgstr "Incluir reclamos en id_token" msgid "Integration" msgstr "Integración" +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts msgid "Integration key" msgstr "Clave de integración" @@ -3131,6 +3155,7 @@ msgstr "Cargando" #: src/admin/sources/saml/SAMLSourceForm.ts #: src/admin/sources/saml/SAMLSourceForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts #: src/admin/stages/authenticator_sms/AuthenticatorSMSStageForm.ts #: src/admin/stages/authenticator_static/AuthenticatorStaticStageForm.ts #: src/admin/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts @@ -4749,6 +4774,7 @@ msgstr "Modo de búsqueda" msgid "Search..." msgstr "Buscar..." +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts msgid "Secret key" msgstr "Clave secreta" @@ -5432,6 +5458,10 @@ msgstr "Se ha generado correctamente el par de claves de certificado." msgid "Successfully generated recovery link" msgstr "Enlace de recuperación generado correctamente" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Successfully imported device." +msgstr "" + #: src/admin/flows/FlowImportForm.ts msgid "Successfully imported flow." msgstr "El flujo se importó correctamente." @@ -5820,6 +5850,14 @@ msgstr "El comienzo de GIDNumbers, este número se agrega a un número generado msgid "The start for uidNumbers, this number is added to the user.Pk to make sure that the numbers aren't too low for POSIX users. Default is 2000 to ensure that we don't collide with local users uidNumber" msgstr "El comienzo de UIDNumbers, este número se agrega a User.pk para asegurarse de que los números no sean demasiado bajos para los usuarios de POSIX. El valor predeterminado es 2000 para garantizar que no colisionemos con el UIDNumber de los usuarios locales" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "The user ID in Duo." +msgstr "" + +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "The user in authentik this device will be assigned to." +msgstr "" + #: src/admin/flows/BoundStagesList.ts msgid "These bindings control if this stage will be applied to the flow." msgstr "Estos enlaces controlan si esta etapa se aplicará al flujo." @@ -6424,6 +6462,7 @@ msgstr "Use este inquilino para cada dominio que no tenga un inquilino dedicado. #: src/admin/policies/PolicyBindingForm.ts #: src/admin/policies/PolicyTestForm.ts #: src/admin/property-mappings/PropertyMappingTestForm.ts +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts #: src/admin/tokens/TokenForm.ts #: src/admin/tokens/TokenListPage.ts #: src/admin/users/RelatedUserList.ts @@ -6839,6 +6878,12 @@ msgstr "Cuando se habilita esta opción, se registrarán todas las ejecuciones d msgid "When used in conjunction with a User Write stage, use attributes.foo to write attributes." msgstr "Cuando se usa junto con una etapa User Write, use attribues.foo para escribir atributos." +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "" +"When using a Duo MFA, Access or Beyond plan, an Admin API application can be created.\n" +"This will allow authentik to import devices automatically." +msgstr "" + #: src/admin/tenants/TenantForm.ts msgid "When using an external logging solution for archiving, this can be set to \"minutes=5\"." msgstr "Cuando se utiliza una solución de registro externa para archivar, se puede establecer en «minutes = 5\"." diff --git a/web/src/locales/fr_FR.po b/web/src/locales/fr_FR.po index 9b10b8ff4..009bc8a1d 100644 --- a/web/src/locales/fr_FR.po +++ b/web/src/locales/fr_FR.po @@ -828,6 +828,10 @@ msgstr "" msgid "Callback URL" msgstr "URL de rappel" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Can be either the username (found in the Users list) or the ID (can be found in the URL after clicking on a user)." +msgstr "" + #: src/pages/outposts/ServiceConnectionDockerForm.ts #~ msgid "Can be in the format of 'unix://' when connecting to a local docker daemon, or 'https://:2376' when connecting to a remote system." #~ msgstr "Peut être au format \"unix://\" pour une connexion à un service docker local, ou \"https://:2376\" pour une connexion à un système distant." @@ -1846,10 +1850,22 @@ msgstr "Étape factice utilisée pour les tests. Montre un simple bouton continu #~ msgid "Duo" #~ msgstr "Duo" +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "Duo Admin API (optional)" +msgstr "" + +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "Duo Auth API" +msgstr "" + #: src/admin/stages/authenticator_validate/AuthenticatorValidateStageForm.ts msgid "Duo Authenticators" msgstr "Authentificateurs Duo" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Duo User ID" +msgstr "" + #: src/flow/stages/authenticator_duo/AuthenticatorDuoStage.ts msgid "Duo activation" msgstr "Activation Duo" @@ -2793,9 +2809,16 @@ msgstr "Début de l'appropriation utilisateur" #: src/admin/flows/FlowListPage.ts #: src/admin/flows/FlowListPage.ts +#: src/admin/stages/StageListPage.ts +#: src/admin/stages/StageListPage.ts msgid "Import" msgstr "Importer" +#: src/admin/stages/StageListPage.ts +#: src/admin/stages/StageListPage.ts +msgid "Import Duo device" +msgstr "" + #: src/admin/flows/FlowListPage.ts msgid "Import Flow" msgstr "Importer un flux" @@ -2842,6 +2865,7 @@ msgstr "Include les demandes utilisateurs dans id_token" msgid "Integration" msgstr "Intégration" +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts msgid "Integration key" msgstr "Clé d'intégration" @@ -3162,6 +3186,7 @@ msgstr "Chargement en cours" #: src/admin/sources/saml/SAMLSourceForm.ts #: src/admin/sources/saml/SAMLSourceForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts #: src/admin/stages/authenticator_sms/AuthenticatorSMSStageForm.ts #: src/admin/stages/authenticator_static/AuthenticatorStaticStageForm.ts #: src/admin/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts @@ -4797,6 +4822,7 @@ msgstr "" msgid "Search..." msgstr "Rechercher..." +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts msgid "Secret key" msgstr "Clé secrète" @@ -5493,6 +5519,10 @@ msgstr "Paire clé/certificat générée avec succès." msgid "Successfully generated recovery link" msgstr "Lien de récupération généré avec succès" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Successfully imported device." +msgstr "" + #: src/admin/flows/FlowImportForm.ts msgid "Successfully imported flow." msgstr "Flux importé avec succès" @@ -5880,6 +5910,14 @@ msgstr "Ce nombre est ajouté au nombre généré à partir de group.Pk pour s'a msgid "The start for uidNumbers, this number is added to the user.Pk to make sure that the numbers aren't too low for POSIX users. Default is 2000 to ensure that we don't collide with local users uidNumber" msgstr "Ce nombre est ajouté au nombre généré à partir de user.Pk pour s'assurer que ceux-ci ne sont pas trop bas pour les utilisateurs POSIX. La valeur par défaut est 2000 pour éviter des collisions avec les uidNumber des utilisateurs locaux." +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "The user ID in Duo." +msgstr "" + +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "The user in authentik this device will be assigned to." +msgstr "" + #: src/admin/flows/BoundStagesList.ts msgid "These bindings control if this stage will be applied to the flow." msgstr "Ces liaisons contrôlent si cette étape sera appliquée au flux." @@ -6482,6 +6520,7 @@ msgstr "Utilisez ce locataire pour chaque domaine qui ne dispose pas d'un locata #: src/admin/policies/PolicyBindingForm.ts #: src/admin/policies/PolicyTestForm.ts #: src/admin/property-mappings/PropertyMappingTestForm.ts +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts #: src/admin/tokens/TokenForm.ts #: src/admin/tokens/TokenListPage.ts #: src/admin/users/RelatedUserList.ts @@ -6902,6 +6941,12 @@ msgstr "Si activée, toutes les exécutions de cette politique seront enregistr msgid "When used in conjunction with a User Write stage, use attributes.foo to write attributes." msgstr "" +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "" +"When using a Duo MFA, Access or Beyond plan, an Admin API application can be created.\n" +"This will allow authentik to import devices automatically." +msgstr "" + #: src/admin/tenants/TenantForm.ts msgid "When using an external logging solution for archiving, this can be set to \"minutes=5\"." msgstr "En cas d'utilisation d'une solution de journalisation externe pour l'archivage, cette valeur peut être fixée à \"minutes=5\"." diff --git a/web/src/locales/pl.po b/web/src/locales/pl.po index c0598e12f..14f08a0cb 100644 --- a/web/src/locales/pl.po +++ b/web/src/locales/pl.po @@ -819,6 +819,10 @@ msgstr "" msgid "Callback URL" msgstr "URL wywołania zwrotnego" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Can be either the username (found in the Users list) or the ID (can be found in the URL after clicking on a user)." +msgstr "" + #~ msgid "Can be in the format of 'unix://' when connecting to a local docker daemon, or 'https://:2376' when connecting to a remote system." #~ msgstr "Może mieć format „unix://” w przypadku łączenia się z lokalnym demonem dockera lub „https://:2376” w przypadku łączenia się z systemem zdalnym." @@ -1825,10 +1829,22 @@ msgstr "Atrapa etapu używana do testowania. Pokazuje prosty przycisk kontynuuj #~ msgid "Duo" #~ msgstr "Duo" +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "Duo Admin API (optional)" +msgstr "" + +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "Duo Auth API" +msgstr "" + #: src/admin/stages/authenticator_validate/AuthenticatorValidateStageForm.ts msgid "Duo Authenticators" msgstr "Uwierzytelniacze Duo" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Duo User ID" +msgstr "" + #: src/flow/stages/authenticator_duo/AuthenticatorDuoStage.ts msgid "Duo activation" msgstr "Aktywacja Duo" @@ -2760,9 +2776,16 @@ msgstr "Rozpoczęto podszywanie się" #: src/admin/flows/FlowListPage.ts #: src/admin/flows/FlowListPage.ts +#: src/admin/stages/StageListPage.ts +#: src/admin/stages/StageListPage.ts msgid "Import" msgstr "Importuj" +#: src/admin/stages/StageListPage.ts +#: src/admin/stages/StageListPage.ts +msgid "Import Duo device" +msgstr "" + #: src/admin/flows/FlowListPage.ts msgid "Import Flow" msgstr "Importuj przepływ" @@ -2809,6 +2832,7 @@ msgstr "Uwzględnij roszczenia w id_token" msgid "Integration" msgstr "Integracja" +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts msgid "Integration key" msgstr "Klucz integracji" @@ -3128,6 +3152,7 @@ msgstr "Ładowanie" #: src/admin/sources/saml/SAMLSourceForm.ts #: src/admin/sources/saml/SAMLSourceForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts #: src/admin/stages/authenticator_sms/AuthenticatorSMSStageForm.ts #: src/admin/stages/authenticator_static/AuthenticatorStaticStageForm.ts #: src/admin/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts @@ -4746,6 +4771,7 @@ msgstr "Tryb szukania" msgid "Search..." msgstr "Szukaj..." +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts msgid "Secret key" msgstr "Sekretny klucz" @@ -5429,6 +5455,10 @@ msgstr "Pomyślnie wygenerowana para certyfikat-klucz." msgid "Successfully generated recovery link" msgstr "Pomyślnie wygenerowano link odzyskiwania" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Successfully imported device." +msgstr "" + #: src/admin/flows/FlowImportForm.ts msgid "Successfully imported flow." msgstr "Pomyślnie zaimportowano przepływ." @@ -5817,6 +5847,14 @@ msgstr "Początek gidNumbers, liczba ta jest dodawana do liczby wygenerowanej z msgid "The start for uidNumbers, this number is added to the user.Pk to make sure that the numbers aren't too low for POSIX users. Default is 2000 to ensure that we don't collide with local users uidNumber" msgstr "Początek dla uidNumbers, ten numer jest dodawany do user.Pk, aby upewnić się, że liczby nie są zbyt niskie dla użytkowników POSIX. Wartość domyślna to 2000, aby zapewnić, że nie kolidujemy z lokalnymi użytkownikami uidNumber" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "The user ID in Duo." +msgstr "" + +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "The user in authentik this device will be assigned to." +msgstr "" + #: src/admin/flows/BoundStagesList.ts msgid "These bindings control if this stage will be applied to the flow." msgstr "Te powiązania kontrolują, czy ten etap zostanie zastosowany do przepływu." @@ -6421,6 +6459,7 @@ msgstr "Użyj tego najemcy dla każdej domeny, która nie ma dedykowanej najmecy #: src/admin/policies/PolicyBindingForm.ts #: src/admin/policies/PolicyTestForm.ts #: src/admin/property-mappings/PropertyMappingTestForm.ts +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts #: src/admin/tokens/TokenForm.ts #: src/admin/tokens/TokenListPage.ts #: src/admin/users/RelatedUserList.ts @@ -6836,6 +6875,12 @@ msgstr "Gdy ta opcja jest włączona, wszystkie wykonania tej zasady będą reje msgid "When used in conjunction with a User Write stage, use attributes.foo to write attributes." msgstr "W przypadku użycia w połączeniu z etapem zapisu użytkownika, użyj attribute.foo do zapisania atrybutów." +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "" +"When using a Duo MFA, Access or Beyond plan, an Admin API application can be created.\n" +"This will allow authentik to import devices automatically." +msgstr "" + #: src/admin/tenants/TenantForm.ts msgid "When using an external logging solution for archiving, this can be set to \"minutes=5\"." msgstr "W przypadku korzystania z zewnętrznego rozwiązania rejestrującego do archiwizacji można to ustawić na „minuty=5”." diff --git a/web/src/locales/pseudo-LOCALE.po b/web/src/locales/pseudo-LOCALE.po index 96ff4e669..a0c02939b 100644 --- a/web/src/locales/pseudo-LOCALE.po +++ b/web/src/locales/pseudo-LOCALE.po @@ -821,6 +821,10 @@ msgstr "" msgid "Callback URL" msgstr "" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Can be either the username (found in the Users list) or the ID (can be found in the URL after clicking on a user)." +msgstr "" + #: src/pages/outposts/ServiceConnectionDockerForm.ts #~ msgid "Can be in the format of 'unix://' when connecting to a local docker daemon, or 'https://:2376' when connecting to a remote system." #~ msgstr "" @@ -1849,10 +1853,22 @@ msgstr "" #~ msgid "Duo" #~ msgstr "" +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "Duo Admin API (optional)" +msgstr "" + +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "Duo Auth API" +msgstr "" + #: src/admin/stages/authenticator_validate/AuthenticatorValidateStageForm.ts msgid "Duo Authenticators" msgstr "" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Duo User ID" +msgstr "" + #: src/flow/stages/authenticator_duo/AuthenticatorDuoStage.ts msgid "Duo activation" msgstr "" @@ -2801,9 +2817,16 @@ msgstr "" #: src/admin/flows/FlowListPage.ts #: src/admin/flows/FlowListPage.ts +#: src/admin/stages/StageListPage.ts +#: src/admin/stages/StageListPage.ts msgid "Import" msgstr "" +#: src/admin/stages/StageListPage.ts +#: src/admin/stages/StageListPage.ts +msgid "Import Duo device" +msgstr "" + #: src/admin/flows/FlowListPage.ts msgid "Import Flow" msgstr "" @@ -2850,6 +2873,7 @@ msgstr "" msgid "Integration" msgstr "" +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts msgid "Integration key" msgstr "" @@ -3172,6 +3196,7 @@ msgstr "" #: src/admin/sources/saml/SAMLSourceForm.ts #: src/admin/sources/saml/SAMLSourceForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts #: src/admin/stages/authenticator_sms/AuthenticatorSMSStageForm.ts #: src/admin/stages/authenticator_static/AuthenticatorStaticStageForm.ts #: src/admin/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts @@ -4816,6 +4841,7 @@ msgstr "" msgid "Search..." msgstr "" +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts msgid "Secret key" msgstr "" @@ -5524,6 +5550,10 @@ msgstr "" msgid "Successfully generated recovery link" msgstr "" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Successfully imported device." +msgstr "" + #: src/admin/flows/FlowImportForm.ts msgid "Successfully imported flow." msgstr "" @@ -5916,6 +5946,14 @@ msgstr "" msgid "The start for uidNumbers, this number is added to the user.Pk to make sure that the numbers aren't too low for POSIX users. Default is 2000 to ensure that we don't collide with local users uidNumber" msgstr "" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "The user ID in Duo." +msgstr "" + +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "The user in authentik this device will be assigned to." +msgstr "" + #: src/admin/flows/BoundStagesList.ts msgid "These bindings control if this stage will be applied to the flow." msgstr "" @@ -6521,6 +6559,7 @@ msgstr "" #: src/admin/policies/PolicyBindingForm.ts #: src/admin/policies/PolicyTestForm.ts #: src/admin/property-mappings/PropertyMappingTestForm.ts +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts #: src/admin/tokens/TokenForm.ts #: src/admin/tokens/TokenListPage.ts #: src/admin/users/RelatedUserList.ts @@ -6943,6 +6982,12 @@ msgstr "" msgid "When used in conjunction with a User Write stage, use attributes.foo to write attributes." msgstr "" +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "" +"When using a Duo MFA, Access or Beyond plan, an Admin API application can be created.\n" +"This will allow authentik to import devices automatically." +msgstr "" + #: src/admin/tenants/TenantForm.ts msgid "When using an external logging solution for archiving, this can be set to \"minutes=5\"." msgstr "" diff --git a/web/src/locales/tr.po b/web/src/locales/tr.po index 7c26b730c..e39de660e 100644 --- a/web/src/locales/tr.po +++ b/web/src/locales/tr.po @@ -822,6 +822,10 @@ msgstr "" msgid "Callback URL" msgstr "Geri arama URL'si" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Can be either the username (found in the Users list) or the ID (can be found in the URL after clicking on a user)." +msgstr "" + #~ msgid "Can be in the format of 'unix://' when connecting to a local docker daemon, or 'https://:2376' when connecting to a remote system." #~ msgstr "Yerel bir docker serine bağlanırken 'unix: //' biçiminde veya uzak bir sisteme bağlanırken 'https://:2376' biçiminde olabilir." @@ -1828,10 +1832,22 @@ msgstr "Test için kullanılan kukla aşama. Basit bir devam düğmesi gösterir #~ msgid "Duo" #~ msgstr "İkili" +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "Duo Admin API (optional)" +msgstr "" + +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "Duo Auth API" +msgstr "" + #: src/admin/stages/authenticator_validate/AuthenticatorValidateStageForm.ts msgid "Duo Authenticators" msgstr "Duo Kimlik Doğrulayıcıları" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Duo User ID" +msgstr "" + #: src/flow/stages/authenticator_duo/AuthenticatorDuoStage.ts msgid "Duo activation" msgstr "İkili aktivasyon" @@ -2764,9 +2780,16 @@ msgstr "Kimliğe bürünme başladı" #: src/admin/flows/FlowListPage.ts #: src/admin/flows/FlowListPage.ts +#: src/admin/stages/StageListPage.ts +#: src/admin/stages/StageListPage.ts msgid "Import" msgstr "İçe Aktar" +#: src/admin/stages/StageListPage.ts +#: src/admin/stages/StageListPage.ts +msgid "Import Duo device" +msgstr "" + #: src/admin/flows/FlowListPage.ts msgid "Import Flow" msgstr "Akışı İçe Aktar" @@ -2813,6 +2836,7 @@ msgstr "İd_token'a hak taleplerini dahil et" msgid "Integration" msgstr "Entegrasyon" +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts msgid "Integration key" msgstr "Entegrasyon anahtarı" @@ -3132,6 +3156,7 @@ msgstr "Yükleniyor" #: src/admin/sources/saml/SAMLSourceForm.ts #: src/admin/sources/saml/SAMLSourceForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts #: src/admin/stages/authenticator_sms/AuthenticatorSMSStageForm.ts #: src/admin/stages/authenticator_static/AuthenticatorStaticStageForm.ts #: src/admin/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts @@ -4751,6 +4776,7 @@ msgstr "Arama modu" msgid "Search..." msgstr "Ara..." +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts msgid "Secret key" msgstr "Gizli anahtar" @@ -5434,6 +5460,10 @@ msgstr "Sertifika-anahtar çifti başarıyla oluşturuldu." msgid "Successfully generated recovery link" msgstr "Kurtarma bağlantısı başarıyla oluşturuldu" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Successfully imported device." +msgstr "" + #: src/admin/flows/FlowImportForm.ts msgid "Successfully imported flow." msgstr "Akış başarıyla aktarıldı." @@ -5822,6 +5852,14 @@ msgstr "gidNumbers'ın başlangıcı, bu sayı group.Pk öğesinden oluşturulan msgid "The start for uidNumbers, this number is added to the user.Pk to make sure that the numbers aren't too low for POSIX users. Default is 2000 to ensure that we don't collide with local users uidNumber" msgstr "UidNumbers'ın başlangıcında, bu sayı, POSIX kullanıcıları için sayıların çok düşük olmadığından emin olmak için user.Pk öğesine eklenir. Varsayılan 2000 yerel kullanıcılarla çarpışmadığımızdan emin olmak için uidNumber" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "The user ID in Duo." +msgstr "" + +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "The user in authentik this device will be assigned to." +msgstr "" + #: src/admin/flows/BoundStagesList.ts msgid "These bindings control if this stage will be applied to the flow." msgstr "Bu bağlamalar, bu aşama akışa uygulanacak olup olmadığını denetler." @@ -6426,6 +6464,7 @@ msgstr "Bu sakini, ayrılmış bir sakine sahip olmayan her etki alanı için ku #: src/admin/policies/PolicyBindingForm.ts #: src/admin/policies/PolicyTestForm.ts #: src/admin/property-mappings/PropertyMappingTestForm.ts +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts #: src/admin/tokens/TokenForm.ts #: src/admin/tokens/TokenListPage.ts #: src/admin/users/RelatedUserList.ts @@ -6841,6 +6880,12 @@ msgstr "Bu seçenek etkinleştirildiğinde, bu ilkenin tüm yürütmeleri günl msgid "When used in conjunction with a User Write stage, use attributes.foo to write attributes." msgstr "Kullanıcı Yazma aşaması ile birlikte kullanıldığında, öznitelikleri yazmak için attributes.foo kullanın." +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "" +"When using a Duo MFA, Access or Beyond plan, an Admin API application can be created.\n" +"This will allow authentik to import devices automatically." +msgstr "" + #: src/admin/tenants/TenantForm.ts msgid "When using an external logging solution for archiving, this can be set to \"minutes=5\"." msgstr "Arşivleme için harici bir günlük çözümü kullanırken, bu “Dakika = 5\" olarak ayarlanabilir." diff --git a/web/src/locales/zh-Hans.po b/web/src/locales/zh-Hans.po index 5c0945b9b..a6db767a9 100644 --- a/web/src/locales/zh-Hans.po +++ b/web/src/locales/zh-Hans.po @@ -817,6 +817,10 @@ msgstr "" msgid "Callback URL" msgstr "回调 URL" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Can be either the username (found in the Users list) or the ID (can be found in the URL after clicking on a user)." +msgstr "" + #~ msgid "Can be in the format of 'unix://' when connecting to a local docker daemon, or 'https://:2376' when connecting to a remote system." #~ msgstr "连接到本地 Docker 守护进程时可以采用 'unix://' 格式,或者在连接到远程系统时采用 'https://:2376' 格式。" @@ -1821,10 +1825,22 @@ msgstr "用于测试的虚拟阶段。显示一个简单的“继续”按钮, #~ msgid "Duo" #~ msgstr "Duo" +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "Duo Admin API (optional)" +msgstr "" + +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "Duo Auth API" +msgstr "" + #: src/admin/stages/authenticator_validate/AuthenticatorValidateStageForm.ts msgid "Duo Authenticators" msgstr "Duo 身份验证器" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Duo User ID" +msgstr "" + #: src/flow/stages/authenticator_duo/AuthenticatorDuoStage.ts msgid "Duo activation" msgstr "Duo 激活" @@ -2748,9 +2764,16 @@ msgstr "已开始模拟身份" #: src/admin/flows/FlowListPage.ts #: src/admin/flows/FlowListPage.ts +#: src/admin/stages/StageListPage.ts +#: src/admin/stages/StageListPage.ts msgid "Import" msgstr "导入" +#: src/admin/stages/StageListPage.ts +#: src/admin/stages/StageListPage.ts +msgid "Import Duo device" +msgstr "" + #: src/admin/flows/FlowListPage.ts msgid "Import Flow" msgstr "导入流程" @@ -2797,6 +2820,7 @@ msgstr "在 id_token 中包含声明" msgid "Integration" msgstr "集成" +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts msgid "Integration key" msgstr "集成密钥" @@ -3115,6 +3139,7 @@ msgstr "正在加载" #: src/admin/sources/saml/SAMLSourceForm.ts #: src/admin/sources/saml/SAMLSourceForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts #: src/admin/stages/authenticator_sms/AuthenticatorSMSStageForm.ts #: src/admin/stages/authenticator_static/AuthenticatorStaticStageForm.ts #: src/admin/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts @@ -4717,6 +4742,7 @@ msgstr "搜索模式" msgid "Search..." msgstr "搜索..." +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts msgid "Secret key" msgstr "Secret 密钥" @@ -5398,6 +5424,10 @@ msgstr "已成功生成证书密钥对。" msgid "Successfully generated recovery link" msgstr "已成功生成恢复链接" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Successfully imported device." +msgstr "" + #: src/admin/flows/FlowImportForm.ts msgid "Successfully imported flow." msgstr "已成功导入流程。" @@ -5785,6 +5815,14 @@ msgstr "起始 gidNumbers,这个数字会被添加到从 group.Pk 生成的数 msgid "The start for uidNumbers, this number is added to the user.Pk to make sure that the numbers aren't too low for POSIX users. Default is 2000 to ensure that we don't collide with local users uidNumber" msgstr "起始 uidNumbers,这个数字会被添加到 user.Pk 中,以确保对于 POSIX 用户来说,这个数字不会太低。默认值为 2000,以确保我们不会与本地用户的 uidNumber 发生冲突" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "The user ID in Duo." +msgstr "" + +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "The user in authentik this device will be assigned to." +msgstr "" + #: src/admin/flows/BoundStagesList.ts msgid "These bindings control if this stage will be applied to the flow." msgstr "这些绑定控制是否将此阶段应用于流程。" @@ -6387,6 +6425,7 @@ msgstr "所有未设置专用租户的域名都将使用此租户。" #: src/admin/policies/PolicyBindingForm.ts #: src/admin/policies/PolicyTestForm.ts #: src/admin/property-mappings/PropertyMappingTestForm.ts +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts #: src/admin/tokens/TokenForm.ts #: src/admin/tokens/TokenListPage.ts #: src/admin/users/RelatedUserList.ts @@ -6803,6 +6842,12 @@ msgstr "启用此选项后,将记录此策略的所有执行日志。默认情 msgid "When used in conjunction with a User Write stage, use attributes.foo to write attributes." msgstr "当与用户写入阶段结合使用时,请使用 attributes.foo 来编写属性。" +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "" +"When using a Duo MFA, Access or Beyond plan, an Admin API application can be created.\n" +"This will allow authentik to import devices automatically." +msgstr "" + #: src/admin/tenants/TenantForm.ts msgid "When using an external logging solution for archiving, this can be set to \"minutes=5\"." msgstr "使用外部日志记录解决方案进行存档时,可以将其设置为 \"minutes=5\"。" diff --git a/web/src/locales/zh-Hant.po b/web/src/locales/zh-Hant.po index 438c674ca..0556673e6 100644 --- a/web/src/locales/zh-Hant.po +++ b/web/src/locales/zh-Hant.po @@ -819,6 +819,10 @@ msgstr "" msgid "Callback URL" msgstr "回调 URL" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Can be either the username (found in the Users list) or the ID (can be found in the URL after clicking on a user)." +msgstr "" + #~ msgid "Can be in the format of 'unix://' when connecting to a local docker daemon, or 'https://:2376' when connecting to a remote system." #~ msgstr "连接到本地 docker 守护进程时可以采用 'unix://' 的格式,或者在连接到远程系统时采用 'https://:2376' 的格式。" @@ -1824,10 +1828,22 @@ msgstr "用于测试的虚拟阶段。显示一个简单的 “继续” 按钮 #~ msgid "Duo" #~ msgstr "Duo" +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "Duo Admin API (optional)" +msgstr "" + +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "Duo Auth API" +msgstr "" + #: src/admin/stages/authenticator_validate/AuthenticatorValidateStageForm.ts msgid "Duo Authenticators" msgstr "Duo 身份验证器" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Duo User ID" +msgstr "" + #: src/flow/stages/authenticator_duo/AuthenticatorDuoStage.ts msgid "Duo activation" msgstr "Duo 激活" @@ -2751,9 +2767,16 @@ msgstr "模拟已开始" #: src/admin/flows/FlowListPage.ts #: src/admin/flows/FlowListPage.ts +#: src/admin/stages/StageListPage.ts +#: src/admin/stages/StageListPage.ts msgid "Import" msgstr "导入" +#: src/admin/stages/StageListPage.ts +#: src/admin/stages/StageListPage.ts +msgid "Import Duo device" +msgstr "" + #: src/admin/flows/FlowListPage.ts msgid "Import Flow" msgstr "导入流程" @@ -2800,6 +2823,7 @@ msgstr "在 id_token 中包含声明" msgid "Integration" msgstr "整合" +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts msgid "Integration key" msgstr "集成密钥" @@ -3119,6 +3143,7 @@ msgstr "正在加载" #: src/admin/sources/saml/SAMLSourceForm.ts #: src/admin/sources/saml/SAMLSourceForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts #: src/admin/stages/authenticator_sms/AuthenticatorSMSStageForm.ts #: src/admin/stages/authenticator_static/AuthenticatorStaticStageForm.ts #: src/admin/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts @@ -4723,6 +4748,7 @@ msgstr "搜索模式" msgid "Search..." msgstr "搜索..." +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts msgid "Secret key" msgstr "密钥" @@ -5405,6 +5431,10 @@ msgstr "成功生成证书密钥对。" msgid "Successfully generated recovery link" msgstr "成功生成恢复链接" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Successfully imported device." +msgstr "" + #: src/admin/flows/FlowImportForm.ts msgid "Successfully imported flow." msgstr "已成功导入流程。" @@ -5792,6 +5822,14 @@ msgstr "对于 GIDNumbers 来说,这个数字被添加到从 group.pk 生成 msgid "The start for uidNumbers, this number is added to the user.Pk to make sure that the numbers aren't too low for POSIX users. Default is 2000 to ensure that we don't collide with local users uidNumber" msgstr "对于UIDNumbers来说,这个数字被添加到User.pk中,以确保对于POSIX用户来说,这个数字不会太低。默认值为 2000,以确保我们不会与本地用户 uidNumber 发生冲突" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "The user ID in Duo." +msgstr "" + +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "The user in authentik this device will be assigned to." +msgstr "" + #: src/admin/flows/BoundStagesList.ts msgid "These bindings control if this stage will be applied to the flow." msgstr "这些绑定控制是否将此阶段应用于流程。" @@ -6396,6 +6434,7 @@ msgstr "对于没有专用租户的每个域,请使用此租户。" #: src/admin/policies/PolicyBindingForm.ts #: src/admin/policies/PolicyTestForm.ts #: src/admin/property-mappings/PropertyMappingTestForm.ts +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts #: src/admin/tokens/TokenForm.ts #: src/admin/tokens/TokenListPage.ts #: src/admin/users/RelatedUserList.ts @@ -6813,6 +6852,12 @@ msgstr "启用此选项后,将记录此策略的所有执行。默认情况下 msgid "When used in conjunction with a User Write stage, use attributes.foo to write attributes." msgstr "当与用户写入阶段结合使用时,请使用 attributes.foo 来编写属性。" +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "" +"When using a Duo MFA, Access or Beyond plan, an Admin API application can be created.\n" +"This will allow authentik to import devices automatically." +msgstr "" + #: src/admin/tenants/TenantForm.ts msgid "When using an external logging solution for archiving, this can be set to \"minutes=5\"." msgstr "使用外部日志记录解决方案进行存档时,可以将其设置为 “minutes=5”。" diff --git a/web/src/locales/zh_TW.po b/web/src/locales/zh_TW.po index aeb9db003..6048e04d1 100644 --- a/web/src/locales/zh_TW.po +++ b/web/src/locales/zh_TW.po @@ -819,6 +819,10 @@ msgstr "" msgid "Callback URL" msgstr "回调 URL" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Can be either the username (found in the Users list) or the ID (can be found in the URL after clicking on a user)." +msgstr "" + #~ msgid "Can be in the format of 'unix://' when connecting to a local docker daemon, or 'https://:2376' when connecting to a remote system." #~ msgstr "连接到本地 docker 守护进程时可以采用 'unix://' 的格式,或者在连接到远程系统时采用 'https://:2376' 的格式。" @@ -1824,10 +1828,22 @@ msgstr "用于测试的虚拟阶段。显示一个简单的 “继续” 按钮 #~ msgid "Duo" #~ msgstr "Duo" +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "Duo Admin API (optional)" +msgstr "" + +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "Duo Auth API" +msgstr "" + #: src/admin/stages/authenticator_validate/AuthenticatorValidateStageForm.ts msgid "Duo Authenticators" msgstr "Duo 身份验证器" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Duo User ID" +msgstr "" + #: src/flow/stages/authenticator_duo/AuthenticatorDuoStage.ts msgid "Duo activation" msgstr "Duo 激活" @@ -2751,9 +2767,16 @@ msgstr "模拟已开始" #: src/admin/flows/FlowListPage.ts #: src/admin/flows/FlowListPage.ts +#: src/admin/stages/StageListPage.ts +#: src/admin/stages/StageListPage.ts msgid "Import" msgstr "导入" +#: src/admin/stages/StageListPage.ts +#: src/admin/stages/StageListPage.ts +msgid "Import Duo device" +msgstr "" + #: src/admin/flows/FlowListPage.ts msgid "Import Flow" msgstr "导入流程" @@ -2800,6 +2823,7 @@ msgstr "在 id_token 中包含声明" msgid "Integration" msgstr "整合" +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts msgid "Integration key" msgstr "集成密钥" @@ -3119,6 +3143,7 @@ msgstr "正在加载" #: src/admin/sources/saml/SAMLSourceForm.ts #: src/admin/sources/saml/SAMLSourceForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts #: src/admin/stages/authenticator_sms/AuthenticatorSMSStageForm.ts #: src/admin/stages/authenticator_static/AuthenticatorStaticStageForm.ts #: src/admin/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts @@ -4723,6 +4748,7 @@ msgstr "搜索模式" msgid "Search..." msgstr "搜索..." +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts #: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts msgid "Secret key" msgstr "密钥" @@ -5405,6 +5431,10 @@ msgstr "成功生成证书密钥对。" msgid "Successfully generated recovery link" msgstr "成功生成恢复链接" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "Successfully imported device." +msgstr "" + #: src/admin/flows/FlowImportForm.ts msgid "Successfully imported flow." msgstr "已成功导入流程。" @@ -5792,6 +5822,14 @@ msgstr "对于 GIDNumbers 来说,这个数字被添加到从 group.pk 生成 msgid "The start for uidNumbers, this number is added to the user.Pk to make sure that the numbers aren't too low for POSIX users. Default is 2000 to ensure that we don't collide with local users uidNumber" msgstr "对于UIDNumbers来说,这个数字被添加到User.pk中,以确保对于POSIX用户来说,这个数字不会太低。默认值为 2000,以确保我们不会与本地用户 uidNumber 发生冲突" +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "The user ID in Duo." +msgstr "" + +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts +msgid "The user in authentik this device will be assigned to." +msgstr "" + #: src/admin/flows/BoundStagesList.ts msgid "These bindings control if this stage will be applied to the flow." msgstr "这些绑定控制是否将此阶段应用于流程。" @@ -6396,6 +6434,7 @@ msgstr "对于没有专用租户的每个域,请使用此租户。" #: src/admin/policies/PolicyBindingForm.ts #: src/admin/policies/PolicyTestForm.ts #: src/admin/property-mappings/PropertyMappingTestForm.ts +#: src/admin/stages/authenticator_duo/DuoDeviceImportForm.ts #: src/admin/tokens/TokenForm.ts #: src/admin/tokens/TokenListPage.ts #: src/admin/users/RelatedUserList.ts @@ -6813,6 +6852,12 @@ msgstr "启用此选项后,将记录此策略的所有执行。默认情况下 msgid "When used in conjunction with a User Write stage, use attributes.foo to write attributes." msgstr "当与用户写入阶段结合使用时,请使用 attributes.foo 来编写属性。" +#: src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts +msgid "" +"When using a Duo MFA, Access or Beyond plan, an Admin API application can be created.\n" +"This will allow authentik to import devices automatically." +msgstr "" + #: src/admin/tenants/TenantForm.ts msgid "When using an external logging solution for archiving, this can be set to \"minutes=5\"." msgstr "使用外部日志记录解决方案进行存档时,可以将其设置为 “minutes=5”。" diff --git a/website/docs/flow/stages/authenticator_duo/index.md b/website/docs/flow/stages/authenticator_duo/index.md index 3cd806222..61839f688 100644 --- a/website/docs/flow/stages/authenticator_duo/index.md +++ b/website/docs/flow/stages/authenticator_duo/index.md @@ -13,11 +13,21 @@ Devices created reference the stage they were created with, since the API creden ## Importing users :::info -Due to the way the Duo API works, authentik cannot automatically import existing Duo users. +Due to the way the Duo API works, authentik can only automatically import existing Duo users when a Duo MFA or higher license is active. ::: :::info -This API requires version 2021.9.1 or later +This requires authentk 2022.9 +::: + +To import a device, open the Stages list in the authentik Admin interface. On the right next to the import button you'll see an import button, with which you can import Duo devices to authentik users. + +The Duo username can be found by navigating to your Duo Admin dashboard and selecting _Users_ in the sidebar. Optionally if you have multiple users with the same username, you can click on a User and copy their ID from the URL, and use that to import the device. + +### Older versions + +:::info +This API requires authentik 2021.9.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: