stages/authenticator_validate: add ability to select multiple configuration stages which the user can choose
closes #1843 Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
9070df6c26
commit
2ccab75021
|
@ -13,8 +13,8 @@ class AuthenticatorValidateStageSerializer(StageSerializer):
|
|||
|
||||
def validate_not_configured_action(self, value):
|
||||
"""Ensure that a configuration stage is set when not_configured_action is configure"""
|
||||
configuration_stage = self.initial_data.get("configuration_stage")
|
||||
if value == NotConfiguredAction.CONFIGURE and configuration_stage is None:
|
||||
configuration_stages = self.initial_data.get("configuration_stages")
|
||||
if value == NotConfiguredAction.CONFIGURE and configuration_stages is None:
|
||||
raise ValidationError(
|
||||
(
|
||||
'When "Not configured action" is set to "Configure", '
|
||||
|
@ -29,7 +29,7 @@ class AuthenticatorValidateStageSerializer(StageSerializer):
|
|||
fields = StageSerializer.Meta.fields + [
|
||||
"not_configured_action",
|
||||
"device_classes",
|
||||
"configuration_stage",
|
||||
"configuration_stages",
|
||||
]
|
||||
|
||||
|
||||
|
@ -38,5 +38,5 @@ class AuthenticatorValidateStageViewSet(UsedByMixin, ModelViewSet):
|
|||
|
||||
queryset = AuthenticatorValidateStage.objects.all()
|
||||
serializer_class = AuthenticatorValidateStageSerializer
|
||||
filterset_fields = ["name", "not_configured_action", "configuration_stage"]
|
||||
filterset_fields = ["name", "not_configured_action", "configuration_stages"]
|
||||
ordering = ["name"]
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
# Generated by Django 4.0.1 on 2022-01-05 22:09
|
||||
|
||||
from django.apps.registry import Apps
|
||||
from django.db import migrations, models
|
||||
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
||||
|
||||
|
||||
def migrate_configuration_stage(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
|
||||
db_alias = schema_editor.connection.alias
|
||||
AuthenticatorValidateStage = apps.get_model(
|
||||
"authentik_stages_authenticator_validate", "AuthenticatorValidateStage"
|
||||
)
|
||||
|
||||
for stage in AuthenticatorValidateStage.objects.using(db_alias).all():
|
||||
if stage.configuration_stage:
|
||||
stage.configuration_stages.set([stage.configuration_stage])
|
||||
stage.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_flows", "0021_auto_20211227_2103"),
|
||||
("authentik_stages_authenticator_validate", "0009_default_stage"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="authenticatorvalidatestage",
|
||||
name="configuration_stages",
|
||||
field=models.ManyToManyField(
|
||||
blank=True,
|
||||
default=None,
|
||||
help_text="Stages used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again.",
|
||||
related_name="+",
|
||||
to="authentik_flows.Stage",
|
||||
),
|
||||
),
|
||||
migrations.RunPython(migrate_configuration_stage),
|
||||
migrations.RemoveField(
|
||||
model_name="authenticatorvalidatestage",
|
||||
name="configuration_stage",
|
||||
),
|
||||
]
|
|
@ -38,16 +38,14 @@ class AuthenticatorValidateStage(Stage):
|
|||
choices=NotConfiguredAction.choices, default=NotConfiguredAction.SKIP
|
||||
)
|
||||
|
||||
configuration_stage = models.ForeignKey(
|
||||
configuration_stages = models.ManyToManyField(
|
||||
Stage,
|
||||
null=True,
|
||||
blank=True,
|
||||
default=None,
|
||||
on_delete=models.SET_DEFAULT,
|
||||
related_name="+",
|
||||
help_text=_(
|
||||
(
|
||||
"Stage used to configure Authenticator when user doesn't have any compatible "
|
||||
"Stages used to configure Authenticator when user doesn't have any compatible "
|
||||
"devices. After this configuration Stage passes, the user is not prompted again."
|
||||
)
|
||||
),
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
"""Authenticator Validation"""
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django_otp import devices_for_user
|
||||
from rest_framework.fields import CharField, IntegerField, JSONField, ListField
|
||||
from rest_framework.fields import CharField, IntegerField, JSONField, ListField, UUIDField
|
||||
from rest_framework.serializers import ValidationError
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.core.api.utils import PassiveSerializer
|
||||
from authentik.core.models import User
|
||||
from authentik.events.models import Event, EventAction
|
||||
from authentik.events.utils import cleanse_dict, sanitize_dict
|
||||
from authentik.flows.challenge import ChallengeResponse, ChallengeTypes, WithUserInfoChallenge
|
||||
|
@ -26,6 +28,18 @@ from authentik.stages.authenticator_webauthn.models import WebAuthnDevice
|
|||
from authentik.stages.password.stage import PLAN_CONTEXT_METHOD, PLAN_CONTEXT_METHOD_ARGS
|
||||
|
||||
LOGGER = get_logger()
|
||||
SESSION_STAGES = "goauthentik.io/stages/authenticator_validate/stages"
|
||||
SESSION_SELECTED_STAGE = "goauthentik.io/stages/authenticator_validate/selected_stage"
|
||||
SESSION_DEVICE_CHALLENGES = "goauthentik.io/stages/authenticator_validate/device_challenges"
|
||||
|
||||
|
||||
class SelectableStageSerializer(PassiveSerializer):
|
||||
"""Serializer for stages which can be selected by users"""
|
||||
|
||||
pk = UUIDField()
|
||||
name = CharField()
|
||||
verbose_name = CharField()
|
||||
meta_model_name = CharField()
|
||||
|
||||
|
||||
class AuthenticatorValidationChallenge(WithUserInfoChallenge):
|
||||
|
@ -33,12 +47,14 @@ class AuthenticatorValidationChallenge(WithUserInfoChallenge):
|
|||
|
||||
device_challenges = ListField(child=DeviceChallenge())
|
||||
component = CharField(default="ak-stage-authenticator-validate")
|
||||
configuration_stages = ListField(child=SelectableStageSerializer())
|
||||
|
||||
|
||||
class AuthenticatorValidationChallengeResponse(ChallengeResponse):
|
||||
"""Challenge used for Code-based and WebAuthn authenticators"""
|
||||
|
||||
selected_challenge = DeviceChallenge(required=False)
|
||||
selected_stage = CharField(required=False)
|
||||
|
||||
code = CharField(required=False)
|
||||
webauthn = JSONField(required=False)
|
||||
|
@ -84,6 +100,15 @@ class AuthenticatorValidationChallengeResponse(ChallengeResponse):
|
|||
select_challenge(self.stage.request, devices.first())
|
||||
return challenge
|
||||
|
||||
def validate_selected_stage(self, stage_pk: str) -> str:
|
||||
"""Check that the selected stage is valid"""
|
||||
stages = self.stage.request.session.get(SESSION_STAGES, [])
|
||||
if not any(str(stage.pk) == stage_pk for stage in stages):
|
||||
raise ValidationError("Selected stage is invalid")
|
||||
LOGGER.debug("Setting selected stage to ", stage=stage_pk)
|
||||
self.stage.request.session[SESSION_SELECTED_STAGE] = stage_pk
|
||||
return stage_pk
|
||||
|
||||
def validate(self, attrs: dict):
|
||||
# Checking if the given data is from a valid device class is done above
|
||||
# Here we only check if the any data was sent at all
|
||||
|
@ -164,7 +189,7 @@ class AuthenticatorValidateStageView(ChallengeStageView):
|
|||
else:
|
||||
LOGGER.debug("No pending user, continuing")
|
||||
return self.executor.stage_ok()
|
||||
self.request.session["device_challenges"] = challenges
|
||||
self.request.session[SESSION_DEVICE_CHALLENGES] = challenges
|
||||
|
||||
# No allowed devices
|
||||
if len(challenges) < 1:
|
||||
|
@ -175,7 +200,16 @@ class AuthenticatorValidateStageView(ChallengeStageView):
|
|||
LOGGER.debug("Authenticator not configured, denying")
|
||||
return self.executor.stage_invalid()
|
||||
if stage.not_configured_action == NotConfiguredAction.CONFIGURE:
|
||||
if not stage.configuration_stage:
|
||||
LOGGER.debug("Authenticator not configured, forcing configure")
|
||||
return self.prepare_stages(user)
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
def prepare_stages(self, user: User, *args, **kwargs) -> HttpResponse:
|
||||
"""Check how the user can configure themselves. If no stages are set, return an error.
|
||||
If a single stage is set, insert that stage directly. If multiple are selected, include
|
||||
them in the challenge."""
|
||||
stage: AuthenticatorValidateStage = self.executor.current_stage
|
||||
if not stage.configuration_stages.exists():
|
||||
Event.new(
|
||||
EventAction.CONFIGURATION_ERROR,
|
||||
message=(
|
||||
|
@ -185,25 +219,52 @@ class AuthenticatorValidateStageView(ChallengeStageView):
|
|||
stage=self,
|
||||
).from_http(self.request).set_user(user).save()
|
||||
return self.executor.stage_invalid()
|
||||
LOGGER.debug("Authenticator not configured, sending user to configure")
|
||||
if stage.configuration_stages.count() == 1:
|
||||
self.request.session[SESSION_SELECTED_STAGE] = stage.configuration_stages.first()
|
||||
LOGGER.debug(
|
||||
"Single stage configured, auto-selecting",
|
||||
stage=self.request.session[SESSION_SELECTED_STAGE],
|
||||
)
|
||||
stages = Stage.objects.filter(pk__in=stage.configuration_stages.all()).select_subclasses()
|
||||
self.request.session[SESSION_STAGES] = stages
|
||||
return super().get(self.request, *args, **kwargs)
|
||||
|
||||
def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||
if (
|
||||
SESSION_SELECTED_STAGE in self.request.session
|
||||
and self.executor.current_stage.not_configured_action == NotConfiguredAction.CONFIGURE
|
||||
):
|
||||
LOGGER.debug("Got selected stage in session, running that")
|
||||
stage_pk = self.request.session.get(SESSION_SELECTED_STAGE)
|
||||
# Because the foreign key to stage.configuration_stage points to
|
||||
# a base stage class, we need to do another lookup
|
||||
stage = Stage.objects.get_subclass(pk=stage.configuration_stage.pk)
|
||||
stage = Stage.objects.get_subclass(pk=stage_pk)
|
||||
# plan.insert inserts at 1 index, so when stage_ok pops 0,
|
||||
# the configuration stage is next
|
||||
self.executor.plan.insert_stage(stage)
|
||||
return self.executor.stage_ok()
|
||||
return super().get(request, *args, **kwargs)
|
||||
return super().post(request, *args, **kwargs)
|
||||
|
||||
def get_challenge(self) -> AuthenticatorValidationChallenge:
|
||||
challenges = self.request.session.get("device_challenges")
|
||||
if not challenges:
|
||||
LOGGER.debug("Authenticator Validation stage ran without challenges")
|
||||
return self.executor.stage_invalid()
|
||||
challenges = self.request.session.get(SESSION_DEVICE_CHALLENGES, [])
|
||||
stages = self.request.session.get(SESSION_STAGES, [])
|
||||
stage_challenges = []
|
||||
for stage in stages:
|
||||
serializer = SelectableStageSerializer(
|
||||
data={
|
||||
"pk": stage.pk,
|
||||
"name": stage.name,
|
||||
"verbose_name": str(stage._meta.verbose_name),
|
||||
"meta_model_name": f"{stage._meta.app_label}.{stage._meta.model_name}",
|
||||
}
|
||||
)
|
||||
serializer.is_valid()
|
||||
stage_challenges.append(serializer.data)
|
||||
return AuthenticatorValidationChallenge(
|
||||
data={
|
||||
"type": ChallengeTypes.NATIVE.value,
|
||||
"device_challenges": challenges,
|
||||
"configuration_stages": stage_challenges,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -43,8 +43,8 @@ class AuthenticatorValidateStageTests(FlowTestCase):
|
|||
stage = AuthenticatorValidateStage.objects.create(
|
||||
name="foo",
|
||||
not_configured_action=NotConfiguredAction.CONFIGURE,
|
||||
configuration_stage=conf_stage,
|
||||
)
|
||||
stage.configuration_stages.set([conf_stage])
|
||||
flow = Flow.objects.create(name="test", slug="test", title="test")
|
||||
FlowStageBinding.objects.create(target=flow, stage=conf_stage, order=0)
|
||||
FlowStageBinding.objects.create(target=flow, stage=stage, order=1)
|
||||
|
|
53
schema.yml
53
schema.yml
|
@ -15045,10 +15045,14 @@ paths:
|
|||
description: AuthenticatorValidateStage Viewset
|
||||
parameters:
|
||||
- in: query
|
||||
name: configuration_stage
|
||||
name: configuration_stages
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: uuid
|
||||
explode: true
|
||||
style: form
|
||||
- in: query
|
||||
name: name
|
||||
schema:
|
||||
|
@ -19826,11 +19830,12 @@ components:
|
|||
items:
|
||||
$ref: '#/components/schemas/DeviceClassesEnum'
|
||||
description: Device classes which can be used to authenticate
|
||||
configuration_stage:
|
||||
configuration_stages:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
description: Stage used to configure Authenticator when user doesn't have
|
||||
description: Stages used to configure Authenticator when user doesn't have
|
||||
any compatible devices. After this configuration Stage passes, the user
|
||||
is not prompted again.
|
||||
required:
|
||||
|
@ -19858,11 +19863,12 @@ components:
|
|||
items:
|
||||
$ref: '#/components/schemas/DeviceClassesEnum'
|
||||
description: Device classes which can be used to authenticate
|
||||
configuration_stage:
|
||||
configuration_stages:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
description: Stage used to configure Authenticator when user doesn't have
|
||||
description: Stages used to configure Authenticator when user doesn't have
|
||||
any compatible devices. After this configuration Stage passes, the user
|
||||
is not prompted again.
|
||||
required:
|
||||
|
@ -19892,7 +19898,12 @@ components:
|
|||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/DeviceChallenge'
|
||||
configuration_stages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/SelectableStage'
|
||||
required:
|
||||
- configuration_stages
|
||||
- device_challenges
|
||||
- pending_user
|
||||
- pending_user_avatar
|
||||
|
@ -19907,6 +19918,9 @@ components:
|
|||
default: ak-stage-authenticator-validate
|
||||
selected_challenge:
|
||||
$ref: '#/components/schemas/DeviceChallengeRequest'
|
||||
selected_stage:
|
||||
type: string
|
||||
minLength: 1
|
||||
code:
|
||||
type: string
|
||||
minLength: 1
|
||||
|
@ -26677,11 +26691,12 @@ components:
|
|||
items:
|
||||
$ref: '#/components/schemas/DeviceClassesEnum'
|
||||
description: Device classes which can be used to authenticate
|
||||
configuration_stage:
|
||||
configuration_stages:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
description: Stage used to configure Authenticator when user doesn't have
|
||||
description: Stages used to configure Authenticator when user doesn't have
|
||||
any compatible devices. After this configuration Stage passes, the user
|
||||
is not prompted again.
|
||||
PatchedCaptchaStageRequest:
|
||||
|
@ -30017,6 +30032,24 @@ components:
|
|||
- direct
|
||||
- cached
|
||||
type: string
|
||||
SelectableStage:
|
||||
type: object
|
||||
description: Serializer for stages which can be selected by users
|
||||
properties:
|
||||
pk:
|
||||
type: string
|
||||
format: uuid
|
||||
name:
|
||||
type: string
|
||||
verbose_name:
|
||||
type: string
|
||||
meta_model_name:
|
||||
type: string
|
||||
required:
|
||||
- meta_model_name
|
||||
- name
|
||||
- pk
|
||||
- verbose_name
|
||||
ServiceConnection:
|
||||
type: object
|
||||
description: ServiceConnection Serializer
|
||||
|
|
|
@ -67,7 +67,7 @@ export class AuthenticatorValidateStage
|
|||
return this._selectedDeviceChallenge;
|
||||
}
|
||||
|
||||
submit(payload: AuthenticatorValidationChallengeResponseRequest): Promise<void> {
|
||||
submit(payload: AuthenticatorValidationChallengeResponseRequest): Promise<boolean> {
|
||||
return this.host?.submit(payload) || Promise.resolve();
|
||||
}
|
||||
|
||||
|
@ -157,6 +157,30 @@ export class AuthenticatorValidateStage
|
|||
</ul>`;
|
||||
}
|
||||
|
||||
renderStagePicker(): TemplateResult {
|
||||
return html`<ul>
|
||||
${this.challenge?.configurationStages.map((stage) => {
|
||||
return html`<li>
|
||||
<button
|
||||
class="pf-c-button authenticator-button"
|
||||
type="button"
|
||||
@click=${() => {
|
||||
this.submit({
|
||||
component: this.challenge.component || "",
|
||||
selectedStage: stage.pk,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div class="right">
|
||||
<p>${stage.name}</p>
|
||||
<small>${stage.verboseName}</small>
|
||||
</div>
|
||||
</button>
|
||||
</li>`;
|
||||
})}
|
||||
</ul>`;
|
||||
}
|
||||
|
||||
renderDeviceChallenge(): TemplateResult {
|
||||
if (!this.selectedDeviceChallenge) {
|
||||
return html``;
|
||||
|
@ -242,6 +266,9 @@ export class AuthenticatorValidateStage
|
|||
${this.selectedDeviceChallenge
|
||||
? ""
|
||||
: html`<p>${t`Select an authentication method.`}</p>`}
|
||||
${this.challenge.configurationStages.length > 0
|
||||
? this.renderStagePicker()
|
||||
: html``}
|
||||
</form>
|
||||
${this.renderDevicePicker()}
|
||||
</div>
|
||||
|
|
|
@ -959,8 +959,12 @@ msgid "Configuration flow"
|
|||
msgstr "Ablauf der Konfiguration"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Configuration stage"
|
||||
msgstr "Konfiguration Stufe"
|
||||
#~ msgid "Configuration stage"
|
||||
#~ msgstr "Konfiguration Stufe"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Configuration stages"
|
||||
msgstr ""
|
||||
|
||||
#~ msgid "Configure WebAuthn"
|
||||
#~ msgstr "Konfiguriere WebAuthn"
|
||||
|
@ -4410,8 +4414,8 @@ msgid "Stage type"
|
|||
msgstr "Phasen Typ"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Stage used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again."
|
||||
msgstr "Phase zum Konfigurieren von Authenticator, wenn der Benutzer keine kompatiblen Geräte hat. Nach Ablauf dieser Konfigurationsphase wird der Benutzer nicht erneut aufgefordert."
|
||||
#~ msgid "Stage used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again."
|
||||
#~ msgstr "Phase zum Konfigurieren von Authenticator, wenn der Benutzer keine kompatiblen Geräte hat. Nach Ablauf dieser Konfigurationsphase wird der Benutzer nicht erneut aufgefordert."
|
||||
|
||||
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
|
||||
msgid "Stage used to configure a TOTP authenticator (i.e. Authy/Google Authenticator)."
|
||||
|
@ -4470,6 +4474,10 @@ msgstr "Phasen"
|
|||
msgid "Stages are single steps of a Flow that a user is guided through. A stage can only be executed from within a flow."
|
||||
msgstr "Phasen sind einzelne Schritte eines Flows, durch die ein Benutzer geführt wird. Eine Phase kann nur innerhalb eines Flows ausgeführt werden."
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Stages used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/outposts/ServiceConnectionListPage.ts
|
||||
msgid "State"
|
||||
msgstr "Zustand"
|
||||
|
@ -5935,6 +5943,10 @@ msgstr "Wenn diese Option aktiviert ist, wird die Einladung nach ihrer Benutzung
|
|||
msgid "When enabled, user fields are matched regardless of their casing."
|
||||
msgstr "Wenn diese Option aktiviert ist, werden Benutzerfelder unabhängig von ihrem Format abgeglichen."
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "When multiple stages are selected, the user can choose which one they want to enroll."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/stages/identification/IdentificationStageForm.ts
|
||||
msgid "When selected, a password field is shown on the same page instead of a separate page. This prevents username enumeration attacks."
|
||||
msgstr "Wenn diese Option ausgewählt ist, wird ein Passwortfeld auf derselben Seite statt auf einer separaten Seite angezeigt. Dadurch werden Angriffe auf die Aufzählung von Benutzernamen verhindert."
|
||||
|
|
|
@ -950,8 +950,12 @@ msgid "Configuration flow"
|
|||
msgstr "Flujo de configuración"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Configuration stage"
|
||||
msgstr "Etapa de configuración"
|
||||
#~ msgid "Configuration stage"
|
||||
#~ msgstr "Etapa de configuración"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Configuration stages"
|
||||
msgstr ""
|
||||
|
||||
#~ msgid "Configure WebAuthn"
|
||||
#~ msgstr "Configurar WebAuthn"
|
||||
|
@ -4403,8 +4407,8 @@ msgid "Stage type"
|
|||
msgstr "Tipo de escenario"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Stage used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again."
|
||||
msgstr "Etapa utilizada para configurar Authenticator cuando el usuario no tiene ningún dispositivo compatible. Una vez superada esta etapa de configuración, no se volverá a preguntar al usuario."
|
||||
#~ msgid "Stage used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again."
|
||||
#~ msgstr "Etapa utilizada para configurar Authenticator cuando el usuario no tiene ningún dispositivo compatible. Una vez superada esta etapa de configuración, no se volverá a preguntar al usuario."
|
||||
|
||||
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
|
||||
msgid "Stage used to configure a TOTP authenticator (i.e. Authy/Google Authenticator)."
|
||||
|
@ -4463,6 +4467,10 @@ msgstr "Etapas"
|
|||
msgid "Stages are single steps of a Flow that a user is guided through. A stage can only be executed from within a flow."
|
||||
msgstr "Las etapas son pasos individuales de un flujo por los que se guía al usuario. Una etapa solo se puede ejecutar desde dentro de un flujo."
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Stages used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/outposts/ServiceConnectionListPage.ts
|
||||
msgid "State"
|
||||
msgstr "Estado"
|
||||
|
@ -5928,6 +5936,10 @@ msgstr "Cuando se habilita, la invitación se eliminará después de su uso."
|
|||
msgid "When enabled, user fields are matched regardless of their casing."
|
||||
msgstr "Cuando se habilita, los campos de usuario coinciden independientemente de su carcasa."
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "When multiple stages are selected, the user can choose which one they want to enroll."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/stages/identification/IdentificationStageForm.ts
|
||||
msgid "When selected, a password field is shown on the same page instead of a separate page. This prevents username enumeration attacks."
|
||||
msgstr "Cuando se selecciona, se muestra un campo de contraseña en la misma página en lugar de en una página separada. Esto evita ataques de enumeración de nombres de usuario."
|
||||
|
|
|
@ -947,8 +947,12 @@ msgid "Configuration flow"
|
|||
msgstr "Przepływ konfiguracji"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Configuration stage"
|
||||
msgstr "Etap konfiguracji"
|
||||
#~ msgid "Configuration stage"
|
||||
#~ msgstr "Etap konfiguracji"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Configuration stages"
|
||||
msgstr ""
|
||||
|
||||
#~ msgid "Configure WebAuthn"
|
||||
#~ msgstr "Skonfiguruj WebAuthn"
|
||||
|
@ -4400,8 +4404,8 @@ msgid "Stage type"
|
|||
msgstr "Typ etapu"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Stage used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again."
|
||||
msgstr "Etap używany do konfiguracji uwierzytelniacza, gdy użytkownik nie ma żadnych kompatybilnych urządzeń. Po zakończeniu tego etapu konfiguracji użytkownik nie jest ponownie pytany."
|
||||
#~ msgid "Stage used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again."
|
||||
#~ msgstr "Etap używany do konfiguracji uwierzytelniacza, gdy użytkownik nie ma żadnych kompatybilnych urządzeń. Po zakończeniu tego etapu konfiguracji użytkownik nie jest ponownie pytany."
|
||||
|
||||
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
|
||||
msgid "Stage used to configure a TOTP authenticator (i.e. Authy/Google Authenticator)."
|
||||
|
@ -4460,6 +4464,10 @@ msgstr "Etapy"
|
|||
msgid "Stages are single steps of a Flow that a user is guided through. A stage can only be executed from within a flow."
|
||||
msgstr "Etapy to pojedyncze kroki przepływu, przez które prowadzony jest użytkownik. Etap można wykonać tylko z przepływu."
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Stages used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/outposts/ServiceConnectionListPage.ts
|
||||
msgid "State"
|
||||
msgstr "Stan"
|
||||
|
@ -5925,6 +5933,10 @@ msgstr "Po włączeniu zaproszenie zostanie usunięte po użyciu."
|
|||
msgid "When enabled, user fields are matched regardless of their casing."
|
||||
msgstr "Po włączeniu pola użytkownika są dopasowywane niezależnie od wielkości liter."
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "When multiple stages are selected, the user can choose which one they want to enroll."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/stages/identification/IdentificationStageForm.ts
|
||||
msgid "When selected, a password field is shown on the same page instead of a separate page. This prevents username enumeration attacks."
|
||||
msgstr "Po wybraniu pole hasła jest wyświetlane na tej samej stronie zamiast na osobnej stronie. Zapobiega to atakom polegającym na wyliczaniu nazw użytkowników."
|
||||
|
|
|
@ -950,8 +950,12 @@ msgid "Configuration flow"
|
|||
msgstr "Yapılandırma akışı"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Configuration stage"
|
||||
msgstr "Yapılandırma aşamasında"
|
||||
#~ msgid "Configuration stage"
|
||||
#~ msgstr "Yapılandırma aşamasında"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Configuration stages"
|
||||
msgstr ""
|
||||
|
||||
#~ msgid "Configure WebAuthn"
|
||||
#~ msgstr "WebAuthn'i Yapılandır"
|
||||
|
@ -4405,8 +4409,8 @@ msgid "Stage type"
|
|||
msgstr "Aşama türü"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Stage used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again."
|
||||
msgstr "Kullanıcının uyumlu bir aygıtı olmadığında Kimlik Doğrulayıcısı'nı yapılandırmak için kullanılan Aşama. Bu yapılandırma Stage geçtikten sonra kullanıcıya yeniden istenmez."
|
||||
#~ msgid "Stage used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again."
|
||||
#~ msgstr "Kullanıcının uyumlu bir aygıtı olmadığında Kimlik Doğrulayıcısı'nı yapılandırmak için kullanılan Aşama. Bu yapılandırma Stage geçtikten sonra kullanıcıya yeniden istenmez."
|
||||
|
||||
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
|
||||
msgid "Stage used to configure a TOTP authenticator (i.e. Authy/Google Authenticator)."
|
||||
|
@ -4465,6 +4469,10 @@ msgstr "Aşamalar"
|
|||
msgid "Stages are single steps of a Flow that a user is guided through. A stage can only be executed from within a flow."
|
||||
msgstr "Aşamalar, bir Akış'ın kullanıcının yönlendirildiği tek adımlardır. Bir aşama yalnızca bir akış içinden yürütülebilir."
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Stages used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/outposts/ServiceConnectionListPage.ts
|
||||
msgid "State"
|
||||
msgstr "Eyalet"
|
||||
|
@ -5930,6 +5938,10 @@ msgstr "Etkinleştirildiğinde, davetiye kullanımdan sonra silinir."
|
|||
msgid "When enabled, user fields are matched regardless of their casing."
|
||||
msgstr "Etkinleştirildiğinde, kullanıcı alanları muhafazası ne olursa olsun eşleştirilir."
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "When multiple stages are selected, the user can choose which one they want to enroll."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/stages/identification/IdentificationStageForm.ts
|
||||
msgid "When selected, a password field is shown on the same page instead of a separate page. This prevents username enumeration attacks."
|
||||
msgstr "Seçildiğinde, ayrı bir sayfa yerine aynı sayfada bir parola alanı gösterilir. Bu, kullanıcı adı numaralandırma saldırılarını engeller."
|
||||
|
|
|
@ -948,8 +948,12 @@ msgid "Configuration flow"
|
|||
msgstr "配置流程"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Configuration stage"
|
||||
msgstr "配置阶段"
|
||||
#~ msgid "Configuration stage"
|
||||
#~ msgstr "配置阶段"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Configuration stages"
|
||||
msgstr ""
|
||||
|
||||
#~ msgid "Configure WebAuthn"
|
||||
#~ msgstr "配置 WebAuthn"
|
||||
|
@ -4401,8 +4405,8 @@ msgid "Stage type"
|
|||
msgstr "阶段类型"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Stage used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again."
|
||||
msgstr "Stage 用于在用户没有任何兼容设备时配置身份验证器。此配置 Stage 通过后,不会再次提示用户。"
|
||||
#~ msgid "Stage used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again."
|
||||
#~ msgstr "Stage 用于在用户没有任何兼容设备时配置身份验证器。此配置 Stage 通过后,不会再次提示用户。"
|
||||
|
||||
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
|
||||
msgid "Stage used to configure a TOTP authenticator (i.e. Authy/Google Authenticator)."
|
||||
|
@ -4461,6 +4465,10 @@ msgstr "阶段"
|
|||
msgid "Stages are single steps of a Flow that a user is guided through. A stage can only be executed from within a flow."
|
||||
msgstr "阶段是引导用户完成的流程的单个步骤。阶段只能在流程内部执行。"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Stages used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/outposts/ServiceConnectionListPage.ts
|
||||
msgid "State"
|
||||
msgstr "状态"
|
||||
|
@ -5926,6 +5934,10 @@ msgstr "启用后,邀请将在使用后被删除。"
|
|||
msgid "When enabled, user fields are matched regardless of their casing."
|
||||
msgstr "启用后,无论用户字段大小写如何,都将匹配用户字段。"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "When multiple stages are selected, the user can choose which one they want to enroll."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/stages/identification/IdentificationStageForm.ts
|
||||
msgid "When selected, a password field is shown on the same page instead of a separate page. This prevents username enumeration attacks."
|
||||
msgstr "选中后,密码字段将显示在同一页面上,而不是单独的页面上。这样可以防止用户名枚举攻击。"
|
||||
|
|
|
@ -948,8 +948,12 @@ msgid "Configuration flow"
|
|||
msgstr "配置流程"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Configuration stage"
|
||||
msgstr "配置阶段"
|
||||
#~ msgid "Configuration stage"
|
||||
#~ msgstr "配置阶段"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Configuration stages"
|
||||
msgstr ""
|
||||
|
||||
#~ msgid "Configure WebAuthn"
|
||||
#~ msgstr "配置 WebAuthn"
|
||||
|
@ -4401,8 +4405,8 @@ msgid "Stage type"
|
|||
msgstr "阶段类型"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Stage used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again."
|
||||
msgstr "Stage 用于在用户没有任何兼容设备时配置身份验证器。此配置 Stage 通过后,不会再次提示用户。"
|
||||
#~ msgid "Stage used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again."
|
||||
#~ msgstr "Stage 用于在用户没有任何兼容设备时配置身份验证器。此配置 Stage 通过后,不会再次提示用户。"
|
||||
|
||||
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
|
||||
msgid "Stage used to configure a TOTP authenticator (i.e. Authy/Google Authenticator)."
|
||||
|
@ -4461,6 +4465,10 @@ msgstr "阶段"
|
|||
msgid "Stages are single steps of a Flow that a user is guided through. A stage can only be executed from within a flow."
|
||||
msgstr "阶段是引导用户完成的流程的单个步骤。阶段只能在流程内部执行。"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Stages used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/outposts/ServiceConnectionListPage.ts
|
||||
msgid "State"
|
||||
msgstr "州"
|
||||
|
@ -5926,6 +5934,10 @@ msgstr "启用后,邀请将在使用后被删除。"
|
|||
msgid "When enabled, user fields are matched regardless of their casing."
|
||||
msgstr "启用后,无论用户字段大小写如何,都将匹配用户字段。"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "When multiple stages are selected, the user can choose which one they want to enroll."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/stages/identification/IdentificationStageForm.ts
|
||||
msgid "When selected, a password field is shown on the same page instead of a separate page. This prevents username enumeration attacks."
|
||||
msgstr "选中后,密码字段将显示在同一页面上,而不是单独的页面上。这样可以防止用户名枚举攻击。"
|
||||
|
|
|
@ -948,8 +948,12 @@ msgid "Configuration flow"
|
|||
msgstr "配置流程"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Configuration stage"
|
||||
msgstr "配置阶段"
|
||||
#~ msgid "Configuration stage"
|
||||
#~ msgstr "配置阶段"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Configuration stages"
|
||||
msgstr ""
|
||||
|
||||
#~ msgid "Configure WebAuthn"
|
||||
#~ msgstr "配置 WebAuthn"
|
||||
|
@ -4401,8 +4405,8 @@ msgid "Stage type"
|
|||
msgstr "阶段类型"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Stage used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again."
|
||||
msgstr "Stage 用于在用户没有任何兼容设备时配置身份验证器。此配置 Stage 通过后,不会再次提示用户。"
|
||||
#~ msgid "Stage used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again."
|
||||
#~ msgstr "Stage 用于在用户没有任何兼容设备时配置身份验证器。此配置 Stage 通过后,不会再次提示用户。"
|
||||
|
||||
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
|
||||
msgid "Stage used to configure a TOTP authenticator (i.e. Authy/Google Authenticator)."
|
||||
|
@ -4461,6 +4465,10 @@ msgstr "阶段"
|
|||
msgid "Stages are single steps of a Flow that a user is guided through. A stage can only be executed from within a flow."
|
||||
msgstr "阶段是引导用户完成的流程的单个步骤。阶段只能在流程内部执行。"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Stages used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/outposts/ServiceConnectionListPage.ts
|
||||
msgid "State"
|
||||
msgstr "州"
|
||||
|
@ -5926,6 +5934,10 @@ msgstr "启用后,邀请将在使用后被删除。"
|
|||
msgid "When enabled, user fields are matched regardless of their casing."
|
||||
msgstr "启用后,无论用户字段大小写如何,都将匹配用户字段。"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "When multiple stages are selected, the user can choose which one they want to enroll."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/stages/identification/IdentificationStageForm.ts
|
||||
msgid "When selected, a password field is shown on the same page instead of a separate page. This prevents username enumeration attacks."
|
||||
msgstr "选中后,密码字段将显示在同一页面上,而不是单独的页面上。这样可以防止用户名枚举攻击。"
|
||||
|
|
|
@ -25,14 +25,14 @@ export class AuthenticatorValidateStageForm extends ModelForm<AuthenticatorValid
|
|||
stageUuid: pk,
|
||||
})
|
||||
.then((stage) => {
|
||||
this.showConfigurationStage =
|
||||
this.showConfigurationStages =
|
||||
stage.notConfiguredAction === NotConfiguredActionEnum.Configure;
|
||||
return stage;
|
||||
});
|
||||
}
|
||||
|
||||
@property({ type: Boolean })
|
||||
showConfigurationStage = true;
|
||||
showConfigurationStages = true;
|
||||
|
||||
getSuccessMessage(): string {
|
||||
if (this.instance) {
|
||||
|
@ -136,9 +136,9 @@ export class AuthenticatorValidateStageForm extends ModelForm<AuthenticatorValid
|
|||
target.selectedOptions[0].value ===
|
||||
NotConfiguredActionEnum.Configure
|
||||
) {
|
||||
this.showConfigurationStage = true;
|
||||
this.showConfigurationStages = true;
|
||||
} else {
|
||||
this.showConfigurationStage = false;
|
||||
this.showConfigurationStages = false;
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
@ -165,21 +165,13 @@ export class AuthenticatorValidateStageForm extends ModelForm<AuthenticatorValid
|
|||
</option>
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
${this.showConfigurationStage
|
||||
${this.showConfigurationStages
|
||||
? html`
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Configuration stage`}
|
||||
?required=${true}
|
||||
name="configurationStage"
|
||||
label=${t`Configuration stages`}
|
||||
name="configurationStages"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option
|
||||
value=""
|
||||
?selected=${this.instance?.configurationStage ===
|
||||
undefined}
|
||||
>
|
||||
---------
|
||||
</option>
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${until(
|
||||
new StagesApi(DEFAULT_CONFIG)
|
||||
.stagesAllList({
|
||||
|
@ -187,9 +179,11 @@ export class AuthenticatorValidateStageForm extends ModelForm<AuthenticatorValid
|
|||
})
|
||||
.then((stages) => {
|
||||
return stages.results.map((stage) => {
|
||||
const selected =
|
||||
this.instance?.configurationStage ===
|
||||
stage.pk;
|
||||
const selected = Array.from(
|
||||
this.instance?.configurationStages || [],
|
||||
).some((su) => {
|
||||
return su == stage.pk;
|
||||
});
|
||||
return html`<option
|
||||
value=${ifDefined(stage.pk)}
|
||||
?selected=${selected}
|
||||
|
@ -202,7 +196,10 @@ export class AuthenticatorValidateStageForm extends ModelForm<AuthenticatorValid
|
|||
)}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Stage used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again.`}
|
||||
${t`Stages used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again.`}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`When multiple stages are selected, the user can choose which one they want to enroll.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
`
|
||||
|
|
|
@ -153,7 +153,7 @@ export class IdentificationStageForm extends ModelForm<IdentificationStage, stri
|
|||
?required=${true}
|
||||
name="sources"
|
||||
>
|
||||
<select name="users" class="pf-c-form-control" multiple>
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${until(
|
||||
new SourcesApi(DEFAULT_CONFIG)
|
||||
.sourcesAllList({})
|
||||
|
|
Reference in New Issue