stages: Add ability to set user friendly names for MFA stages (#5005)

* Added ability to name MFA stage

* Schema

* Changed Charfield to Textfield

* Regenerated schema

* Add explicit required

* set null instead of blank so title check works

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add help text and adjust wording

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
sdimovv 2023-04-02 15:52:44 +01:00 committed by GitHub
parent fd2677af1f
commit 6192d01b7e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 275 additions and 47 deletions

View file

@ -27,6 +27,6 @@ class UserSettingSerializer(PassiveSerializer):
object_uid = CharField() object_uid = CharField()
component = CharField() component = CharField()
title = CharField() title = CharField(required=True)
configure_url = CharField(required=False) configure_url = CharField(required=False)
icon_url = CharField(required=False) icon_url = CharField(required=False)

View file

@ -271,6 +271,15 @@ class ConfigurableStage(models.Model):
abstract = True abstract = True
class FriendlyNamedStage(models.Model):
"""Abstract base class for a Stage that can have a user friendly name configured."""
friendly_name = models.TextField(null=True)
class Meta:
abstract = True
class FlowToken(Token): class FlowToken(Token):
"""Subclass of a standard Token, stores the currently active flow plan upon creation. """Subclass of a standard Token, stores the currently active flow plan upon creation.
Can be used to later resume a flow.""" Can be used to later resume a flow."""

View file

@ -33,6 +33,7 @@ class AuthenticatorDuoStageSerializer(StageSerializer):
model = AuthenticatorDuoStage model = AuthenticatorDuoStage
fields = StageSerializer.Meta.fields + [ fields = StageSerializer.Meta.fields + [
"configure_flow", "configure_flow",
"friendly_name",
"client_id", "client_id",
"client_secret", "client_secret",
"api_hostname", "api_hostname",

View file

@ -0,0 +1,20 @@
# Generated by Django 4.1.7 on 2023-04-02 14:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
(
"authentik_stages_authenticator_duo",
"0004_authenticatorduostage_admin_integration_key_and_more",
),
]
operations = [
migrations.AddField(
model_name="authenticatorduostage",
name="friendly_name",
field=models.TextField(null=True),
),
]

View file

@ -12,12 +12,12 @@ from rest_framework.serializers import BaseSerializer, Serializer
from authentik import __version__ from authentik import __version__
from authentik.core.types import UserSettingSerializer from authentik.core.types import UserSettingSerializer
from authentik.flows.models import ConfigurableStage, Stage from authentik.flows.models import ConfigurableStage, FriendlyNamedStage, Stage
from authentik.lib.models import SerializerModel from authentik.lib.models import SerializerModel
from authentik.lib.utils.http import authentik_user_agent from authentik.lib.utils.http import authentik_user_agent
class AuthenticatorDuoStage(ConfigurableStage, Stage): class AuthenticatorDuoStage(ConfigurableStage, FriendlyNamedStage, Stage):
"""Setup Duo authenticator devices""" """Setup Duo authenticator devices"""
api_hostname = models.TextField() api_hostname = models.TextField()
@ -68,7 +68,7 @@ class AuthenticatorDuoStage(ConfigurableStage, Stage):
def ui_user_settings(self) -> Optional[UserSettingSerializer]: def ui_user_settings(self) -> Optional[UserSettingSerializer]:
return UserSettingSerializer( return UserSettingSerializer(
data={ data={
"title": str(self._meta.verbose_name), "title": self.friendly_name or str(self._meta.verbose_name),
"component": "ak-user-settings-authenticator-duo", "component": "ak-user-settings-authenticator-duo",
} }
) )

View file

@ -19,6 +19,7 @@ class AuthenticatorSMSStageSerializer(StageSerializer):
model = AuthenticatorSMSStage model = AuthenticatorSMSStage
fields = StageSerializer.Meta.fields + [ fields = StageSerializer.Meta.fields + [
"configure_flow", "configure_flow",
"friendly_name",
"provider", "provider",
"from_number", "from_number",
"account_sid", "account_sid",

View file

@ -0,0 +1,17 @@
# Generated by Django 4.1.7 on 2023-04-02 14:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_stages_authenticator_sms", "0005_authenticatorsmsstage_mapping"),
]
operations = [
migrations.AddField(
model_name="authenticatorsmsstage",
name="friendly_name",
field=models.TextField(null=True),
),
]

View file

@ -17,7 +17,7 @@ from twilio.rest import Client
from authentik.core.types import UserSettingSerializer from authentik.core.types import UserSettingSerializer
from authentik.events.models import Event, EventAction, NotificationWebhookMapping from authentik.events.models import Event, EventAction, NotificationWebhookMapping
from authentik.events.utils import sanitize_item from authentik.events.utils import sanitize_item
from authentik.flows.models import ConfigurableStage, Stage from authentik.flows.models import ConfigurableStage, FriendlyNamedStage, Stage
from authentik.lib.models import SerializerModel from authentik.lib.models import SerializerModel
from authentik.lib.utils.errors import exception_to_string from authentik.lib.utils.errors import exception_to_string
from authentik.lib.utils.http import get_http_session from authentik.lib.utils.http import get_http_session
@ -39,7 +39,7 @@ class SMSAuthTypes(models.TextChoices):
BEARER = "bearer" BEARER = "bearer"
class AuthenticatorSMSStage(ConfigurableStage, Stage): class AuthenticatorSMSStage(ConfigurableStage, FriendlyNamedStage, Stage):
"""Use SMS-based TOTP instead of authenticator-based.""" """Use SMS-based TOTP instead of authenticator-based."""
provider = models.TextField(choices=SMSProviders.choices) provider = models.TextField(choices=SMSProviders.choices)
@ -168,7 +168,7 @@ class AuthenticatorSMSStage(ConfigurableStage, Stage):
def ui_user_settings(self) -> Optional[UserSettingSerializer]: def ui_user_settings(self) -> Optional[UserSettingSerializer]:
return UserSettingSerializer( return UserSettingSerializer(
data={ data={
"title": str(self._meta.verbose_name), "title": self.friendly_name or str(self._meta.verbose_name),
"component": "ak-user-settings-authenticator-sms", "component": "ak-user-settings-authenticator-sms",
} }
) )

View file

@ -18,7 +18,7 @@ class AuthenticatorStaticStageSerializer(StageSerializer):
class Meta: class Meta:
model = AuthenticatorStaticStage model = AuthenticatorStaticStage
fields = StageSerializer.Meta.fields + ["configure_flow", "token_count"] fields = StageSerializer.Meta.fields + ["configure_flow", "friendly_name", "token_count"]
class AuthenticatorStaticStageViewSet(UsedByMixin, ModelViewSet): class AuthenticatorStaticStageViewSet(UsedByMixin, ModelViewSet):

View file

@ -0,0 +1,17 @@
# Generated by Django 4.1.7 on 2023-04-02 14:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_stages_authenticator_static", "0005_default_setup_flow"),
]
operations = [
migrations.AddField(
model_name="authenticatorstaticstage",
name="friendly_name",
field=models.TextField(null=True),
),
]

View file

@ -7,10 +7,10 @@ from django.views import View
from rest_framework.serializers import BaseSerializer from rest_framework.serializers import BaseSerializer
from authentik.core.types import UserSettingSerializer from authentik.core.types import UserSettingSerializer
from authentik.flows.models import ConfigurableStage, Stage from authentik.flows.models import ConfigurableStage, FriendlyNamedStage, Stage
class AuthenticatorStaticStage(ConfigurableStage, Stage): class AuthenticatorStaticStage(ConfigurableStage, FriendlyNamedStage, Stage):
"""Generate static tokens for the user as a backup.""" """Generate static tokens for the user as a backup."""
token_count = models.IntegerField(default=6) token_count = models.IntegerField(default=6)
@ -34,7 +34,7 @@ class AuthenticatorStaticStage(ConfigurableStage, Stage):
def ui_user_settings(self) -> Optional[UserSettingSerializer]: def ui_user_settings(self) -> Optional[UserSettingSerializer]:
return UserSettingSerializer( return UserSettingSerializer(
data={ data={
"title": str(self._meta.verbose_name), "title": self.friendly_name or str(self._meta.verbose_name),
"component": "ak-user-settings-authenticator-static", "component": "ak-user-settings-authenticator-static",
} }
) )

View file

@ -18,7 +18,7 @@ class AuthenticatorTOTPStageSerializer(StageSerializer):
class Meta: class Meta:
model = AuthenticatorTOTPStage model = AuthenticatorTOTPStage
fields = StageSerializer.Meta.fields + ["configure_flow", "digits"] fields = StageSerializer.Meta.fields + ["configure_flow", "friendly_name", "digits"]
class AuthenticatorTOTPStageViewSet(UsedByMixin, ModelViewSet): class AuthenticatorTOTPStageViewSet(UsedByMixin, ModelViewSet):

View file

@ -0,0 +1,17 @@
# Generated by Django 4.1.7 on 2023-04-02 14:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_stages_authenticator_totp", "0006_default_setup_flow"),
]
operations = [
migrations.AddField(
model_name="authenticatortotpstage",
name="friendly_name",
field=models.TextField(null=True),
),
]

View file

@ -7,7 +7,7 @@ from django.views import View
from rest_framework.serializers import BaseSerializer from rest_framework.serializers import BaseSerializer
from authentik.core.types import UserSettingSerializer from authentik.core.types import UserSettingSerializer
from authentik.flows.models import ConfigurableStage, Stage from authentik.flows.models import ConfigurableStage, FriendlyNamedStage, Stage
class TOTPDigits(models.IntegerChoices): class TOTPDigits(models.IntegerChoices):
@ -17,7 +17,7 @@ class TOTPDigits(models.IntegerChoices):
EIGHT = 8, _("8 digits, not compatible with apps like Google Authenticator") EIGHT = 8, _("8 digits, not compatible with apps like Google Authenticator")
class AuthenticatorTOTPStage(ConfigurableStage, Stage): class AuthenticatorTOTPStage(ConfigurableStage, FriendlyNamedStage, Stage):
"""Enroll a user's device into Time-based OTP.""" """Enroll a user's device into Time-based OTP."""
digits = models.IntegerField(choices=TOTPDigits.choices) digits = models.IntegerField(choices=TOTPDigits.choices)
@ -41,7 +41,7 @@ class AuthenticatorTOTPStage(ConfigurableStage, Stage):
def ui_user_settings(self) -> Optional[UserSettingSerializer]: def ui_user_settings(self) -> Optional[UserSettingSerializer]:
return UserSettingSerializer( return UserSettingSerializer(
data={ data={
"title": str(self._meta.verbose_name), "title": self.friendly_name or str(self._meta.verbose_name),
"component": "ak-user-settings-authenticator-totp", "component": "ak-user-settings-authenticator-totp",
} }
) )

View file

@ -19,6 +19,7 @@ class AuthenticateWebAuthnStageSerializer(StageSerializer):
model = AuthenticateWebAuthnStage model = AuthenticateWebAuthnStage
fields = StageSerializer.Meta.fields + [ fields = StageSerializer.Meta.fields + [
"configure_flow", "configure_flow",
"friendly_name",
"user_verification", "user_verification",
"authenticator_attachment", "authenticator_attachment",
"resident_key_requirement", "resident_key_requirement",

View file

@ -0,0 +1,17 @@
# Generated by Django 4.1.7 on 2023-04-02 14:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_stages_authenticator_webauthn", "0008_alter_webauthndevice_credential_id"),
]
operations = [
migrations.AddField(
model_name="authenticatewebauthnstage",
name="friendly_name",
field=models.TextField(null=True),
),
]

View file

@ -12,7 +12,7 @@ from webauthn.helpers.base64url_to_bytes import base64url_to_bytes
from webauthn.helpers.structs import PublicKeyCredentialDescriptor from webauthn.helpers.structs import PublicKeyCredentialDescriptor
from authentik.core.types import UserSettingSerializer from authentik.core.types import UserSettingSerializer
from authentik.flows.models import ConfigurableStage, Stage from authentik.flows.models import ConfigurableStage, FriendlyNamedStage, Stage
from authentik.lib.models import SerializerModel from authentik.lib.models import SerializerModel
@ -66,7 +66,7 @@ class AuthenticatorAttachment(models.TextChoices):
CROSS_PLATFORM = "cross-platform" CROSS_PLATFORM = "cross-platform"
class AuthenticateWebAuthnStage(ConfigurableStage, Stage): class AuthenticateWebAuthnStage(ConfigurableStage, FriendlyNamedStage, Stage):
"""WebAuthn stage""" """WebAuthn stage"""
user_verification = models.TextField( user_verification = models.TextField(
@ -100,7 +100,7 @@ class AuthenticateWebAuthnStage(ConfigurableStage, Stage):
def ui_user_settings(self) -> Optional[UserSettingSerializer]: def ui_user_settings(self) -> Optional[UserSettingSerializer]:
return UserSettingSerializer( return UserSettingSerializer(
data={ data={
"title": str(self._meta.verbose_name), "title": self.friendly_name or str(self._meta.verbose_name),
"component": "ak-user-settings-authenticator-webauthn", "component": "ak-user-settings-authenticator-webauthn",
} }
) )

View file

@ -20345,6 +20345,10 @@ paths:
schema: schema:
type: string type: string
format: uuid format: uuid
- in: query
name: friendly_name
schema:
type: string
- in: query - in: query
name: from_number name: from_number
schema: schema:
@ -20650,6 +20654,10 @@ paths:
schema: schema:
type: string type: string
format: uuid format: uuid
- in: query
name: friendly_name
schema:
type: string
- in: query - in: query
name: name name: name
schema: schema:
@ -20946,6 +20954,10 @@ paths:
* `6` - 6 digits, widely compatible * `6` - 6 digits, widely compatible
* `8` - 8 digits, not compatible with apps like Google Authenticator * `8` - 8 digits, not compatible with apps like Google Authenticator
- in: query
name: friendly_name
schema:
type: string
- in: query - in: query
name: name name: name
schema: schema:
@ -21533,6 +21545,10 @@ paths:
schema: schema:
type: string type: string
format: uuid format: uuid
- in: query
name: friendly_name
schema:
type: string
- in: query - in: query
name: name name: name
schema: schema:
@ -26560,6 +26576,9 @@ components:
nullable: true nullable: true
description: Flow used by an authenticated user to configure this Stage. description: Flow used by an authenticated user to configure this Stage.
If empty, user will not be able to configure this stage. If empty, user will not be able to configure this stage.
friendly_name:
type: string
nullable: true
user_verification: user_verification:
$ref: '#/components/schemas/UserVerificationEnum' $ref: '#/components/schemas/UserVerificationEnum'
authenticator_attachment: authenticator_attachment:
@ -26592,6 +26611,10 @@ components:
nullable: true nullable: true
description: Flow used by an authenticated user to configure this Stage. description: Flow used by an authenticated user to configure this Stage.
If empty, user will not be able to configure this stage. If empty, user will not be able to configure this stage.
friendly_name:
type: string
nullable: true
minLength: 1
user_verification: user_verification:
$ref: '#/components/schemas/UserVerificationEnum' $ref: '#/components/schemas/UserVerificationEnum'
authenticator_attachment: authenticator_attachment:
@ -26819,6 +26842,9 @@ components:
nullable: true nullable: true
description: Flow used by an authenticated user to configure this Stage. description: Flow used by an authenticated user to configure this Stage.
If empty, user will not be able to configure this stage. If empty, user will not be able to configure this stage.
friendly_name:
type: string
nullable: true
client_id: client_id:
type: string type: string
api_hostname: api_hostname:
@ -26875,6 +26901,10 @@ components:
nullable: true nullable: true
description: Flow used by an authenticated user to configure this Stage. description: Flow used by an authenticated user to configure this Stage.
If empty, user will not be able to configure this stage. If empty, user will not be able to configure this stage.
friendly_name:
type: string
nullable: true
minLength: 1
client_id: client_id:
type: string type: string
minLength: 1 minLength: 1
@ -26973,6 +27003,9 @@ components:
nullable: true nullable: true
description: Flow used by an authenticated user to configure this Stage. description: Flow used by an authenticated user to configure this Stage.
If empty, user will not be able to configure this stage. If empty, user will not be able to configure this stage.
friendly_name:
type: string
nullable: true
provider: provider:
$ref: '#/components/schemas/ProviderEnum' $ref: '#/components/schemas/ProviderEnum'
from_number: from_number:
@ -27023,6 +27056,10 @@ components:
nullable: true nullable: true
description: Flow used by an authenticated user to configure this Stage. description: Flow used by an authenticated user to configure this Stage.
If empty, user will not be able to configure this stage. If empty, user will not be able to configure this stage.
friendly_name:
type: string
nullable: true
minLength: 1
provider: provider:
$ref: '#/components/schemas/ProviderEnum' $ref: '#/components/schemas/ProviderEnum'
from_number: from_number:
@ -27129,6 +27166,9 @@ components:
nullable: true nullable: true
description: Flow used by an authenticated user to configure this Stage. description: Flow used by an authenticated user to configure this Stage.
If empty, user will not be able to configure this stage. If empty, user will not be able to configure this stage.
friendly_name:
type: string
nullable: true
token_count: token_count:
type: integer type: integer
maximum: 2147483647 maximum: 2147483647
@ -27157,6 +27197,10 @@ components:
nullable: true nullable: true
description: Flow used by an authenticated user to configure this Stage. description: Flow used by an authenticated user to configure this Stage.
If empty, user will not be able to configure this stage. If empty, user will not be able to configure this stage.
friendly_name:
type: string
nullable: true
minLength: 1
token_count: token_count:
type: integer type: integer
maximum: 2147483647 maximum: 2147483647
@ -27240,6 +27284,9 @@ components:
nullable: true nullable: true
description: Flow used by an authenticated user to configure this Stage. description: Flow used by an authenticated user to configure this Stage.
If empty, user will not be able to configure this stage. If empty, user will not be able to configure this stage.
friendly_name:
type: string
nullable: true
digits: digits:
allOf: allOf:
- $ref: '#/components/schemas/DigitsEnum' - $ref: '#/components/schemas/DigitsEnum'
@ -27270,6 +27317,10 @@ components:
nullable: true nullable: true
description: Flow used by an authenticated user to configure this Stage. description: Flow used by an authenticated user to configure this Stage.
If empty, user will not be able to configure this stage. If empty, user will not be able to configure this stage.
friendly_name:
type: string
nullable: true
minLength: 1
digits: digits:
allOf: allOf:
- $ref: '#/components/schemas/DigitsEnum' - $ref: '#/components/schemas/DigitsEnum'
@ -35347,6 +35398,10 @@ components:
nullable: true nullable: true
description: Flow used by an authenticated user to configure this Stage. description: Flow used by an authenticated user to configure this Stage.
If empty, user will not be able to configure this stage. If empty, user will not be able to configure this stage.
friendly_name:
type: string
nullable: true
minLength: 1
user_verification: user_verification:
$ref: '#/components/schemas/UserVerificationEnum' $ref: '#/components/schemas/UserVerificationEnum'
authenticator_attachment: authenticator_attachment:
@ -35372,6 +35427,10 @@ components:
nullable: true nullable: true
description: Flow used by an authenticated user to configure this Stage. description: Flow used by an authenticated user to configure this Stage.
If empty, user will not be able to configure this stage. If empty, user will not be able to configure this stage.
friendly_name:
type: string
nullable: true
minLength: 1
client_id: client_id:
type: string type: string
minLength: 1 minLength: 1
@ -35404,6 +35463,10 @@ components:
nullable: true nullable: true
description: Flow used by an authenticated user to configure this Stage. description: Flow used by an authenticated user to configure this Stage.
If empty, user will not be able to configure this stage. If empty, user will not be able to configure this stage.
friendly_name:
type: string
nullable: true
minLength: 1
provider: provider:
$ref: '#/components/schemas/ProviderEnum' $ref: '#/components/schemas/ProviderEnum'
from_number: from_number:
@ -35446,6 +35509,10 @@ components:
nullable: true nullable: true
description: Flow used by an authenticated user to configure this Stage. description: Flow used by an authenticated user to configure this Stage.
If empty, user will not be able to configure this stage. If empty, user will not be able to configure this stage.
friendly_name:
type: string
nullable: true
minLength: 1
token_count: token_count:
type: integer type: integer
maximum: 2147483647 maximum: 2147483647
@ -35467,6 +35534,10 @@ components:
nullable: true nullable: true
description: Flow used by an authenticated user to configure this Stage. description: Flow used by an authenticated user to configure this Stage.
If empty, user will not be able to configure this stage. If empty, user will not be able to configure this stage.
friendly_name:
type: string
nullable: true
minLength: 1
digits: digits:
allOf: allOf:
- $ref: '#/components/schemas/DigitsEnum' - $ref: '#/components/schemas/DigitsEnum'

View file

@ -10,7 +10,6 @@ import { t } from "@lingui/macro";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
import { customElement } from "lit/decorators.js"; import { customElement } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import { import {
AuthenticatorDuoStage, AuthenticatorDuoStage,
@ -59,11 +58,25 @@ export class AuthenticatorDuoStageForm extends ModelForm<AuthenticatorDuoStage,
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name"> <ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
<input <input
type="text" type="text"
value="${ifDefined(this.instance?.name || "")}" value="${first(this.instance?.name, "")}"
class="pf-c-form-control" class="pf-c-form-control"
required required
/> />
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`Authenticator type name`}
?required=${false}
name="friendlyName"
>
<input
type="text"
value="${first(this.instance?.friendlyName, "")}"
class="pf-c-form-control"
/>
<p class="pf-c-form__helper-text">
${t`Display name of this authenticator, used by users when they enroll an authenticator.`}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`API Hostname`} label=${t`API Hostname`}
?required=${true} ?required=${true}

View file

@ -11,7 +11,6 @@ import { t } from "@lingui/macro";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
import { customElement, property } from "lit/decorators.js"; import { customElement, property } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import { import {
AuthTypeEnum, AuthTypeEnum,
@ -76,7 +75,7 @@ export class AuthenticatorSMSStageForm extends ModelForm<AuthenticatorSMSStage,
> >
<input <input
type="text" type="text"
value="${ifDefined(this.instance?.accountSid || "")}" value="${first(this.instance?.accountSid, "")}"
class="pf-c-form-control" class="pf-c-form-control"
required required
/> />
@ -87,7 +86,7 @@ export class AuthenticatorSMSStageForm extends ModelForm<AuthenticatorSMSStage,
<ak-form-element-horizontal label=${t`Twilio Auth Token`} ?required=${true} name="auth"> <ak-form-element-horizontal label=${t`Twilio Auth Token`} ?required=${true} name="auth">
<input <input
type="text" type="text"
value="${ifDefined(this.instance?.auth || "")}" value="${first(this.instance?.auth, "")}"
class="pf-c-form-control" class="pf-c-form-control"
required required
/> />
@ -131,7 +130,7 @@ export class AuthenticatorSMSStageForm extends ModelForm<AuthenticatorSMSStage,
> >
<input <input
type="text" type="text"
value="${ifDefined(this.instance?.accountSid || "")}" value="${first(this.instance?.accountSid, "")}"
class="pf-c-form-control" class="pf-c-form-control"
required required
/> />
@ -142,7 +141,7 @@ export class AuthenticatorSMSStageForm extends ModelForm<AuthenticatorSMSStage,
<ak-form-element-horizontal label=${t`API Auth Username`} ?required=${true} name="auth"> <ak-form-element-horizontal label=${t`API Auth Username`} ?required=${true} name="auth">
<input <input
type="text" type="text"
value="${ifDefined(this.instance?.auth || "")}" value="${first(this.instance?.auth, "")}"
class="pf-c-form-control" class="pf-c-form-control"
/> />
<p class="pf-c-form__helper-text"> <p class="pf-c-form__helper-text">
@ -156,7 +155,7 @@ export class AuthenticatorSMSStageForm extends ModelForm<AuthenticatorSMSStage,
> >
<input <input
type="text" type="text"
value="${ifDefined(this.instance?.authPassword)}" value="${first(this.instance?.authPassword, "")}"
class="pf-c-form-control" class="pf-c-form-control"
/> />
<p class="pf-c-form__helper-text"> <p class="pf-c-form__helper-text">
@ -206,11 +205,25 @@ export class AuthenticatorSMSStageForm extends ModelForm<AuthenticatorSMSStage,
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name"> <ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
<input <input
type="text" type="text"
value="${ifDefined(this.instance?.name || "")}" value="${first(this.instance?.name, "")}"
class="pf-c-form-control" class="pf-c-form-control"
required required
/> />
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`Authenticator type name`}
?required=${false}
name="friendlyName"
>
<input
type="text"
value="${first(this.instance?.friendlyName, "")}"
class="pf-c-form-control"
/>
<p class="pf-c-form__helper-text">
${t`Display name of this authenticator, used by users when they enroll an authenticator.`}
</p>
</ak-form-element-horizontal>
<ak-form-group .expanded=${true}> <ak-form-group .expanded=${true}>
<span slot="header"> ${t`Stage-specific settings`} </span> <span slot="header"> ${t`Stage-specific settings`} </span>
<div slot="body" class="pf-c-form"> <div slot="body" class="pf-c-form">
@ -247,7 +260,7 @@ export class AuthenticatorSMSStageForm extends ModelForm<AuthenticatorSMSStage,
> >
<input <input
type="text" type="text"
value="${ifDefined(this.instance?.fromNumber || "")}" value="${first(this.instance?.fromNumber, "")}"
class="pf-c-form-control" class="pf-c-form-control"
required required
/> />

View file

@ -9,7 +9,6 @@ import { t } from "@lingui/macro";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
import { customElement } from "lit/decorators.js"; import { customElement } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import { import {
AuthenticatorStaticStage, AuthenticatorStaticStage,
@ -57,11 +56,25 @@ export class AuthenticatorStaticStageForm extends ModelForm<AuthenticatorStaticS
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name"> <ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
<input <input
type="text" type="text"
value="${ifDefined(this.instance?.name || "")}" value="${first(this.instance?.name, "")}"
class="pf-c-form-control" class="pf-c-form-control"
required required
/> />
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`Authenticator type name`}
?required=${false}
name="friendlyName"
>
<input
type="text"
value="${first(this.instance?.friendlyName, "")}"
class="pf-c-form-control"
/>
<p class="pf-c-form__helper-text">
${t`Display name of this authenticator, used by users when they enroll an authenticator.`}
</p>
</ak-form-element-horizontal>
<ak-form-group .expanded=${true}> <ak-form-group .expanded=${true}>
<span slot="header"> ${t`Stage-specific settings`} </span> <span slot="header"> ${t`Stage-specific settings`} </span>
<div slot="body" class="pf-c-form"> <div slot="body" class="pf-c-form">

View file

@ -1,5 +1,6 @@
import { RenderFlowOption } from "@goauthentik/admin/flows/utils"; import { RenderFlowOption } from "@goauthentik/admin/flows/utils";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/elements/forms/FormGroup"; import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement"; import "@goauthentik/elements/forms/HorizontalFormElement";
import { ModelForm } from "@goauthentik/elements/forms/ModelForm"; import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
@ -9,7 +10,6 @@ import { t } from "@lingui/macro";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
import { customElement } from "lit/decorators.js"; import { customElement } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import { import {
AuthenticatorTOTPStage, AuthenticatorTOTPStage,
@ -58,11 +58,25 @@ export class AuthenticatorTOTPStageForm extends ModelForm<AuthenticatorTOTPStage
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name"> <ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
<input <input
type="text" type="text"
value="${ifDefined(this.instance?.name || "")}" value="${first(this.instance?.name, "")}"
class="pf-c-form-control" class="pf-c-form-control"
required required
/> />
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`Authenticator type name`}
?required=${false}
name="friendlyName"
>
<input
type="text"
value="${first(this.instance?.friendlyName, "")}"
class="pf-c-form-control"
/>
<p class="pf-c-form__helper-text">
${t`Display name of this authenticator, used by users when they enroll an authenticator.`}
</p>
</ak-form-element-horizontal>
<ak-form-group .expanded=${true}> <ak-form-group .expanded=${true}>
<span slot="header"> ${t`Stage-specific settings`} </span> <span slot="header"> ${t`Stage-specific settings`} </span>
<div slot="body" class="pf-c-form"> <div slot="body" class="pf-c-form">

View file

@ -1,5 +1,6 @@
import { RenderFlowOption } from "@goauthentik/admin/flows/utils"; import { RenderFlowOption } from "@goauthentik/admin/flows/utils";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/elements/forms/HorizontalFormElement"; import "@goauthentik/elements/forms/HorizontalFormElement";
import { ModelForm } from "@goauthentik/elements/forms/ModelForm"; import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/forms/Radio"; import "@goauthentik/elements/forms/Radio";
@ -9,7 +10,6 @@ import { t } from "@lingui/macro";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
import { customElement } from "lit/decorators.js"; import { customElement } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import { import {
AuthenticateWebAuthnStage, AuthenticateWebAuthnStage,
@ -63,11 +63,25 @@ export class AuthenticateWebAuthnStageForm extends ModelForm<AuthenticateWebAuth
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name"> <ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
<input <input
type="text" type="text"
value="${ifDefined(this.instance?.name || "")}" value="${first(this.instance?.name, "")}"
class="pf-c-form-control" class="pf-c-form-control"
required required
/> />
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`Authenticator type name`}
?required=${false}
name="friendlyName"
>
<input
type="text"
value="${first(this.instance?.friendlyName, "")}"
class="pf-c-form-control"
/>
<p class="pf-c-form__helper-text">
${t`Display name of this authenticator, used by users when they enroll an authenticator.`}
</p>
</ak-form-element-horizontal>
<ak-form-group .expanded=${true}> <ak-form-group .expanded=${true}>
<span slot="header"> ${t`Stage-specific settings`} </span> <span slot="header"> ${t`Stage-specific settings`} </span>
<div slot="body" class="pf-c-form"> <div slot="body" class="pf-c-form">

View file

@ -4,8 +4,7 @@ import "@goauthentik/elements/buttons/ModalButton";
import "@goauthentik/elements/buttons/TokenCopyButton"; import "@goauthentik/elements/buttons/TokenCopyButton";
import "@goauthentik/elements/forms/DeleteBulkForm"; import "@goauthentik/elements/forms/DeleteBulkForm";
import "@goauthentik/elements/forms/ModalForm"; import "@goauthentik/elements/forms/ModalForm";
import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table";
import { Table, TableColumn } from "@goauthentik/elements/table/Table";
import "@goauthentik/user/user-settings/mfa/MFADeviceForm"; import "@goauthentik/user/user-settings/mfa/MFADeviceForm";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
@ -17,17 +16,8 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { AuthenticatorsApi, Device, UserSetting } from "@goauthentik/api"; import { AuthenticatorsApi, Device, UserSetting } from "@goauthentik/api";
export function stageToAuthenticatorName(stage: UserSetting): string { export function stageToAuthenticatorName(stage: UserSetting): string {
switch (stage.component) { if (stage.title) {
case "ak-user-settings-authenticator-duo": return stage.title;
return t`Duo authenticator`;
case "ak-user-settings-authenticator-sms":
return t`SMS authenticator`;
case "ak-user-settings-authenticator-static":
return t`Static authenticator`;
case "ak-user-settings-authenticator-totp":
return t`TOTP authenticator`;
case "ak-user-settings-authenticator-webauthn":
return t`Security key authenticator`;
} }
return `Invalid stage component ${stage.component}`; return `Invalid stage component ${stage.component}`;
} }