re-gen migrations, implement one half of number matching
this also treats accept/deny as "number" matching (we call it item matching to make it more general), since it's just a more static version of selecting the correct thing Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
parent
afc347ddeb
commit
97871ecd6c
|
@ -21,19 +21,13 @@ from authentik.stages.authenticator_mobile.models import (
|
||||||
MobileDevice,
|
MobileDevice,
|
||||||
MobileDeviceToken,
|
MobileDeviceToken,
|
||||||
MobileTransaction,
|
MobileTransaction,
|
||||||
TransactionStates,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class MobileDeviceInfoSerializer(PassiveSerializer):
|
class MobileDeviceInfoSerializer(PassiveSerializer):
|
||||||
"""Info about a mobile device"""
|
"""Info about a mobile device"""
|
||||||
|
|
||||||
platform = ChoiceField(
|
platform = ChoiceField((("ios", "iOS"), ("android", "Android"), ("other", "Other")))
|
||||||
(
|
|
||||||
("ios", "iOS"),
|
|
||||||
("android", "Android"),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
os_version = CharField()
|
os_version = CharField()
|
||||||
model = CharField()
|
model = CharField()
|
||||||
hostname = CharField()
|
hostname = CharField()
|
||||||
|
@ -76,10 +70,7 @@ class MobileDeviceResponseSerializer(PassiveSerializer):
|
||||||
"""Response from push sent to phone"""
|
"""Response from push sent to phone"""
|
||||||
|
|
||||||
tx_id = UUIDField(required=True)
|
tx_id = UUIDField(required=True)
|
||||||
status = ChoiceField(
|
selected_item = CharField(required=True)
|
||||||
TransactionStates.choices,
|
|
||||||
required=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class MobileDeviceViewSet(
|
class MobileDeviceViewSet(
|
||||||
|
@ -214,7 +205,7 @@ class MobileDeviceViewSet(
|
||||||
transaction = MobileTransaction.objects.filter(tx_id=data.validated_data["tx_id"]).first()
|
transaction = MobileTransaction.objects.filter(tx_id=data.validated_data["tx_id"]).first()
|
||||||
if not transaction:
|
if not transaction:
|
||||||
raise Http404
|
raise Http404
|
||||||
transaction.status = data.validated_data["status"]
|
transaction.selected_item = data.validated_data["selected_item"]
|
||||||
transaction.save()
|
transaction.save()
|
||||||
return Response(status=204)
|
return Response(status=204)
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ class AuthenticatorMobileStageSerializer(StageSerializer):
|
||||||
fields = StageSerializer.Meta.fields + [
|
fields = StageSerializer.Meta.fields + [
|
||||||
"configure_flow",
|
"configure_flow",
|
||||||
"friendly_name",
|
"friendly_name",
|
||||||
|
"item_matching_mode",
|
||||||
"firebase_config",
|
"firebase_config",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 4.2.4 on 2023-09-04 13:21
|
# Generated by Django 4.2.7 on 2023-12-14 20:06
|
||||||
|
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
@ -14,8 +14,8 @@ class Migration(migrations.Migration):
|
||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
("authentik_flows", "0025_alter_flowstagebinding_evaluate_on_plan_and_more"),
|
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
("authentik_flows", "0027_auto_20231028_1424"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
@ -34,6 +34,18 @@ class Migration(migrations.Migration):
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
("friendly_name", models.TextField(null=True)),
|
("friendly_name", models.TextField(null=True)),
|
||||||
|
(
|
||||||
|
"item_matching_mode",
|
||||||
|
models.TextField(
|
||||||
|
choices=[
|
||||||
|
("accept_deny", "Accept Deny"),
|
||||||
|
("number_matching_2", "Number Matching 2"),
|
||||||
|
("number_matching_3", "Number Matching 3"),
|
||||||
|
],
|
||||||
|
default="number_matching_3",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("firebase_config", models.JSONField(default=dict, help_text="temp")),
|
||||||
(
|
(
|
||||||
"configure_flow",
|
"configure_flow",
|
||||||
models.ForeignKey(
|
models.ForeignKey(
|
||||||
|
@ -67,6 +79,8 @@ class Migration(migrations.Migration):
|
||||||
("uuid", models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
("uuid", models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
||||||
("device_id", models.TextField(unique=True)),
|
("device_id", models.TextField(unique=True)),
|
||||||
("firebase_token", models.TextField(blank=True)),
|
("firebase_token", models.TextField(blank=True)),
|
||||||
|
("state", models.JSONField(default=dict)),
|
||||||
|
("last_checkin", models.DateTimeField(auto_now=True)),
|
||||||
(
|
(
|
||||||
"stage",
|
"stage",
|
||||||
models.ForeignKey(
|
models.ForeignKey(
|
||||||
|
@ -86,6 +100,30 @@ class Migration(migrations.Migration):
|
||||||
"verbose_name_plural": "Mobile Devices",
|
"verbose_name_plural": "Mobile Devices",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="MobileTransaction",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"expires",
|
||||||
|
models.DateTimeField(default=authentik.core.models.default_token_duration),
|
||||||
|
),
|
||||||
|
("expiring", models.BooleanField(default=True)),
|
||||||
|
("tx_id", models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
||||||
|
("decision_items", models.JSONField(default=list)),
|
||||||
|
("correct_item", models.TextField()),
|
||||||
|
("selected_item", models.TextField(default=None, null=True)),
|
||||||
|
(
|
||||||
|
"device",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to="authentik_stages_authenticator_mobile.mobiledevice",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"abstract": False,
|
||||||
|
},
|
||||||
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name="MobileDeviceToken",
|
name="MobileDeviceToken",
|
||||||
fields=[
|
fields=[
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
# Generated by Django 4.2.4 on 2023-09-04 18:18
|
|
||||||
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
import authentik.core.models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
dependencies = [
|
|
||||||
("authentik_stages_authenticator_mobile", "0001_initial"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name="MobileTransaction",
|
|
||||||
fields=[
|
|
||||||
(
|
|
||||||
"expires",
|
|
||||||
models.DateTimeField(default=authentik.core.models.default_token_duration),
|
|
||||||
),
|
|
||||||
("expiring", models.BooleanField(default=True)),
|
|
||||||
("tx_id", models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
|
||||||
(
|
|
||||||
"device",
|
|
||||||
models.ForeignKey(
|
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
|
||||||
to="authentik_stages_authenticator_mobile.mobiledevice",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
"abstract": False,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,19 +0,0 @@
|
||||||
# Generated by Django 4.2.4 on 2023-09-04 18:28
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
dependencies = [
|
|
||||||
("authentik_stages_authenticator_mobile", "0002_mobiletransaction"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name="mobiletransaction",
|
|
||||||
name="status",
|
|
||||||
field=models.TextField(
|
|
||||||
choices=[("wait", "Wait"), ("accept", "Accept"), ("deny", "Deny")], default="wait"
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,22 +0,0 @@
|
||||||
# Generated by Django 4.2.4 on 2023-09-05 13:16
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
dependencies = [
|
|
||||||
("authentik_stages_authenticator_mobile", "0003_mobiletransaction_status"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name="mobiledevice",
|
|
||||||
name="last_checkin",
|
|
||||||
field=models.DateTimeField(auto_now=True),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name="mobiledevice",
|
|
||||||
name="state",
|
|
||||||
field=models.JSONField(default=dict),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,20 +0,0 @@
|
||||||
# Generated by Django 4.2.5 on 2023-09-21 15:27
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
dependencies = [
|
|
||||||
(
|
|
||||||
"authentik_stages_authenticator_mobile",
|
|
||||||
"0004_mobiledevice_last_checkin_mobiledevice_state",
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name="authenticatormobilestage",
|
|
||||||
name="firebase_config",
|
|
||||||
field=models.JSONField(default=dict, help_text="temp"),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -28,7 +28,7 @@ from structlog.stdlib import get_logger
|
||||||
from authentik.core.models import ExpiringModel, User
|
from authentik.core.models import ExpiringModel, User
|
||||||
from authentik.core.types import UserSettingSerializer
|
from authentik.core.types import UserSettingSerializer
|
||||||
from authentik.flows.models import ConfigurableStage, FriendlyNamedStage, Stage
|
from authentik.flows.models import ConfigurableStage, FriendlyNamedStage, Stage
|
||||||
from authentik.lib.generators import generate_id
|
from authentik.lib.generators import generate_code_fixed_length, generate_id
|
||||||
from authentik.lib.models import SerializerModel
|
from authentik.lib.models import SerializerModel
|
||||||
from authentik.stages.authenticator.models import Device
|
from authentik.stages.authenticator.models import Device
|
||||||
from authentik.tenants.utils import DEFAULT_TENANT
|
from authentik.tenants.utils import DEFAULT_TENANT
|
||||||
|
@ -41,11 +41,34 @@ def default_token_key():
|
||||||
return generate_id(40)
|
return generate_id(40)
|
||||||
|
|
||||||
|
|
||||||
|
class ItemMatchingMode(models.TextChoices):
|
||||||
|
"""Configure which items the app shows the user, and what the user must select"""
|
||||||
|
|
||||||
|
ACCEPT_DENY = "accept_deny"
|
||||||
|
NUMBER_MATCHING_2 = "number_matching_2"
|
||||||
|
NUMBER_MATCHING_3 = "number_matching_3"
|
||||||
|
|
||||||
|
|
||||||
class AuthenticatorMobileStage(ConfigurableStage, FriendlyNamedStage, Stage):
|
class AuthenticatorMobileStage(ConfigurableStage, FriendlyNamedStage, Stage):
|
||||||
"""Setup Mobile authenticator devices"""
|
"""Setup Mobile authenticator devices"""
|
||||||
|
|
||||||
|
item_matching_mode = models.TextField(
|
||||||
|
choices=ItemMatchingMode.choices, default=ItemMatchingMode.NUMBER_MATCHING_3
|
||||||
|
)
|
||||||
firebase_config = models.JSONField(default=dict, help_text="temp")
|
firebase_config = models.JSONField(default=dict, help_text="temp")
|
||||||
|
|
||||||
|
def create_transaction(self, device: "MobileDevice") -> "MobileTransaction":
|
||||||
|
"""Create a transaction for `device` with the config of this stage."""
|
||||||
|
transaction = MobileTransaction(device=device)
|
||||||
|
if self.item_matching_mode == ItemMatchingMode.ACCEPT_DENY:
|
||||||
|
transaction.item_matching = [TransactionStates.ACCEPT, TransactionStates.DENY]
|
||||||
|
if self.item_matching_mode == ItemMatchingMode.NUMBER_MATCHING_2:
|
||||||
|
transaction.item_matching = [generate_code_fixed_length(2)] * 3
|
||||||
|
if self.item_matching_mode == ItemMatchingMode.NUMBER_MATCHING_3:
|
||||||
|
transaction.item_matching = [generate_code_fixed_length(3)] * 3
|
||||||
|
transaction.save()
|
||||||
|
return transaction
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def serializer(self) -> type[BaseSerializer]:
|
def serializer(self) -> type[BaseSerializer]:
|
||||||
from authentik.stages.authenticator_mobile.api.stage import (
|
from authentik.stages.authenticator_mobile.api.stage import (
|
||||||
|
@ -96,6 +119,11 @@ class MobileDevice(SerializerModel, Device):
|
||||||
state = models.JSONField(default=dict)
|
state = models.JSONField(default=dict)
|
||||||
last_checkin = models.DateTimeField(auto_now=True)
|
last_checkin = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
def create_transaction(self) -> "MobileTransaction":
|
||||||
|
"""Create a transaction for this device with the config of its stage."""
|
||||||
|
stage: AuthenticatorMobileStage = self.stage
|
||||||
|
return stage.create_transaction(self)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def serializer(self) -> Serializer:
|
def serializer(self) -> Serializer:
|
||||||
from authentik.stages.authenticator_mobile.api.device import MobileDeviceSerializer
|
from authentik.stages.authenticator_mobile.api.device import MobileDeviceSerializer
|
||||||
|
@ -123,8 +151,18 @@ class MobileTransaction(ExpiringModel):
|
||||||
|
|
||||||
tx_id = models.UUIDField(default=uuid4, primary_key=True)
|
tx_id = models.UUIDField(default=uuid4, primary_key=True)
|
||||||
device = models.ForeignKey(MobileDevice, on_delete=models.CASCADE)
|
device = models.ForeignKey(MobileDevice, on_delete=models.CASCADE)
|
||||||
|
decision_items = models.JSONField(default=list)
|
||||||
|
correct_item = models.TextField()
|
||||||
|
selected_item = models.TextField(default=None, null=True)
|
||||||
|
|
||||||
status = models.TextField(choices=TransactionStates.choices, default=TransactionStates.WAIT)
|
@property
|
||||||
|
def status(self) -> TransactionStates:
|
||||||
|
"""Get the status"""
|
||||||
|
if not self.selected_item:
|
||||||
|
return TransactionStates.WAIT
|
||||||
|
if self.selected_item != self.correct_item:
|
||||||
|
return TransactionStates.DENY
|
||||||
|
return TransactionStates.ACCEPT
|
||||||
|
|
||||||
def send_message(self, request: Optional[HttpRequest], **context):
|
def send_message(self, request: Optional[HttpRequest], **context):
|
||||||
"""Send mobile message"""
|
"""Send mobile message"""
|
||||||
|
@ -153,7 +191,7 @@ class MobileTransaction(ExpiringModel):
|
||||||
notification=AndroidNotification(icon="stock_ticker_update", color="#f45342"),
|
notification=AndroidNotification(icon="stock_ticker_update", color="#f45342"),
|
||||||
data={
|
data={
|
||||||
"tx_id": str(self.tx_id),
|
"tx_id": str(self.tx_id),
|
||||||
"numbers": dumps([123, 456, 789]),
|
"user_decision_items": dumps(self.item_matching),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
apns=APNSConfig(
|
apns=APNSConfig(
|
||||||
|
@ -167,12 +205,7 @@ class MobileTransaction(ExpiringModel):
|
||||||
),
|
),
|
||||||
interruption_level="time-sensitive",
|
interruption_level="time-sensitive",
|
||||||
tx_id=str(self.tx_id),
|
tx_id=str(self.tx_id),
|
||||||
numbers=[
|
user_decision_items=self.item_matching,
|
||||||
123,
|
|
||||||
456,
|
|
||||||
789,
|
|
||||||
],
|
|
||||||
options=["foo", "bar", "baz"],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
token=self.device.firebase_token,
|
token=self.device.firebase_token,
|
||||||
|
|
|
@ -26,11 +26,7 @@ from authentik.root.middleware import ClientIPMiddleware
|
||||||
from authentik.stages.authenticator import match_token
|
from authentik.stages.authenticator import match_token
|
||||||
from authentik.stages.authenticator.models import Device
|
from authentik.stages.authenticator.models import Device
|
||||||
from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice
|
from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice
|
||||||
from authentik.stages.authenticator_mobile.models import (
|
from authentik.stages.authenticator_mobile.models import MobileDevice, TransactionStates
|
||||||
MobileDevice,
|
|
||||||
MobileTransaction,
|
|
||||||
TransactionStates,
|
|
||||||
)
|
|
||||||
from authentik.stages.authenticator_sms.models import SMSDevice
|
from authentik.stages.authenticator_sms.models import SMSDevice
|
||||||
from authentik.stages.authenticator_validate.models import AuthenticatorValidateStage, DeviceClasses
|
from authentik.stages.authenticator_validate.models import AuthenticatorValidateStage, DeviceClasses
|
||||||
from authentik.stages.authenticator_webauthn.models import UserVerification, WebAuthnDevice
|
from authentik.stages.authenticator_webauthn.models import UserVerification, WebAuthnDevice
|
||||||
|
@ -198,7 +194,7 @@ def validate_challenge_mobile(device_pk: str, stage_view: StageView, user: User)
|
||||||
).name
|
).name
|
||||||
|
|
||||||
try:
|
try:
|
||||||
transaction = MobileTransaction.objects.create(device=device)
|
transaction = device.create_transaction()
|
||||||
transaction.send_message(stage_view.request, **push_context)
|
transaction.send_message(stage_view.request, **push_context)
|
||||||
status = transaction.wait_for_response()
|
status = transaction.wait_for_response()
|
||||||
if status == TransactionStates.DENY:
|
if status == TransactionStates.DENY:
|
||||||
|
|
|
@ -6144,6 +6144,15 @@
|
||||||
"minLength": 1,
|
"minLength": 1,
|
||||||
"title": "Friendly name"
|
"title": "Friendly name"
|
||||||
},
|
},
|
||||||
|
"item_matching_mode": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"accept_deny",
|
||||||
|
"number_matching_2",
|
||||||
|
"number_matching_3"
|
||||||
|
],
|
||||||
|
"title": "Item matching mode"
|
||||||
|
},
|
||||||
"firebase_config": {
|
"firebase_config": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": true,
|
"additionalProperties": true,
|
||||||
|
|
37
schema.yml
37
schema.yml
|
@ -30425,6 +30425,8 @@ components:
|
||||||
friendly_name:
|
friendly_name:
|
||||||
type: string
|
type: string
|
||||||
nullable: true
|
nullable: true
|
||||||
|
item_matching_mode:
|
||||||
|
$ref: '#/components/schemas/ItemMatchingModeEnum'
|
||||||
firebase_config:
|
firebase_config:
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: {}
|
additionalProperties: {}
|
||||||
|
@ -30457,6 +30459,8 @@ components:
|
||||||
type: string
|
type: string
|
||||||
nullable: true
|
nullable: true
|
||||||
minLength: 1
|
minLength: 1
|
||||||
|
item_matching_mode:
|
||||||
|
$ref: '#/components/schemas/ItemMatchingModeEnum'
|
||||||
firebase_config:
|
firebase_config:
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: {}
|
additionalProperties: {}
|
||||||
|
@ -34420,6 +34424,16 @@ components:
|
||||||
description: |-
|
description: |-
|
||||||
* `global` - Same identifier is used for all providers
|
* `global` - Same identifier is used for all providers
|
||||||
* `per_provider` - Each provider has a different issuer, based on the application slug.
|
* `per_provider` - Each provider has a different issuer, based on the application slug.
|
||||||
|
ItemMatchingModeEnum:
|
||||||
|
enum:
|
||||||
|
- accept_deny
|
||||||
|
- number_matching_2
|
||||||
|
- number_matching_3
|
||||||
|
type: string
|
||||||
|
description: |-
|
||||||
|
* `accept_deny` - Accept Deny
|
||||||
|
* `number_matching_2` - Number Matching 2
|
||||||
|
* `number_matching_3` - Number Matching 3
|
||||||
KubernetesServiceConnection:
|
KubernetesServiceConnection:
|
||||||
type: object
|
type: object
|
||||||
description: KubernetesServiceConnection Serializer
|
description: KubernetesServiceConnection Serializer
|
||||||
|
@ -35426,21 +35440,12 @@ components:
|
||||||
tx_id:
|
tx_id:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
status:
|
selected_item:
|
||||||
$ref: '#/components/schemas/MobileDeviceResponseStatusEnum'
|
|
||||||
required:
|
|
||||||
- status
|
|
||||||
- tx_id
|
|
||||||
MobileDeviceResponseStatusEnum:
|
|
||||||
enum:
|
|
||||||
- wait
|
|
||||||
- accept
|
|
||||||
- deny
|
|
||||||
type: string
|
type: string
|
||||||
description: |-
|
minLength: 1
|
||||||
* `wait` - Wait
|
required:
|
||||||
* `accept` - Accept
|
- selected_item
|
||||||
* `deny` - Deny
|
- tx_id
|
||||||
MobileDeviceSetPushKeyRequest:
|
MobileDeviceSetPushKeyRequest:
|
||||||
type: object
|
type: object
|
||||||
description: Set notification key
|
description: Set notification key
|
||||||
|
@ -38224,6 +38229,8 @@ components:
|
||||||
type: string
|
type: string
|
||||||
nullable: true
|
nullable: true
|
||||||
minLength: 1
|
minLength: 1
|
||||||
|
item_matching_mode:
|
||||||
|
$ref: '#/components/schemas/ItemMatchingModeEnum'
|
||||||
firebase_config:
|
firebase_config:
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: {}
|
additionalProperties: {}
|
||||||
|
@ -40679,10 +40686,12 @@ components:
|
||||||
enum:
|
enum:
|
||||||
- ios
|
- ios
|
||||||
- android
|
- android
|
||||||
|
- other
|
||||||
type: string
|
type: string
|
||||||
description: |-
|
description: |-
|
||||||
* `ios` - iOS
|
* `ios` - iOS
|
||||||
* `android` - Android
|
* `android` - Android
|
||||||
|
* `other` - Other
|
||||||
PlexAuthenticationChallenge:
|
PlexAuthenticationChallenge:
|
||||||
type: object
|
type: object
|
||||||
description: Challenge shown to the user in identification stage
|
description: Challenge shown to the user in identification stage
|
||||||
|
|
|
@ -505,17 +505,9 @@ components:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
status:
|
status:
|
||||||
$ref: '#/components/schemas/MobileDeviceEnrollmentStatusStatusEnum'
|
$ref: '#/components/schemas/StatusEnum'
|
||||||
required:
|
required:
|
||||||
- status
|
- status
|
||||||
MobileDeviceEnrollmentStatusStatusEnum:
|
|
||||||
enum:
|
|
||||||
- success
|
|
||||||
- waiting
|
|
||||||
type: string
|
|
||||||
description: |-
|
|
||||||
* `success` - Success
|
|
||||||
* `waiting` - Waiting
|
|
||||||
MobileDeviceInfo:
|
MobileDeviceInfo:
|
||||||
type: object
|
type: object
|
||||||
description: Info about a mobile device
|
description: Info about a mobile device
|
||||||
|
@ -593,21 +585,12 @@ components:
|
||||||
tx_id:
|
tx_id:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
status:
|
selected_item:
|
||||||
$ref: '#/components/schemas/MobileDeviceResponseStatusEnum'
|
|
||||||
required:
|
|
||||||
- status
|
|
||||||
- tx_id
|
|
||||||
MobileDeviceResponseStatusEnum:
|
|
||||||
enum:
|
|
||||||
- wait
|
|
||||||
- accept
|
|
||||||
- deny
|
|
||||||
type: string
|
type: string
|
||||||
description: |-
|
minLength: 1
|
||||||
* `wait` - Wait
|
required:
|
||||||
* `accept` - Accept
|
- selected_item
|
||||||
* `deny` - Deny
|
- tx_id
|
||||||
MobileDeviceSetPushKeyRequest:
|
MobileDeviceSetPushKeyRequest:
|
||||||
type: object
|
type: object
|
||||||
description: Set notification key
|
description: Set notification key
|
||||||
|
@ -674,10 +657,20 @@ components:
|
||||||
enum:
|
enum:
|
||||||
- ios
|
- ios
|
||||||
- android
|
- android
|
||||||
|
- other
|
||||||
type: string
|
type: string
|
||||||
description: |-
|
description: |-
|
||||||
* `ios` - iOS
|
* `ios` - iOS
|
||||||
* `android` - Android
|
* `android` - Android
|
||||||
|
* `other` - Other
|
||||||
|
StatusEnum:
|
||||||
|
enum:
|
||||||
|
- success
|
||||||
|
- waiting
|
||||||
|
type: string
|
||||||
|
description: |-
|
||||||
|
* `success` - Success
|
||||||
|
* `waiting` - Waiting
|
||||||
UsedBy:
|
UsedBy:
|
||||||
type: object
|
type: object
|
||||||
description: A list of all objects referencing the queried object
|
description: A list of all objects referencing the queried object
|
||||||
|
|
|
@ -17,6 +17,7 @@ import {
|
||||||
FlowsApi,
|
FlowsApi,
|
||||||
FlowsInstancesListDesignationEnum,
|
FlowsInstancesListDesignationEnum,
|
||||||
FlowsInstancesListRequest,
|
FlowsInstancesListRequest,
|
||||||
|
ItemMatchingModeEnum,
|
||||||
StagesApi,
|
StagesApi,
|
||||||
} from "@goauthentik/api";
|
} from "@goauthentik/api";
|
||||||
|
|
||||||
|
@ -83,6 +84,31 @@ export class AuthenticatorMobileStageForm extends ModelForm<AuthenticatorMobileS
|
||||||
<ak-form-group .expanded=${true}>
|
<ak-form-group .expanded=${true}>
|
||||||
<span slot="header"> ${msg("Stage-specific settings")} </span>
|
<span slot="header"> ${msg("Stage-specific settings")} </span>
|
||||||
<div slot="body" class="pf-c-form">
|
<div slot="body" class="pf-c-form">
|
||||||
|
<ak-form-element-horizontal
|
||||||
|
label=${msg("User decision mode")}
|
||||||
|
?required=${true}
|
||||||
|
name="itemMatchingMode"
|
||||||
|
>
|
||||||
|
<ak-radio
|
||||||
|
.options=${[
|
||||||
|
{
|
||||||
|
label: msg("Accept/Deny"),
|
||||||
|
value: ItemMatchingModeEnum.AcceptDeny,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: msg("Number matching (2 digit numbers)"),
|
||||||
|
value: ItemMatchingModeEnum.NumberMatching2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: msg("Number matching (3 digit numbers)"),
|
||||||
|
value: ItemMatchingModeEnum.NumberMatching3,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
.value=${this.instance?.itemMatchingMode}
|
||||||
|
>
|
||||||
|
</ak-radio>
|
||||||
|
</ak-form-element-horizontal>
|
||||||
<ak-form-element-horizontal
|
<ak-form-element-horizontal
|
||||||
label=${msg("Firebase config")}
|
label=${msg("Firebase config")}
|
||||||
?required=${false}
|
?required=${false}
|
||||||
|
|
Reference in a new issue