stages/authenticator_validate: remember (#2828)
* initial Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web: cleanup timedelta help Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * add tooltip Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * add tests Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * assert response code in self.assertStageResponse Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * add more tests, add duo Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * add docs Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * fix Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
4d755dc0f6
commit
fd1d38f844
30
Makefile
30
Makefile
|
@ -99,11 +99,18 @@ migrate:
|
|||
run:
|
||||
go run -v cmd/server/main.go
|
||||
|
||||
web-watch:
|
||||
cd web && npm run watch
|
||||
#########################
|
||||
## Web
|
||||
#########################
|
||||
|
||||
web: web-lint-fix web-lint web-extract
|
||||
|
||||
web-install:
|
||||
cd web && npm ci
|
||||
|
||||
web-watch:
|
||||
cd web && npm run watch
|
||||
|
||||
web-lint-fix:
|
||||
cd web && npm run prettier
|
||||
|
||||
|
@ -114,6 +121,21 @@ web-lint:
|
|||
web-extract:
|
||||
cd web && npm run extract
|
||||
|
||||
#########################
|
||||
## Website
|
||||
#########################
|
||||
|
||||
website: website-lint-fix
|
||||
|
||||
website-install:
|
||||
cd website && npm ci
|
||||
|
||||
website-lint-fix:
|
||||
cd website && npm run prettier
|
||||
|
||||
website-watch:
|
||||
cd website && npm run watch
|
||||
|
||||
# These targets are use by GitHub actions to allow usage of matrix
|
||||
# which makes the YAML File a lot smaller
|
||||
|
||||
|
@ -139,10 +161,8 @@ ci-pyright: ci--meta-debug
|
|||
ci-pending-migrations: ci--meta-debug
|
||||
./manage.py makemigrations --check
|
||||
|
||||
install:
|
||||
install: web-install website-install
|
||||
poetry install
|
||||
cd web && npm ci
|
||||
cd website && npm ci
|
||||
|
||||
a: install
|
||||
tmux \
|
||||
|
|
|
@ -12,3 +12,7 @@ class FlowNonApplicableException(SentryIgnoredException):
|
|||
|
||||
class EmptyFlowException(SentryIgnoredException):
|
||||
"""Flow has no stages."""
|
||||
|
||||
|
||||
class FlowSkipStageException(SentryIgnoredException):
|
||||
"""Exception to skip a stage"""
|
||||
|
|
|
@ -23,6 +23,7 @@ class FlowTestCase(APITestCase):
|
|||
**kwargs,
|
||||
) -> dict[str, Any]:
|
||||
"""Assert various attributes of a stage response"""
|
||||
self.assertEqual(response.status_code, 200)
|
||||
raw_response = loads(response.content.decode())
|
||||
self.assertIsNotNone(raw_response["component"])
|
||||
self.assertIsNotNone(raw_response["type"])
|
||||
|
|
|
@ -87,7 +87,6 @@ class TestFlowExecutor(FlowTestCase):
|
|||
response = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageResponse(
|
||||
response,
|
||||
flow=flow,
|
||||
|
@ -406,7 +405,6 @@ class TestFlowExecutor(FlowTestCase):
|
|||
# A get request will evaluate the policies and this will return stage 4
|
||||
# but it won't save it, hence we can't check the plan
|
||||
response = self.client.get(exec_url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageResponse(response, flow, component="ak-stage-dummy")
|
||||
|
||||
# fourth request, this confirms the last stage (dummy4)
|
||||
|
@ -479,7 +477,6 @@ class TestFlowExecutor(FlowTestCase):
|
|||
exec_url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug})
|
||||
# First request, run the planner
|
||||
response = self.client.get(exec_url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageResponse(
|
||||
response,
|
||||
flow,
|
||||
|
@ -491,5 +488,4 @@ class TestFlowExecutor(FlowTestCase):
|
|||
user_fields=[UserFields.E_MAIL],
|
||||
)
|
||||
response = self.client.post(exec_url, {"uid_field": "invalid-string"}, follow=True)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageResponse(response, flow, component="ak-stage-access-denied")
|
||||
|
|
|
@ -50,7 +50,6 @@ class TestPasswordPolicyFlow(FlowTestCase):
|
|||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
{"password": "akadmin"},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageResponse(
|
||||
response,
|
||||
self.flow,
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 4.0.4 on 2022-05-10 17:52
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_stages_authenticator_duo", "0002_default_setup_flow"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="duodevice",
|
||||
name="last_t",
|
||||
field=models.DateTimeField(auto_now=True),
|
||||
),
|
||||
]
|
|
@ -74,6 +74,8 @@ class DuoDevice(Device):
|
|||
stage = models.ForeignKey(AuthenticatorDuoStage, on_delete=models.CASCADE)
|
||||
duo_user_id = models.TextField()
|
||||
|
||||
last_t = models.DateTimeField(auto_now=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name or str(self.user)
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
"""Duo stage"""
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.utils.timezone import now
|
||||
from rest_framework.fields import CharField
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
|
@ -85,7 +86,11 @@ class AuthenticatorDuoStageView(ChallengeStageView):
|
|||
self.request.session.pop(SESSION_KEY_DUO_ACTIVATION_CODE)
|
||||
if not existing_device:
|
||||
DuoDevice.objects.create(
|
||||
name="Duo Device", user=self.get_pending_user(), duo_user_id=user_id, stage=stage
|
||||
name="Duo Device",
|
||||
user=self.get_pending_user(),
|
||||
duo_user_id=user_id,
|
||||
stage=stage,
|
||||
last_t=now(),
|
||||
)
|
||||
else:
|
||||
return self.executor.stage_invalid("Device with Credential ID already exists.")
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 4.0.4 on 2022-04-14 20:54
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_stages_authenticator_sms", "0002_alter_authenticatorsmsstage_from_number"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="smsdevice",
|
||||
name="last_t",
|
||||
field=models.DateTimeField(auto_now=True),
|
||||
),
|
||||
]
|
|
@ -168,6 +168,14 @@ class SMSDevice(SideChannelDevice):
|
|||
|
||||
phone_number = models.TextField()
|
||||
|
||||
last_t = models.DateTimeField(auto_now=True)
|
||||
|
||||
def verify_token(self, token):
|
||||
valid = super().verify_token(token)
|
||||
if valid:
|
||||
self.save()
|
||||
return valid
|
||||
|
||||
def __str__(self):
|
||||
return self.name or str(self.user)
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ class AuthenticatorValidateStageSerializer(StageSerializer):
|
|||
"not_configured_action",
|
||||
"device_classes",
|
||||
"configuration_stages",
|
||||
"last_auth_threshold",
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -147,4 +147,5 @@ def validate_challenge_duo(device_pk: int, request: HttpRequest, user: User) ->
|
|||
# {'result': 'allow', 'status': 'allow', 'status_msg': 'Success. Logging you in...'}
|
||||
if response["result"] == "deny":
|
||||
raise ValidationError("Duo denied access")
|
||||
device.save()
|
||||
return device_pk
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
# Generated by Django 4.0.4 on 2022-04-14 20:54
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
import authentik.lib.utils.time
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
(
|
||||
"authentik_stages_authenticator_validate",
|
||||
"0010_remove_authenticatorvalidatestage_configuration_stage_and_more",
|
||||
),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="authenticatorvalidatestage",
|
||||
name="last_auth_threshold",
|
||||
field=models.TextField(
|
||||
default="seconds=0",
|
||||
help_text="If any of the user's device has been used within this threshold, this stage will be skipped",
|
||||
validators=[authentik.lib.utils.time.timedelta_string_validator],
|
||||
),
|
||||
),
|
||||
]
|
|
@ -7,6 +7,7 @@ from django.views import View
|
|||
from rest_framework.serializers import BaseSerializer
|
||||
|
||||
from authentik.flows.models import NotConfiguredAction, Stage
|
||||
from authentik.lib.utils.time import timedelta_string_validator
|
||||
|
||||
|
||||
class DeviceClasses(models.TextChoices):
|
||||
|
@ -57,6 +58,17 @@ class AuthenticatorValidateStage(Stage):
|
|||
default=default_device_classes,
|
||||
)
|
||||
|
||||
last_auth_threshold = models.TextField(
|
||||
default="seconds=0",
|
||||
validators=[timedelta_string_validator],
|
||||
help_text=_(
|
||||
(
|
||||
"If any of the user's device has been used within this threshold, this "
|
||||
"stage will be skipped"
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
@property
|
||||
def serializer(self) -> BaseSerializer:
|
||||
from authentik.stages.authenticator_validate.api import AuthenticatorValidateStageSerializer
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
"""Authenticator Validation"""
|
||||
from datetime import timezone
|
||||
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.utils.timezone import datetime, now
|
||||
from django_otp import devices_for_user
|
||||
from django_otp.models import Device
|
||||
from rest_framework.fields import CharField, IntegerField, JSONField, ListField, UUIDField
|
||||
from rest_framework.serializers import ValidationError
|
||||
from structlog.stdlib import get_logger
|
||||
|
@ -10,9 +14,11 @@ 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
|
||||
from authentik.flows.exceptions import FlowSkipStageException
|
||||
from authentik.flows.models import FlowDesignation, NotConfiguredAction, Stage
|
||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
||||
from authentik.flows.stage import ChallengeStageView
|
||||
from authentik.lib.utils.time import timedelta_from_string
|
||||
from authentik.stages.authenticator_sms.models import SMSDevice
|
||||
from authentik.stages.authenticator_validate.challenge import (
|
||||
DeviceChallenge,
|
||||
|
@ -121,6 +127,15 @@ class AuthenticatorValidationChallengeResponse(ChallengeResponse):
|
|||
return attrs
|
||||
|
||||
|
||||
def get_device_last_usage(device: Device) -> datetime:
|
||||
"""Get a datetime object from last_t"""
|
||||
if not hasattr(device, "last_t"):
|
||||
return datetime.fromtimestamp(0, tz=timezone.utc)
|
||||
if isinstance(device.last_t, datetime):
|
||||
return device.last_t
|
||||
return datetime.fromtimestamp(device.last_t * device.step, tz=timezone.utc)
|
||||
|
||||
|
||||
class AuthenticatorValidateStageView(ChallengeStageView):
|
||||
"""Authenticator Validation"""
|
||||
|
||||
|
@ -139,6 +154,9 @@ class AuthenticatorValidateStageView(ChallengeStageView):
|
|||
|
||||
stage: AuthenticatorValidateStage = self.executor.current_stage
|
||||
|
||||
_now = now()
|
||||
threshold = timedelta_from_string(stage.last_auth_threshold)
|
||||
|
||||
for device in user_devices:
|
||||
device_class = device.__class__.__name__.lower().replace("device", "")
|
||||
if device_class not in stage.device_classes:
|
||||
|
@ -148,6 +166,16 @@ class AuthenticatorValidateStageView(ChallengeStageView):
|
|||
# WebAuthn does another device loop to find all webuahtn devices
|
||||
if device_class in seen_classes:
|
||||
continue
|
||||
# check if device has been used within threshold and skip this stage if so
|
||||
if threshold.total_seconds() > 0:
|
||||
print("yeet")
|
||||
print(get_device_last_usage(device))
|
||||
print(_now - get_device_last_usage(device))
|
||||
print(threshold)
|
||||
print(_now - get_device_last_usage(device) <= threshold)
|
||||
if _now - get_device_last_usage(device) <= threshold:
|
||||
LOGGER.info("Device has been used within threshold", device=device)
|
||||
raise FlowSkipStageException()
|
||||
if device_class not in seen_classes:
|
||||
seen_classes.append(device_class)
|
||||
challenge = DeviceChallenge(
|
||||
|
@ -181,7 +209,10 @@ class AuthenticatorValidateStageView(ChallengeStageView):
|
|||
user = self.get_pending_user()
|
||||
stage: AuthenticatorValidateStage = self.executor.current_stage
|
||||
if user and not user.is_anonymous:
|
||||
try:
|
||||
challenges = self.get_device_challenges()
|
||||
except FlowSkipStageException:
|
||||
return self.executor.stage_ok()
|
||||
else:
|
||||
if self.executor.flow.designation != FlowDesignation.AUTHENTICATION:
|
||||
LOGGER.debug("Refusing passwordless flow in non-authentication flow")
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
"""Test validator stage"""
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from django.test.client import RequestFactory
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
from authentik.core.tests.utils import create_test_admin_user
|
||||
from authentik.flows.tests import FlowTestCase
|
||||
from authentik.lib.generators import generate_id, generate_key
|
||||
from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice
|
||||
from authentik.stages.authenticator_validate.challenge import validate_challenge_duo
|
||||
|
||||
|
||||
class AuthenticatorValidateStageDuoTests(FlowTestCase):
|
||||
"""Test validator stage"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.user = create_test_admin_user()
|
||||
self.request_factory = RequestFactory()
|
||||
|
||||
def test_device_challenge_duo(self):
|
||||
"""Test duo"""
|
||||
request = self.request_factory.get("/")
|
||||
stage = AuthenticatorDuoStage.objects.create(
|
||||
name="test",
|
||||
client_id=generate_id(),
|
||||
client_secret=generate_key(),
|
||||
api_hostname="",
|
||||
)
|
||||
duo_device = DuoDevice.objects.create(
|
||||
user=self.user,
|
||||
stage=stage,
|
||||
)
|
||||
duo_mock = MagicMock(
|
||||
auth=MagicMock(
|
||||
return_value={
|
||||
"result": "allow",
|
||||
"status": "allow",
|
||||
"status_msg": "Success. Logging you in...",
|
||||
}
|
||||
)
|
||||
)
|
||||
failed_duo_mock = MagicMock(auth=MagicMock(return_value={"result": "deny"}))
|
||||
with patch(
|
||||
"authentik.stages.authenticator_duo.models.AuthenticatorDuoStage.client",
|
||||
duo_mock,
|
||||
):
|
||||
self.assertEqual(
|
||||
duo_device.pk, validate_challenge_duo(duo_device.pk, request, self.user)
|
||||
)
|
||||
with patch(
|
||||
"authentik.stages.authenticator_duo.models.AuthenticatorDuoStage.client",
|
||||
failed_duo_mock,
|
||||
):
|
||||
with self.assertRaises(ValidationError):
|
||||
validate_challenge_duo(duo_device.pk, request, self.user)
|
|
@ -0,0 +1,106 @@
|
|||
"""Test validator stage"""
|
||||
from time import sleep
|
||||
|
||||
from django.test.client import RequestFactory
|
||||
from django.urls.base import reverse
|
||||
|
||||
from authentik.core.tests.utils import create_test_admin_user
|
||||
from authentik.flows.models import Flow, FlowStageBinding, NotConfiguredAction
|
||||
from authentik.flows.tests import FlowTestCase
|
||||
from authentik.stages.authenticator_sms.models import AuthenticatorSMSStage, SMSDevice, SMSProviders
|
||||
from authentik.stages.authenticator_validate.models import AuthenticatorValidateStage, DeviceClasses
|
||||
from authentik.stages.identification.models import IdentificationStage, UserFields
|
||||
|
||||
|
||||
class AuthenticatorValidateStageSMSTests(FlowTestCase):
|
||||
"""Test validator stage"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.user = create_test_admin_user()
|
||||
self.request_factory = RequestFactory()
|
||||
self.stage = AuthenticatorSMSStage.objects.create(
|
||||
name="sms",
|
||||
provider=SMSProviders.GENERIC,
|
||||
from_number="1234",
|
||||
)
|
||||
|
||||
def test_last_auth_threshold(self):
|
||||
"""Test last_auth_threshold"""
|
||||
conf_stage = IdentificationStage.objects.create(
|
||||
name="conf",
|
||||
user_fields=[
|
||||
UserFields.USERNAME,
|
||||
],
|
||||
)
|
||||
device: SMSDevice = SMSDevice.objects.create(
|
||||
user=self.user,
|
||||
confirmed=True,
|
||||
stage=self.stage,
|
||||
)
|
||||
# Verify token once here to set last_t etc
|
||||
token = device.generate_token()
|
||||
device.verify_token(token)
|
||||
stage = AuthenticatorValidateStage.objects.create(
|
||||
name="foo",
|
||||
last_auth_threshold="milliseconds=0",
|
||||
not_configured_action=NotConfiguredAction.CONFIGURE,
|
||||
device_classes=[DeviceClasses.SMS],
|
||||
)
|
||||
sleep(1)
|
||||
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)
|
||||
|
||||
response = self.client.post(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||
{"uid_field": self.user.username},
|
||||
)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
response = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||
follow=True,
|
||||
)
|
||||
self.assertStageResponse(
|
||||
response,
|
||||
flow,
|
||||
component="ak-stage-authenticator-validate",
|
||||
)
|
||||
|
||||
def test_last_auth_threshold_valid(self):
|
||||
"""Test last_auth_threshold"""
|
||||
conf_stage = IdentificationStage.objects.create(
|
||||
name="conf",
|
||||
user_fields=[
|
||||
UserFields.USERNAME,
|
||||
],
|
||||
)
|
||||
device: SMSDevice = SMSDevice.objects.create(
|
||||
user=self.user,
|
||||
confirmed=True,
|
||||
stage=self.stage,
|
||||
)
|
||||
# Verify token once here to set last_t etc
|
||||
token = device.generate_token()
|
||||
device.verify_token(token)
|
||||
stage = AuthenticatorValidateStage.objects.create(
|
||||
name="foo",
|
||||
last_auth_threshold="hours=1",
|
||||
not_configured_action=NotConfiguredAction.CONFIGURE,
|
||||
device_classes=[DeviceClasses.SMS],
|
||||
)
|
||||
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)
|
||||
|
||||
response = self.client.post(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||
{"uid_field": self.user.username},
|
||||
)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
response = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||
follow=True,
|
||||
)
|
||||
self.assertStageResponse(response, component="xak-flow-redirect", to="/")
|
|
@ -1,34 +1,21 @@
|
|||
"""Test validator stage"""
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from django.contrib.sessions.middleware import SessionMiddleware
|
||||
from django.test.client import RequestFactory
|
||||
from django.urls.base import reverse
|
||||
from django_otp.plugins.otp_totp.models import TOTPDevice
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from webauthn.helpers import bytes_to_base64url
|
||||
|
||||
from authentik.core.tests.utils import create_test_admin_user
|
||||
from authentik.flows.models import Flow, FlowStageBinding, NotConfiguredAction
|
||||
from authentik.flows.stage import StageView
|
||||
from authentik.flows.tests import FlowTestCase
|
||||
from authentik.flows.views.executor import FlowExecutorView
|
||||
from authentik.lib.generators import generate_id, generate_key
|
||||
from authentik.lib.tests.utils import dummy_get_response, get_request
|
||||
from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice
|
||||
from authentik.lib.tests.utils import dummy_get_response
|
||||
from authentik.stages.authenticator_validate.api import AuthenticatorValidateStageSerializer
|
||||
from authentik.stages.authenticator_validate.challenge import (
|
||||
get_challenge_for_device,
|
||||
validate_challenge_code,
|
||||
validate_challenge_duo,
|
||||
validate_challenge_webauthn,
|
||||
)
|
||||
from authentik.stages.authenticator_validate.models import AuthenticatorValidateStage
|
||||
from authentik.stages.authenticator_validate.stage import (
|
||||
SESSION_DEVICE_CHALLENGES,
|
||||
AuthenticatorValidationChallengeResponse,
|
||||
)
|
||||
from authentik.stages.authenticator_webauthn.models import WebAuthnDevice
|
||||
from authentik.stages.identification.models import IdentificationStage, UserFields
|
||||
|
||||
|
||||
|
@ -65,7 +52,6 @@ class AuthenticatorValidateStageTests(FlowTestCase):
|
|||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||
follow=True,
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageResponse(
|
||||
response,
|
||||
flow,
|
||||
|
@ -90,83 +76,6 @@ class AuthenticatorValidateStageTests(FlowTestCase):
|
|||
)
|
||||
self.assertTrue(serializer.is_valid())
|
||||
|
||||
def test_device_challenge_totp(self):
|
||||
"""Test device challenge"""
|
||||
request = self.request_factory.get("/")
|
||||
totp_device = TOTPDevice.objects.create(user=self.user, confirmed=True, digits=6)
|
||||
self.assertEqual(get_challenge_for_device(request, totp_device), {})
|
||||
with self.assertRaises(ValidationError):
|
||||
validate_challenge_code("1234", request, self.user)
|
||||
|
||||
def test_device_challenge_webauthn(self):
|
||||
"""Test webauthn"""
|
||||
request = get_request("/")
|
||||
request.user = self.user
|
||||
|
||||
webauthn_device = WebAuthnDevice.objects.create(
|
||||
user=self.user,
|
||||
public_key=bytes_to_base64url(b"qwerqwerqre"),
|
||||
credential_id=bytes_to_base64url(b"foobarbaz"),
|
||||
sign_count=0,
|
||||
rp_id="foo",
|
||||
)
|
||||
challenge = get_challenge_for_device(request, webauthn_device)
|
||||
del challenge["challenge"]
|
||||
self.assertEqual(
|
||||
challenge,
|
||||
{
|
||||
"allowCredentials": [
|
||||
{
|
||||
"id": "Zm9vYmFyYmF6",
|
||||
"type": "public-key",
|
||||
}
|
||||
],
|
||||
"rpId": "testserver",
|
||||
"timeout": 60000,
|
||||
"userVerification": "preferred",
|
||||
},
|
||||
)
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
validate_challenge_webauthn({}, request, self.user)
|
||||
|
||||
def test_device_challenge_duo(self):
|
||||
"""Test duo"""
|
||||
request = self.request_factory.get("/")
|
||||
stage = AuthenticatorDuoStage.objects.create(
|
||||
name="test",
|
||||
client_id=generate_id(),
|
||||
client_secret=generate_key(),
|
||||
api_hostname="",
|
||||
)
|
||||
duo_device = DuoDevice.objects.create(
|
||||
user=self.user,
|
||||
stage=stage,
|
||||
)
|
||||
duo_mock = MagicMock(
|
||||
auth=MagicMock(
|
||||
return_value={
|
||||
"result": "allow",
|
||||
"status": "allow",
|
||||
"status_msg": "Success. Logging you in...",
|
||||
}
|
||||
)
|
||||
)
|
||||
failed_duo_mock = MagicMock(auth=MagicMock(return_value={"result": "deny"}))
|
||||
with patch(
|
||||
"authentik.stages.authenticator_duo.models.AuthenticatorDuoStage.client",
|
||||
duo_mock,
|
||||
):
|
||||
self.assertEqual(
|
||||
duo_device.pk, validate_challenge_duo(duo_device.pk, request, self.user)
|
||||
)
|
||||
with patch(
|
||||
"authentik.stages.authenticator_duo.models.AuthenticatorDuoStage.client",
|
||||
failed_duo_mock,
|
||||
):
|
||||
with self.assertRaises(ValidationError):
|
||||
validate_challenge_duo(duo_device.pk, request, self.user)
|
||||
|
||||
def test_validate_selected_challenge(self):
|
||||
"""Test validate_selected_challenge"""
|
||||
# Prepare request with session
|
|
@ -0,0 +1,114 @@
|
|||
"""Test validator stage"""
|
||||
from time import sleep
|
||||
|
||||
from django.test.client import RequestFactory
|
||||
from django.urls.base import reverse
|
||||
from django_otp.oath import TOTP
|
||||
from django_otp.plugins.otp_totp.models import TOTPDevice
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
from authentik.core.tests.utils import create_test_admin_user
|
||||
from authentik.flows.models import Flow, FlowStageBinding, NotConfiguredAction
|
||||
from authentik.flows.tests import FlowTestCase
|
||||
from authentik.stages.authenticator_validate.challenge import (
|
||||
get_challenge_for_device,
|
||||
validate_challenge_code,
|
||||
)
|
||||
from authentik.stages.authenticator_validate.models import AuthenticatorValidateStage, DeviceClasses
|
||||
from authentik.stages.identification.models import IdentificationStage, UserFields
|
||||
|
||||
|
||||
class AuthenticatorValidateStageTOTPTests(FlowTestCase):
|
||||
"""Test validator stage"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.user = create_test_admin_user()
|
||||
self.request_factory = RequestFactory()
|
||||
|
||||
def test_last_auth_threshold(self):
|
||||
"""Test last_auth_threshold"""
|
||||
conf_stage = IdentificationStage.objects.create(
|
||||
name="conf",
|
||||
user_fields=[
|
||||
UserFields.USERNAME,
|
||||
],
|
||||
)
|
||||
device: TOTPDevice = TOTPDevice.objects.create(
|
||||
user=self.user,
|
||||
confirmed=True,
|
||||
)
|
||||
# Verify token once here to set last_t etc
|
||||
totp = TOTP(device.bin_key)
|
||||
sleep(1)
|
||||
self.assertTrue(device.verify_token(totp.token()))
|
||||
stage = AuthenticatorValidateStage.objects.create(
|
||||
name="foo",
|
||||
last_auth_threshold="milliseconds=0",
|
||||
not_configured_action=NotConfiguredAction.CONFIGURE,
|
||||
device_classes=[DeviceClasses.TOTP],
|
||||
)
|
||||
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)
|
||||
|
||||
response = self.client.post(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||
{"uid_field": self.user.username},
|
||||
)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
response = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||
follow=True,
|
||||
)
|
||||
self.assertStageResponse(
|
||||
response,
|
||||
flow,
|
||||
component="ak-stage-authenticator-validate",
|
||||
)
|
||||
|
||||
def test_last_auth_threshold_valid(self):
|
||||
"""Test last_auth_threshold"""
|
||||
conf_stage = IdentificationStage.objects.create(
|
||||
name="conf",
|
||||
user_fields=[
|
||||
UserFields.USERNAME,
|
||||
],
|
||||
)
|
||||
device: TOTPDevice = TOTPDevice.objects.create(
|
||||
user=self.user,
|
||||
confirmed=True,
|
||||
)
|
||||
# Verify token once here to set last_t etc
|
||||
totp = TOTP(device.bin_key)
|
||||
sleep(1)
|
||||
self.assertTrue(device.verify_token(totp.token()))
|
||||
stage = AuthenticatorValidateStage.objects.create(
|
||||
name="foo",
|
||||
last_auth_threshold="hours=1",
|
||||
not_configured_action=NotConfiguredAction.CONFIGURE,
|
||||
device_classes=[DeviceClasses.TOTP],
|
||||
)
|
||||
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)
|
||||
|
||||
response = self.client.post(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||
{"uid_field": self.user.username},
|
||||
)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
response = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||
follow=True,
|
||||
)
|
||||
self.assertStageResponse(response, component="xak-flow-redirect", to="/")
|
||||
|
||||
def test_device_challenge_totp(self):
|
||||
"""Test device challenge"""
|
||||
request = self.request_factory.get("/")
|
||||
totp_device = TOTPDevice.objects.create(user=self.user, confirmed=True, digits=6)
|
||||
self.assertEqual(get_challenge_for_device(request, totp_device), {})
|
||||
with self.assertRaises(ValidationError):
|
||||
validate_challenge_code("1234", request, self.user)
|
|
@ -0,0 +1,134 @@
|
|||
"""Test validator stage"""
|
||||
from time import sleep
|
||||
|
||||
from django.test.client import RequestFactory
|
||||
from django.urls.base import reverse
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from webauthn.helpers import bytes_to_base64url
|
||||
|
||||
from authentik.core.tests.utils import create_test_admin_user
|
||||
from authentik.flows.models import Flow, FlowStageBinding, NotConfiguredAction
|
||||
from authentik.flows.tests import FlowTestCase
|
||||
from authentik.lib.tests.utils import get_request
|
||||
from authentik.stages.authenticator_validate.challenge import (
|
||||
get_challenge_for_device,
|
||||
validate_challenge_webauthn,
|
||||
)
|
||||
from authentik.stages.authenticator_validate.models import AuthenticatorValidateStage, DeviceClasses
|
||||
from authentik.stages.authenticator_webauthn.models import WebAuthnDevice
|
||||
from authentik.stages.identification.models import IdentificationStage, UserFields
|
||||
|
||||
|
||||
class AuthenticatorValidateStageWebAuthnTests(FlowTestCase):
|
||||
"""Test validator stage"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.user = create_test_admin_user()
|
||||
self.request_factory = RequestFactory()
|
||||
|
||||
def test_last_auth_threshold(self):
|
||||
"""Test last_auth_threshold"""
|
||||
conf_stage = IdentificationStage.objects.create(
|
||||
name="conf",
|
||||
user_fields=[
|
||||
UserFields.USERNAME,
|
||||
],
|
||||
)
|
||||
device: WebAuthnDevice = WebAuthnDevice.objects.create(
|
||||
user=self.user,
|
||||
confirmed=True,
|
||||
)
|
||||
device.set_sign_count(device.sign_count + 1)
|
||||
stage = AuthenticatorValidateStage.objects.create(
|
||||
name="foo",
|
||||
last_auth_threshold="milliseconds=0",
|
||||
not_configured_action=NotConfiguredAction.CONFIGURE,
|
||||
device_classes=[DeviceClasses.WEBAUTHN],
|
||||
)
|
||||
sleep(1)
|
||||
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)
|
||||
|
||||
response = self.client.post(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||
{"uid_field": self.user.username},
|
||||
)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
response = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||
follow=True,
|
||||
)
|
||||
self.assertStageResponse(
|
||||
response,
|
||||
flow,
|
||||
component="ak-stage-authenticator-validate",
|
||||
)
|
||||
|
||||
def test_last_auth_threshold_valid(self):
|
||||
"""Test last_auth_threshold"""
|
||||
conf_stage = IdentificationStage.objects.create(
|
||||
name="conf",
|
||||
user_fields=[
|
||||
UserFields.USERNAME,
|
||||
],
|
||||
)
|
||||
device: WebAuthnDevice = WebAuthnDevice.objects.create(
|
||||
user=self.user,
|
||||
confirmed=True,
|
||||
)
|
||||
device.set_sign_count(device.sign_count + 1)
|
||||
stage = AuthenticatorValidateStage.objects.create(
|
||||
name="foo",
|
||||
last_auth_threshold="hours=1",
|
||||
not_configured_action=NotConfiguredAction.CONFIGURE,
|
||||
device_classes=[DeviceClasses.WEBAUTHN],
|
||||
)
|
||||
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)
|
||||
|
||||
response = self.client.post(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||
{"uid_field": self.user.username},
|
||||
)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
response = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||
follow=True,
|
||||
)
|
||||
self.assertStageResponse(response, component="xak-flow-redirect", to="/")
|
||||
|
||||
def test_device_challenge_webauthn(self):
|
||||
"""Test webauthn"""
|
||||
request = get_request("/")
|
||||
request.user = self.user
|
||||
|
||||
webauthn_device = WebAuthnDevice.objects.create(
|
||||
user=self.user,
|
||||
public_key=bytes_to_base64url(b"qwerqwerqre"),
|
||||
credential_id=bytes_to_base64url(b"foobarbaz"),
|
||||
sign_count=0,
|
||||
rp_id="foo",
|
||||
)
|
||||
challenge = get_challenge_for_device(request, webauthn_device)
|
||||
del challenge["challenge"]
|
||||
self.assertEqual(
|
||||
challenge,
|
||||
{
|
||||
"allowCredentials": [
|
||||
{
|
||||
"id": "Zm9vYmFyYmF6",
|
||||
"type": "public-key",
|
||||
}
|
||||
],
|
||||
"rpId": "testserver",
|
||||
"timeout": 60000,
|
||||
"userVerification": "preferred",
|
||||
},
|
||||
)
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
validate_challenge_webauthn({}, request, self.user)
|
|
@ -0,0 +1,21 @@
|
|||
# Generated by Django 4.0.4 on 2022-04-14 20:58
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
(
|
||||
"authentik_stages_authenticator_webauthn",
|
||||
"0006_authenticatewebauthnstage_authenticator_attachment_and_more",
|
||||
),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name="webauthndevice",
|
||||
old_name="last_used_on",
|
||||
new_name="last_t",
|
||||
),
|
||||
]
|
|
@ -125,7 +125,7 @@ class WebAuthnDevice(Device):
|
|||
rp_id = models.CharField(max_length=253)
|
||||
|
||||
created_on = models.DateTimeField(auto_now_add=True)
|
||||
last_used_on = models.DateTimeField(default=now)
|
||||
last_t = models.DateTimeField(default=now)
|
||||
|
||||
@property
|
||||
def descriptor(self) -> PublicKeyCredentialDescriptor:
|
||||
|
@ -133,9 +133,9 @@ class WebAuthnDevice(Device):
|
|||
return PublicKeyCredentialDescriptor(id=base64url_to_bytes(self.credential_id))
|
||||
|
||||
def set_sign_count(self, sign_count: int) -> None:
|
||||
"""Set the sign_count and update the last_used_on datetime."""
|
||||
"""Set the sign_count and update the last_t datetime."""
|
||||
self.sign_count = sign_count
|
||||
self.last_used_on = now()
|
||||
self.last_t = now()
|
||||
self.save()
|
||||
|
||||
def __str__(self):
|
||||
|
|
|
@ -36,7 +36,6 @@ class TestUserDenyStage(FlowTestCase):
|
|||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageResponse(response, self.flow, component="ak-stage-access-denied")
|
||||
|
||||
def test_valid_post(self):
|
||||
|
@ -50,5 +49,4 @@ class TestUserDenyStage(FlowTestCase):
|
|||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageResponse(response, self.flow, component="ak-stage-access-denied")
|
||||
|
|
|
@ -79,7 +79,6 @@ class TestIdentificationStage(FlowTestCase):
|
|||
}
|
||||
url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
||||
response = self.client.post(url, form_data)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageResponse(
|
||||
response,
|
||||
self.flow,
|
||||
|
@ -122,7 +121,6 @@ class TestIdentificationStage(FlowTestCase):
|
|||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
form_data,
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageResponse(
|
||||
response,
|
||||
self.flow,
|
||||
|
@ -175,7 +173,6 @@ class TestIdentificationStage(FlowTestCase):
|
|||
response = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageResponse(
|
||||
response,
|
||||
self.flow,
|
||||
|
@ -218,7 +215,6 @@ class TestIdentificationStage(FlowTestCase):
|
|||
response = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageResponse(
|
||||
response,
|
||||
self.flow,
|
||||
|
|
|
@ -55,7 +55,6 @@ class TestUserLoginStage(FlowTestCase):
|
|||
response = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageResponse(
|
||||
response,
|
||||
flow=self.flow,
|
||||
|
|
|
@ -53,7 +53,6 @@ class TestPasswordStage(FlowTestCase):
|
|||
{"password": self.password},
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageResponse(
|
||||
response,
|
||||
self.flow,
|
||||
|
@ -159,7 +158,6 @@ class TestPasswordStage(FlowTestCase):
|
|||
{"password": self.password + "test"},
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageResponse(
|
||||
response,
|
||||
self.flow,
|
||||
|
|
|
@ -43,7 +43,6 @@ class TestUserDeleteStage(FlowTestCase):
|
|||
response = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageResponse(response, self.flow, component="ak-stage-access-denied")
|
||||
|
||||
def test_user_delete_get(self):
|
||||
|
|
|
@ -94,7 +94,6 @@ class TestUserLoginStage(FlowTestCase):
|
|||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageResponse(
|
||||
response,
|
||||
self.flow,
|
||||
|
|
|
@ -112,7 +112,6 @@ class TestUserWriteStage(FlowTestCase):
|
|||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageResponse(
|
||||
response,
|
||||
self.flow,
|
||||
|
@ -139,7 +138,6 @@ class TestUserWriteStage(FlowTestCase):
|
|||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageResponse(
|
||||
response,
|
||||
self.flow,
|
||||
|
@ -167,7 +165,6 @@ class TestUserWriteStage(FlowTestCase):
|
|||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageResponse(
|
||||
response,
|
||||
self.flow,
|
||||
|
|
14
schema.yml
14
schema.yml
|
@ -19894,6 +19894,10 @@ components:
|
|||
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.
|
||||
last_auth_threshold:
|
||||
type: string
|
||||
description: If any of the user's device has been used within this threshold,
|
||||
this stage will be skipped
|
||||
required:
|
||||
- component
|
||||
- meta_model_name
|
||||
|
@ -19927,6 +19931,11 @@ components:
|
|||
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.
|
||||
last_auth_threshold:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: If any of the user's device has been used within this threshold,
|
||||
this stage will be skipped
|
||||
required:
|
||||
- name
|
||||
AuthenticatorValidationChallenge:
|
||||
|
@ -26797,6 +26806,11 @@ components:
|
|||
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.
|
||||
last_auth_threshold:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: If any of the user's device has been used within this threshold,
|
||||
this stage will be skipped
|
||||
PatchedCaptchaStageRequest:
|
||||
type: object
|
||||
description: CaptchaStage Serializer
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
import { CSSResult, LitElement, TemplateResult, css, html } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
|
||||
import AKGlobal from "../authentik.css";
|
||||
import PFTooltip from "@patternfly/patternfly/components/Tooltip/Tooltip.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
@customElement("ak-tooltip")
|
||||
export class Tooltip extends LitElement {
|
||||
@state()
|
||||
open = false;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
PFBase,
|
||||
PFTooltip,
|
||||
AKGlobal,
|
||||
css`
|
||||
.pf-c-tooltip__content {
|
||||
text-align: inherit;
|
||||
}
|
||||
.outer {
|
||||
position: relative;
|
||||
}
|
||||
.pf-c-tooltip {
|
||||
position: absolute;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<slot
|
||||
@mouseenter=${() => {
|
||||
this.open = true;
|
||||
}}
|
||||
@mouseleave=${() => {
|
||||
this.open = false;
|
||||
}}
|
||||
name="trigger"
|
||||
></slot>
|
||||
${this.open
|
||||
? html`<div class="outer">
|
||||
<div class="pf-c-tooltip" role="tooltip">
|
||||
<div class="pf-c-tooltip__arrow"></div>
|
||||
|
||||
<div class="pf-c-tooltip__content">
|
||||
<slot name="tooltip"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
: html``}`;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
import { t } from "@lingui/macro";
|
||||
|
||||
import { CSSResult, LitElement, TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import AKGlobal from "../../authentik.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/Form.css";
|
||||
import PFList from "@patternfly/patternfly/components/List/list.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
import "../Tooltip";
|
||||
|
||||
@customElement("ak-utils-time-delta-help")
|
||||
export class TimeDeltaHelp extends LitElement {
|
||||
@property({ type: Boolean })
|
||||
negative = false;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFForm, PFList, AKGlobal];
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html` <ak-tooltip>
|
||||
<p class="pf-c-form__helper-text" slot="trigger">
|
||||
${this.negative
|
||||
? t`(Format: hours=-1;minutes=-2;seconds=-3).`
|
||||
: t`(Format: hours=1;minutes=2;seconds=3).`}
|
||||
<i class="pf-icon fa fa-question-circle" aria-hidden="true"></i>
|
||||
</p>
|
||||
|
||||
<div slot="tooltip">
|
||||
${t`The following keywords are supported:`}
|
||||
<ul class="pf-c-list">
|
||||
<li><pre>microseconds</pre></li>
|
||||
<li><pre>milliseconds</pre></li>
|
||||
<li><pre>seconds</pre></li>
|
||||
<li><pre>minutes</pre></li>
|
||||
<li><pre>hours</pre></li>
|
||||
<li><pre>days</pre></li>
|
||||
<li><pre>weeks</pre></li>
|
||||
</ul>
|
||||
</div>
|
||||
</ak-tooltip>`;
|
||||
}
|
||||
}
|
|
@ -33,14 +33,15 @@ msgstr ""
|
|||
#~ msgid "#/identity/users/{0}"
|
||||
#~ msgstr "#/Identität/Benutzer/{0}"
|
||||
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
#: src/pages/providers/proxy/ProxyProviderForm.ts
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
#~ msgid "(Format: days=-1;minutes=-2;seconds=-3)."
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "(Format: hours=-1;minutes=-2;seconds=-3)."
|
||||
msgstr "(Format: hours=-1;minutes=-2;seconds=-3)."
|
||||
|
||||
#: src/pages/stages/user_login/UserLoginStageForm.ts
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "(Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "(Format: hours=-1;minutes=-2;seconds=-3)."
|
||||
|
||||
|
@ -458,8 +459,12 @@ msgid "Are you sure you want to update {0} \"{1}\"?"
|
|||
msgstr "Sind Sie sicher, dass Sie {0} \"{1}\" aktualisieren wollen?"
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "SAML Assertion nicht gültig am oder nach der aktuellen Uhrzeit + diesem Wert (Format: Stunden=1;Minuten=2;Sekunden=3)."
|
||||
#~ msgid "Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "SAML Assertion nicht gültig am oder nach der aktuellen Uhrzeit + diesem Wert (Format: Stunden=1;Minuten=2;Sekunden=3)."
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Assertion not valid on or after current time + this value."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Assertion valid not before"
|
||||
|
@ -2496,6 +2501,10 @@ msgstr "Kennung"
|
|||
#~ msgid "Identity & Cryptography"
|
||||
#~ msgstr "Identität & Kryptographie"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "If any of the devices user of the types selected above have been used within this duration, this stage will be skipped."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/outposts/ServiceConnectionDockerForm.ts
|
||||
#: src/pages/outposts/ServiceConnectionKubernetesForm.ts
|
||||
msgid "If enabled, use the local connection. Required Docker socket/Kubernetes Integration."
|
||||
|
@ -2769,6 +2778,10 @@ msgstr "Überprüft: {0}"
|
|||
msgid "Last sync: {0}"
|
||||
msgstr "Letzte Synchronisierung: {0}"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Last validation threshold"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/applications/ApplicationViewPage.ts
|
||||
#: src/pages/applications/ApplicationViewPage.ts
|
||||
msgid "Launch"
|
||||
|
@ -3490,8 +3503,12 @@ msgid "Objects created"
|
|||
msgstr "Objekte erstellt"
|
||||
|
||||
#: src/pages/stages/consent/ConsentStageForm.ts
|
||||
msgid "Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "Die Einwilligung erlischt in. (Format: Stunden=1;Minuten=2;Sekunden=3)."
|
||||
msgid "Offset after which consent expires."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/stages/consent/ConsentStageForm.ts
|
||||
#~ msgid "Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "Die Einwilligung erlischt in. (Format: Stunden=1;Minuten=2;Sekunden=3)."
|
||||
|
||||
#: src/elements/events/ObjectChangelog.ts
|
||||
#: src/elements/events/UserEvents.ts
|
||||
|
@ -4522,8 +4539,12 @@ msgid "Session duration"
|
|||
msgstr "Sessionsdauer"
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "Session am oder nach der aktuellen Uhrzeit + diesem Wert nicht gültig (Format: Stunden=1;Minuten=2;Sekunden=3)."
|
||||
#~ msgid "Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "Session am oder nach der aktuellen Uhrzeit + diesem Wert nicht gültig (Format: Stunden=1;Minuten=2;Sekunden=3)."
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Session not valid on or after current time + this value."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Session valid not on or after"
|
||||
|
@ -5331,6 +5352,10 @@ msgstr "Die externe URL, unter der Sie auf die Anwendung zugreifen. Schließen S
|
|||
msgid "The external URL you'll authenticate at. The authentik core server should be reachable under this URL."
|
||||
msgstr "Die externe URL, bei der Sie sich authentifizieren. Unter dieser URL sollte der Authentik Core Server erreichbar sein."
|
||||
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "The following keywords are supported:"
|
||||
msgstr ""
|
||||
|
||||
#~ msgid "The following objects use {0}:"
|
||||
#~ msgstr "Die folgenden Objekte verwenden {0}:"
|
||||
|
||||
|
@ -5440,8 +5465,12 @@ msgid "Time in minutes the token sent is valid."
|
|||
msgstr "Zeit in Minuten wie lange der verschickte Token gültig ist"
|
||||
|
||||
#: src/pages/sources/saml/SAMLSourceForm.ts
|
||||
msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "Zeitversatz, wann temporäre Benutzer gelöscht werden sollen. Dies gilt nur, wenn Ihr IDP das NameID-Format „transient“ verwendet und der Benutzer sich nicht manuell abmeldet. (Format: Stunden=1;Minuten=2;Sekunden=3)."
|
||||
msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/sources/saml/SAMLSourceForm.ts
|
||||
#~ msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "Zeitversatz, wann temporäre Benutzer gelöscht werden sollen. Dies gilt nur, wenn Ihr IDP das NameID-Format „transient“ verwendet und der Benutzer sich nicht manuell abmeldet. (Format: Stunden=1;Minuten=2;Sekunden=3)."
|
||||
|
||||
#~ msgid "Time-based One-Time Passwords"
|
||||
#~ msgstr "Zeitbasierte Einmalpasswörter"
|
||||
|
|
|
@ -17,14 +17,15 @@ msgstr ""
|
|||
#~ msgid "#/identity/users/{0}"
|
||||
#~ msgstr "#/identity/users/{0}"
|
||||
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
#: src/pages/providers/proxy/ProxyProviderForm.ts
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
#~ msgid "(Format: days=-1;minutes=-2;seconds=-3)."
|
||||
#~ msgstr "(Format: days=-1;minutes=-2;seconds=-3)."
|
||||
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "(Format: hours=-1;minutes=-2;seconds=-3)."
|
||||
msgstr "(Format: hours=-1;minutes=-2;seconds=-3)."
|
||||
|
||||
#: src/pages/stages/user_login/UserLoginStageForm.ts
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "(Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "(Format: hours=1;minutes=2;seconds=3)."
|
||||
|
||||
|
@ -448,8 +449,12 @@ msgid "Are you sure you want to update {0} \"{1}\"?"
|
|||
msgstr "Are you sure you want to update {0} \"{1}\"?"
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgid "Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Assertion not valid on or after current time + this value."
|
||||
msgstr "Assertion not valid on or after current time + this value."
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Assertion valid not before"
|
||||
|
@ -2538,6 +2543,10 @@ msgstr "Identifier"
|
|||
#~ msgid "Identity & Cryptography"
|
||||
#~ msgstr "Identity & Cryptography"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "If any of the devices user of the types selected above have been used within this duration, this stage will be skipped."
|
||||
msgstr "If any of the devices user of the types selected above have been used within this duration, this stage will be skipped."
|
||||
|
||||
#: src/pages/outposts/ServiceConnectionDockerForm.ts
|
||||
#: src/pages/outposts/ServiceConnectionKubernetesForm.ts
|
||||
msgid "If enabled, use the local connection. Required Docker socket/Kubernetes Integration."
|
||||
|
@ -2820,6 +2829,10 @@ msgstr "Last seen: {0}"
|
|||
msgid "Last sync: {0}"
|
||||
msgstr "Last sync: {0}"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Last validation threshold"
|
||||
msgstr "Last validation threshold"
|
||||
|
||||
#: src/pages/applications/ApplicationViewPage.ts
|
||||
#: src/pages/applications/ApplicationViewPage.ts
|
||||
msgid "Launch"
|
||||
|
@ -3548,8 +3561,12 @@ msgid "Objects created"
|
|||
msgstr "Objects created"
|
||||
|
||||
#: src/pages/stages/consent/ConsentStageForm.ts
|
||||
msgid "Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgid "Offset after which consent expires."
|
||||
msgstr "Offset after which consent expires."
|
||||
|
||||
#: src/pages/stages/consent/ConsentStageForm.ts
|
||||
#~ msgid "Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
|
||||
|
||||
#: src/elements/events/ObjectChangelog.ts
|
||||
#: src/elements/events/UserEvents.ts
|
||||
|
@ -4610,8 +4627,12 @@ msgid "Session duration"
|
|||
msgstr "Session duration"
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgid "Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Session not valid on or after current time + this value."
|
||||
msgstr "Session not valid on or after current time + this value."
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Session valid not on or after"
|
||||
|
@ -5445,6 +5466,10 @@ msgstr "The external URL you'll access the application at. Include any non-stand
|
|||
msgid "The external URL you'll authenticate at. The authentik core server should be reachable under this URL."
|
||||
msgstr "The external URL you'll authenticate at. The authentik core server should be reachable under this URL."
|
||||
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "The following keywords are supported:"
|
||||
msgstr "The following keywords are supported:"
|
||||
|
||||
#:
|
||||
#~ msgid "The following objects use {0}:"
|
||||
#~ msgstr "The following objects use {0}:"
|
||||
|
@ -5556,8 +5581,12 @@ msgid "Time in minutes the token sent is valid."
|
|||
msgstr "Time in minutes the token sent is valid."
|
||||
|
||||
#: src/pages/sources/saml/SAMLSourceForm.ts
|
||||
msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually."
|
||||
msgstr "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually."
|
||||
|
||||
#: src/pages/sources/saml/SAMLSourceForm.ts
|
||||
#~ msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3)."
|
||||
|
||||
#:
|
||||
#~ msgid "Time-based One-Time Passwords"
|
||||
|
|
|
@ -20,14 +20,15 @@ msgstr ""
|
|||
#~ msgid "#/identity/users/{0}"
|
||||
#~ msgstr "#/identidad/usuarios/ {0}"
|
||||
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
#: src/pages/providers/proxy/ProxyProviderForm.ts
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
#~ msgid "(Format: days=-1;minutes=-2;seconds=-3)."
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "(Format: hours=-1;minutes=-2;seconds=-3)."
|
||||
msgstr "(Formato: horas = -1; minutos = -2; segundos = -3)."
|
||||
|
||||
#: src/pages/stages/user_login/UserLoginStageForm.ts
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "(Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "(Formato: horas = 1; minutos = 2; segundos = 3)."
|
||||
|
||||
|
@ -445,8 +446,12 @@ msgid "Are you sure you want to update {0} \"{1}\"?"
|
|||
msgstr "¿Seguro que quieres actualizar {0} «{1}»?"
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "La afirmación no es válida en o después de la hora actual + este valor (Formato: horas = 1; minutos = 2; segundos = 3)."
|
||||
#~ msgid "Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "La afirmación no es válida en o después de la hora actual + este valor (Formato: horas = 1; minutos = 2; segundos = 3)."
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Assertion not valid on or after current time + this value."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Assertion valid not before"
|
||||
|
@ -2487,6 +2492,10 @@ msgstr "Identificador"
|
|||
#~ msgid "Identity & Cryptography"
|
||||
#~ msgstr "Identidad y criptografía"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "If any of the devices user of the types selected above have been used within this duration, this stage will be skipped."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/outposts/ServiceConnectionDockerForm.ts
|
||||
#: src/pages/outposts/ServiceConnectionKubernetesForm.ts
|
||||
msgid "If enabled, use the local connection. Required Docker socket/Kubernetes Integration."
|
||||
|
@ -2762,6 +2771,10 @@ msgstr "Visto por última vez: {0}"
|
|||
msgid "Last sync: {0}"
|
||||
msgstr "Última sincronización: {0}"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Last validation threshold"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/applications/ApplicationViewPage.ts
|
||||
#: src/pages/applications/ApplicationViewPage.ts
|
||||
msgid "Launch"
|
||||
|
@ -3483,8 +3496,12 @@ msgid "Objects created"
|
|||
msgstr "Objetos creados"
|
||||
|
||||
#: src/pages/stages/consent/ConsentStageForm.ts
|
||||
msgid "Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "Compensación después de la cual caduca el consentimiento. (Formato: horas = 1; minutos = 2; segundos = 3)."
|
||||
msgid "Offset after which consent expires."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/stages/consent/ConsentStageForm.ts
|
||||
#~ msgid "Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "Compensación después de la cual caduca el consentimiento. (Formato: horas = 1; minutos = 2; segundos = 3)."
|
||||
|
||||
#: src/elements/events/ObjectChangelog.ts
|
||||
#: src/elements/events/UserEvents.ts
|
||||
|
@ -4515,8 +4532,12 @@ msgid "Session duration"
|
|||
msgstr "Duración de la sesión"
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "La sesión no es válida a partir de la hora actual + este valor (Formato: horas=1; minutos=2; segundos=3)."
|
||||
#~ msgid "Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "La sesión no es válida a partir de la hora actual + este valor (Formato: horas=1; minutos=2; segundos=3)."
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Session not valid on or after current time + this value."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Session valid not on or after"
|
||||
|
@ -5325,6 +5346,10 @@ msgstr "La URL externa en la que accederás a la aplicación. Incluya cualquier
|
|||
msgid "The external URL you'll authenticate at. The authentik core server should be reachable under this URL."
|
||||
msgstr "La URL externa en la que te autenticarás. Se debe poder acceder al servidor principal de authentik en esta URL."
|
||||
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "The following keywords are supported:"
|
||||
msgstr ""
|
||||
|
||||
#~ msgid "The following objects use {0}:"
|
||||
#~ msgstr "Los siguientes objetos usan {0}:"
|
||||
|
||||
|
@ -5434,8 +5459,12 @@ msgid "Time in minutes the token sent is valid."
|
|||
msgstr "El tiempo en minutos que se envía el token es válido."
|
||||
|
||||
#: src/pages/sources/saml/SAMLSourceForm.ts
|
||||
msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "Desplazamiento temporal en el que se deben eliminar los usuarios temporales. Esto solo se aplica si su IDP utiliza el formato NameID «transitorio» y el usuario no cierra sesión manualmente. (Formato: horas = 1; minutos = 2; segundos = 3)."
|
||||
msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/sources/saml/SAMLSourceForm.ts
|
||||
#~ msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "Desplazamiento temporal en el que se deben eliminar los usuarios temporales. Esto solo se aplica si su IDP utiliza el formato NameID «transitorio» y el usuario no cierra sesión manualmente. (Formato: horas = 1; minutos = 2; segundos = 3)."
|
||||
|
||||
#~ msgid "Time-based One-Time Passwords"
|
||||
#~ msgstr "Contraseñas únicas basadas en tiempo"
|
||||
|
|
|
@ -23,14 +23,15 @@ msgstr ""
|
|||
#~ msgid "#/identity/users/{0}"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
#: src/pages/providers/proxy/ProxyProviderForm.ts
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
#~ msgid "(Format: days=-1;minutes=-2;seconds=-3)."
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "(Format: hours=-1;minutes=-2;seconds=-3)."
|
||||
msgstr "(Format : heures=-1;minutes=-2;seconds=-3)"
|
||||
|
||||
#: src/pages/stages/user_login/UserLoginStageForm.ts
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "(Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr ""
|
||||
|
||||
|
@ -449,8 +450,12 @@ msgid "Are you sure you want to update {0} \"{1}\"?"
|
|||
msgstr "Êtes-vous sûr de vouloir modifier {0} {1} ?"
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "Assertion non valide après écoulement de ce délai (format : hours=1;minutes=2;seconds=3)"
|
||||
#~ msgid "Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "Assertion non valide après écoulement de ce délai (format : hours=1;minutes=2;seconds=3)"
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Assertion not valid on or after current time + this value."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Assertion valid not before"
|
||||
|
@ -2516,6 +2521,10 @@ msgstr "Identifiant"
|
|||
#~ msgid "Identity & Cryptography"
|
||||
#~ msgstr "Identité et chiffrement"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "If any of the devices user of the types selected above have been used within this duration, this stage will be skipped."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/outposts/ServiceConnectionDockerForm.ts
|
||||
#: src/pages/outposts/ServiceConnectionKubernetesForm.ts
|
||||
msgid "If enabled, use the local connection. Required Docker socket/Kubernetes Integration."
|
||||
|
@ -2793,6 +2802,10 @@ msgstr "Vu le : {0}"
|
|||
msgid "Last sync: {0}"
|
||||
msgstr "Dernière synchro : {0}"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Last validation threshold"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/applications/ApplicationViewPage.ts
|
||||
#: src/pages/applications/ApplicationViewPage.ts
|
||||
msgid "Launch"
|
||||
|
@ -3517,8 +3530,12 @@ msgid "Objects created"
|
|||
msgstr ""
|
||||
|
||||
#: src/pages/stages/consent/ConsentStageForm.ts
|
||||
msgid "Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "Durée d'expiration du consentement (Format : hours=1;minutes=2;seconds=3)."
|
||||
msgid "Offset after which consent expires."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/stages/consent/ConsentStageForm.ts
|
||||
#~ msgid "Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "Durée d'expiration du consentement (Format : hours=1;minutes=2;seconds=3)."
|
||||
|
||||
#: src/elements/events/ObjectChangelog.ts
|
||||
#: src/elements/events/UserEvents.ts
|
||||
|
@ -4564,8 +4581,12 @@ msgid "Session duration"
|
|||
msgstr "Durée de la session"
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "La session n'est plus valide à partir de l'heure actuelle + cette valeur (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgid "Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "La session n'est plus valide à partir de l'heure actuelle + cette valeur (Format: hours=1;minutes=2;seconds=3)."
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Session not valid on or after current time + this value."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Session valid not on or after"
|
||||
|
@ -5390,6 +5411,10 @@ msgstr "L'URL externe par laquelle vous accéderez à l'application. Incluez un
|
|||
msgid "The external URL you'll authenticate at. The authentik core server should be reachable under this URL."
|
||||
msgstr ""
|
||||
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "The following keywords are supported:"
|
||||
msgstr ""
|
||||
|
||||
#~ msgid "The following objects use {0}:"
|
||||
#~ msgstr "Les objets suivants utilisent {0} :"
|
||||
|
||||
|
@ -5490,8 +5515,12 @@ msgid "Time in minutes the token sent is valid."
|
|||
msgstr "Temps en minutes durant lequel le jeton envoyé est valide."
|
||||
|
||||
#: src/pages/sources/saml/SAMLSourceForm.ts
|
||||
msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "Délai de suppression des utilisateurs temporaires. Ceci s'applique uniquement si votre fournisseur d'identité utilise le format NameID transitoire ('transient') et que l'utilisateur ne se déconnecte pas manuellement. (Format : heures=1;minutes=2;secondes=3)."
|
||||
msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/sources/saml/SAMLSourceForm.ts
|
||||
#~ msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "Délai de suppression des utilisateurs temporaires. Ceci s'applique uniquement si votre fournisseur d'identité utilise le format NameID transitoire ('transient') et que l'utilisateur ne se déconnecte pas manuellement. (Format : heures=1;minutes=2;secondes=3)."
|
||||
|
||||
#:
|
||||
#~ msgid "Time-based One-Time Passwords"
|
||||
|
|
|
@ -20,14 +20,15 @@ msgstr ""
|
|||
#~ msgid "#/identity/users/{0}"
|
||||
#~ msgstr "#/identity/users/{0}"
|
||||
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
#: src/pages/providers/proxy/ProxyProviderForm.ts
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
#~ msgid "(Format: days=-1;minutes=-2;seconds=-3)."
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "(Format: hours=-1;minutes=-2;seconds=-3)."
|
||||
msgstr "(Format: hours=-1;minutes=-2;seconds=-3)."
|
||||
|
||||
#: src/pages/stages/user_login/UserLoginStageForm.ts
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "(Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "(Format: hours=1;minutes=2;seconds=3)."
|
||||
|
||||
|
@ -445,8 +446,12 @@ msgid "Are you sure you want to update {0} \"{1}\"?"
|
|||
msgstr "Czy na pewno chcesz zaktualizować {0} \"{1}”?"
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "Asercja nieważna w bieżącym lub późniejszym czasie + ta wartość (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgid "Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "Asercja nieważna w bieżącym lub późniejszym czasie + ta wartość (Format: hours=1;minutes=2;seconds=3)."
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Assertion not valid on or after current time + this value."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Assertion valid not before"
|
||||
|
@ -2484,6 +2489,10 @@ msgstr "Identyfikator"
|
|||
#~ msgid "Identity & Cryptography"
|
||||
#~ msgstr "Tożsamość i Kryptografia"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "If any of the devices user of the types selected above have been used within this duration, this stage will be skipped."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/outposts/ServiceConnectionDockerForm.ts
|
||||
#: src/pages/outposts/ServiceConnectionKubernetesForm.ts
|
||||
msgid "If enabled, use the local connection. Required Docker socket/Kubernetes Integration."
|
||||
|
@ -2759,6 +2768,10 @@ msgstr "Ostatnio widziany: {0}"
|
|||
msgid "Last sync: {0}"
|
||||
msgstr "Ostatnia synchronizacja: {0}"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Last validation threshold"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/applications/ApplicationViewPage.ts
|
||||
#: src/pages/applications/ApplicationViewPage.ts
|
||||
msgid "Launch"
|
||||
|
@ -3480,8 +3493,12 @@ msgid "Objects created"
|
|||
msgstr "Utworzone obiekty"
|
||||
|
||||
#: src/pages/stages/consent/ConsentStageForm.ts
|
||||
msgid "Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "Przesunięcie, po którym zgoda wygasa. (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgid "Offset after which consent expires."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/stages/consent/ConsentStageForm.ts
|
||||
#~ msgid "Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "Przesunięcie, po którym zgoda wygasa. (Format: hours=1;minutes=2;seconds=3)."
|
||||
|
||||
#: src/elements/events/ObjectChangelog.ts
|
||||
#: src/elements/events/UserEvents.ts
|
||||
|
@ -4512,8 +4529,12 @@ msgid "Session duration"
|
|||
msgstr "Długość sesji"
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "Sesja nieważna w bieżącym czasie lub później + ta wartość (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgid "Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "Sesja nieważna w bieżącym czasie lub później + ta wartość (Format: hours=1;minutes=2;seconds=3)."
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Session not valid on or after current time + this value."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Session valid not on or after"
|
||||
|
@ -5322,6 +5343,10 @@ msgstr "Zewnętrzny adres URL, pod którym uzyskasz dostęp do aplikacji. Uwzgl
|
|||
msgid "The external URL you'll authenticate at. The authentik core server should be reachable under this URL."
|
||||
msgstr "Zewnętrzny adres URL, pod którym będziesz się uwierzytelniać. Jądro serwera authentik powinien być dostępny pod tym adresem URL."
|
||||
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "The following keywords are supported:"
|
||||
msgstr ""
|
||||
|
||||
#~ msgid "The following objects use {0}:"
|
||||
#~ msgstr "Następujące obiekty używają {0}:"
|
||||
|
||||
|
@ -5431,8 +5456,12 @@ msgid "Time in minutes the token sent is valid."
|
|||
msgstr "Czas w minutach, w którym wysłany token jest ważny."
|
||||
|
||||
#: src/pages/sources/saml/SAMLSourceForm.ts
|
||||
msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "Przesunięcie czasowe, kiedy użytkownicy tymczasowi powinni zostać usunięci. Ma to zastosowanie tylko wtedy, gdy IDP używa „przejściowego” formatu NameID, a użytkownik nie wylogowuje się ręcznie. (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/sources/saml/SAMLSourceForm.ts
|
||||
#~ msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "Przesunięcie czasowe, kiedy użytkownicy tymczasowi powinni zostać usunięci. Ma to zastosowanie tylko wtedy, gdy IDP używa „przejściowego” formatu NameID, a użytkownik nie wylogowuje się ręcznie. (Format: hours=1;minutes=2;seconds=3)."
|
||||
|
||||
#~ msgid "Time-based One-Time Passwords"
|
||||
#~ msgstr "Hasła jednorazowe oparte na czasie"
|
||||
|
|
|
@ -17,14 +17,15 @@ msgstr ""
|
|||
#~ msgid "#/identity/users/{0}"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
#: src/pages/providers/proxy/ProxyProviderForm.ts
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
#~ msgid "(Format: days=-1;minutes=-2;seconds=-3)."
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "(Format: hours=-1;minutes=-2;seconds=-3)."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/stages/user_login/UserLoginStageForm.ts
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "(Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr ""
|
||||
|
||||
|
@ -440,7 +441,11 @@ msgid "Are you sure you want to update {0} \"{1}\"?"
|
|||
msgstr ""
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgid "Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Assertion not valid on or after current time + this value."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
|
@ -2524,6 +2529,10 @@ msgstr ""
|
|||
#~ msgid "Identity & Cryptography"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "If any of the devices user of the types selected above have been used within this duration, this stage will be skipped."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/outposts/ServiceConnectionDockerForm.ts
|
||||
#: src/pages/outposts/ServiceConnectionKubernetesForm.ts
|
||||
msgid "If enabled, use the local connection. Required Docker socket/Kubernetes Integration."
|
||||
|
@ -2802,6 +2811,10 @@ msgstr ""
|
|||
msgid "Last sync: {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Last validation threshold"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/applications/ApplicationViewPage.ts
|
||||
#: src/pages/applications/ApplicationViewPage.ts
|
||||
msgid "Launch"
|
||||
|
@ -3530,9 +3543,13 @@ msgid "Objects created"
|
|||
msgstr ""
|
||||
|
||||
#: src/pages/stages/consent/ConsentStageForm.ts
|
||||
msgid "Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgid "Offset after which consent expires."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/stages/consent/ConsentStageForm.ts
|
||||
#~ msgid "Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/elements/events/ObjectChangelog.ts
|
||||
#: src/elements/events/UserEvents.ts
|
||||
#: src/pages/events/EventListPage.ts
|
||||
|
@ -4590,7 +4607,11 @@ msgid "Session duration"
|
|||
msgstr ""
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgid "Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Session not valid on or after current time + this value."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
|
@ -5425,6 +5446,10 @@ msgstr ""
|
|||
msgid "The external URL you'll authenticate at. The authentik core server should be reachable under this URL."
|
||||
msgstr ""
|
||||
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "The following keywords are supported:"
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
#~ msgid "The following objects use {0}:"
|
||||
#~ msgstr ""
|
||||
|
@ -5526,9 +5551,13 @@ msgid "Time in minutes the token sent is valid."
|
|||
msgstr ""
|
||||
|
||||
#: src/pages/sources/saml/SAMLSourceForm.ts
|
||||
msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/sources/saml/SAMLSourceForm.ts
|
||||
#~ msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr ""
|
||||
|
||||
#:
|
||||
#~ msgid "Time-based One-Time Passwords"
|
||||
#~ msgstr ""
|
||||
|
|
|
@ -20,14 +20,15 @@ msgstr ""
|
|||
#~ msgid "#/identity/users/{0}"
|
||||
#~ msgstr "#/kimlik/kullanıcılar/ {0}"
|
||||
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
#: src/pages/providers/proxy/ProxyProviderForm.ts
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
#~ msgid "(Format: days=-1;minutes=-2;seconds=-3)."
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "(Format: hours=-1;minutes=-2;seconds=-3)."
|
||||
msgstr "(Biçim: saat=-1; dakika=-2; ikincil=-3)."
|
||||
|
||||
#: src/pages/stages/user_login/UserLoginStageForm.ts
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "(Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "(Biçim: saat=1; dakika=2; saniye= 3)."
|
||||
|
||||
|
@ -445,8 +446,12 @@ msgid "Are you sure you want to update {0} \"{1}\"?"
|
|||
msgstr "{0} “{1}” güncellemesini istediğinizden emin misiniz?"
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "Onay işlemi geçerli saat+bu değerden sonra geçerli değil (Biçim: hours=1; Dakika=2; ikinci=3)."
|
||||
#~ msgid "Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "Onay işlemi geçerli saat+bu değerden sonra geçerli değil (Biçim: hours=1; Dakika=2; ikinci=3)."
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Assertion not valid on or after current time + this value."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Assertion valid not before"
|
||||
|
@ -2487,6 +2492,10 @@ msgstr "Tanımlayıcı"
|
|||
#~ msgid "Identity & Cryptography"
|
||||
#~ msgstr "Kimlik ve Kriptografi"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "If any of the devices user of the types selected above have been used within this duration, this stage will be skipped."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/outposts/ServiceConnectionDockerForm.ts
|
||||
#: src/pages/outposts/ServiceConnectionKubernetesForm.ts
|
||||
msgid "If enabled, use the local connection. Required Docker socket/Kubernetes Integration."
|
||||
|
@ -2763,6 +2772,10 @@ msgstr "Son görüldü: {0}"
|
|||
msgid "Last sync: {0}"
|
||||
msgstr "Son senkronizasyon: {0}"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Last validation threshold"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/applications/ApplicationViewPage.ts
|
||||
#: src/pages/applications/ApplicationViewPage.ts
|
||||
msgid "Launch"
|
||||
|
@ -3485,8 +3498,12 @@ msgid "Objects created"
|
|||
msgstr "Oluşturulan nesneler"
|
||||
|
||||
#: src/pages/stages/consent/ConsentStageForm.ts
|
||||
msgid "Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "Onay sona erdikten sonra ofset. (Biçim: saat=1; dakika=2; saniye/= 3)."
|
||||
msgid "Offset after which consent expires."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/stages/consent/ConsentStageForm.ts
|
||||
#~ msgid "Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "Onay sona erdikten sonra ofset. (Biçim: saat=1; dakika=2; saniye/= 3)."
|
||||
|
||||
#: src/elements/events/ObjectChangelog.ts
|
||||
#: src/elements/events/UserEvents.ts
|
||||
|
@ -4517,8 +4534,12 @@ msgid "Session duration"
|
|||
msgstr "Oturum süresi"
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "Oturum geçerli saat+bu değerden sonra geçerli değil (Biçim: hours=1; Dakika=2; ikinci=3)."
|
||||
#~ msgid "Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "Oturum geçerli saat+bu değerden sonra geçerli değil (Biçim: hours=1; Dakika=2; ikinci=3)."
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Session not valid on or after current time + this value."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Session valid not on or after"
|
||||
|
@ -5327,6 +5348,10 @@ msgstr "Uygulamaya erişeceğiniz harici URL. Standart olmayan herhangi bir bağ
|
|||
msgid "The external URL you'll authenticate at. The authentik core server should be reachable under this URL."
|
||||
msgstr "Kimlik doğrulayacağınız harici URL. Auentik çekirdek sunucusuna bu URL altında erişilebilir olmalıdır."
|
||||
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "The following keywords are supported:"
|
||||
msgstr ""
|
||||
|
||||
#~ msgid "The following objects use {0}:"
|
||||
#~ msgstr "Aşağıdaki nesneler {0} kullanır:"
|
||||
|
||||
|
@ -5436,8 +5461,12 @@ msgid "Time in minutes the token sent is valid."
|
|||
msgstr "Gönderilen belirtecin dakika cinsinden geçerlilik süresi."
|
||||
|
||||
#: src/pages/sources/saml/SAMLSourceForm.ts
|
||||
msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "Geçici kullanıcıların silinmesi gerektiğinde zaman uzaklığı. Bu yalnızca IDP'niz NameID Biçimi 'geçici' kullanıyorsa ve kullanıcı el ile oturumu kapatmazsa geçerlidir. (Biçim: saat=1; dakika=2; saniye/= 3)."
|
||||
msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/sources/saml/SAMLSourceForm.ts
|
||||
#~ msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "Geçici kullanıcıların silinmesi gerektiğinde zaman uzaklığı. Bu yalnızca IDP'niz NameID Biçimi 'geçici' kullanıyorsa ve kullanıcı el ile oturumu kapatmazsa geçerlidir. (Biçim: saat=1; dakika=2; saniye/= 3)."
|
||||
|
||||
#~ msgid "Time-based One-Time Passwords"
|
||||
#~ msgstr "Zaman Tabanlı Tek seferlik Parolalar"
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -22,14 +22,15 @@ msgstr ""
|
|||
#~ msgid "#/identity/users/{0}"
|
||||
#~ msgstr "#/identity/users/{0}"
|
||||
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
#: src/pages/providers/proxy/ProxyProviderForm.ts
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
#~ msgid "(Format: days=-1;minutes=-2;seconds=-3)."
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "(Format: hours=-1;minutes=-2;seconds=-3)."
|
||||
msgstr "(格式: hours=-1;minutes=-2;seconds=-3)."
|
||||
|
||||
#: src/pages/stages/user_login/UserLoginStageForm.ts
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "(Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "(格式: hours=1;minutes=2;seconds=3)."
|
||||
|
||||
|
@ -446,8 +447,12 @@ msgid "Are you sure you want to update {0} \"{1}\"?"
|
|||
msgstr "你确定要更新 {0} \"{1}\" 吗?"
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "断言在当前时间+此值时或之后无效(格式:hours=1;minutes=2;seconds=3)。"
|
||||
#~ msgid "Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "断言在当前时间+此值时或之后无效(格式:hours=1;minutes=2;seconds=3)。"
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Assertion not valid on or after current time + this value."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Assertion valid not before"
|
||||
|
@ -2476,6 +2481,10 @@ msgstr "标识符"
|
|||
#~ msgid "Identity & Cryptography"
|
||||
#~ msgstr "身份与加密"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "If any of the devices user of the types selected above have been used within this duration, this stage will be skipped."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/outposts/ServiceConnectionDockerForm.ts
|
||||
#: src/pages/outposts/ServiceConnectionKubernetesForm.ts
|
||||
msgid "If enabled, use the local connection. Required Docker socket/Kubernetes Integration."
|
||||
|
@ -2749,6 +2758,10 @@ msgstr "最后显示:{0}"
|
|||
msgid "Last sync: {0}"
|
||||
msgstr "上次同步:{0}"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Last validation threshold"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/applications/ApplicationViewPage.ts
|
||||
#: src/pages/applications/ApplicationViewPage.ts
|
||||
msgid "Launch"
|
||||
|
@ -3466,8 +3479,12 @@ msgid "Objects created"
|
|||
msgstr "已创建对象"
|
||||
|
||||
#: src/pages/stages/consent/ConsentStageForm.ts
|
||||
msgid "Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "偏移量,在此之后同意过期。(格式:hours=1;minutes=2;seconds=3)。"
|
||||
msgid "Offset after which consent expires."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/stages/consent/ConsentStageForm.ts
|
||||
#~ msgid "Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "偏移量,在此之后同意过期。(格式:hours=1;minutes=2;seconds=3)。"
|
||||
|
||||
#: src/elements/events/ObjectChangelog.ts
|
||||
#: src/elements/events/UserEvents.ts
|
||||
|
@ -4485,8 +4502,12 @@ msgid "Session duration"
|
|||
msgstr "会话持续时间"
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "在当前时间+此值时或之后,会话无效(格式:hours=1;minutes=2;seconds=3)。"
|
||||
#~ msgid "Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "在当前时间+此值时或之后,会话无效(格式:hours=1;minutes=2;seconds=3)。"
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Session not valid on or after current time + this value."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Session valid not on or after"
|
||||
|
@ -5293,6 +5314,10 @@ msgstr "您将通过其访问应用程序的外部 URL。包括任何非标准
|
|||
msgid "The external URL you'll authenticate at. The authentik core server should be reachable under this URL."
|
||||
msgstr "您将在其中进行身份验证的外部 URL。在此 URL 下应该可以访问身份验证核心服务器。"
|
||||
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "The following keywords are supported:"
|
||||
msgstr ""
|
||||
|
||||
#~ msgid "The following objects use {0}:"
|
||||
#~ msgstr "以下对象使用 {0}:"
|
||||
|
||||
|
@ -5402,8 +5427,12 @@ msgid "Time in minutes the token sent is valid."
|
|||
msgstr "发送的令牌的有效时间(以分钟为单位)。"
|
||||
|
||||
#: src/pages/sources/saml/SAMLSourceForm.ts
|
||||
msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "删除临时用户的时间偏移。这仅适用于您的 IDP 使用 NameID 格式为 “瞬态” 且用户未手动注销的情况。(格式:hours=1;minutes=2;seconds=3)。"
|
||||
msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/sources/saml/SAMLSourceForm.ts
|
||||
#~ msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "删除临时用户的时间偏移。这仅适用于您的 IDP 使用 NameID 格式为 “瞬态” 且用户未手动注销的情况。(格式:hours=1;minutes=2;seconds=3)。"
|
||||
|
||||
#~ msgid "Time-based One-Time Passwords"
|
||||
#~ msgstr "基于时间的一次性密码"
|
||||
|
|
|
@ -22,14 +22,15 @@ msgstr ""
|
|||
#~ msgid "#/identity/users/{0}"
|
||||
#~ msgstr "#/identity/users/{0}"
|
||||
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
#: src/pages/providers/proxy/ProxyProviderForm.ts
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
#~ msgid "(Format: days=-1;minutes=-2;seconds=-3)."
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "(Format: hours=-1;minutes=-2;seconds=-3)."
|
||||
msgstr "(格式: hours=-1;minutes=-2;seconds=-3)."
|
||||
|
||||
#: src/pages/stages/user_login/UserLoginStageForm.ts
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "(Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "(格式: hours=1;minutes=2;seconds=3)."
|
||||
|
||||
|
@ -446,8 +447,12 @@ msgid "Are you sure you want to update {0} \"{1}\"?"
|
|||
msgstr "你确定要更新 {0} \"{1}\" 吗?"
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "断言在当前时间+此值时或之后无效(格式:hours=1;minutes=2;seconds=3)。"
|
||||
#~ msgid "Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "断言在当前时间+此值时或之后无效(格式:hours=1;minutes=2;seconds=3)。"
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Assertion not valid on or after current time + this value."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Assertion valid not before"
|
||||
|
@ -2476,6 +2481,10 @@ msgstr "标识符"
|
|||
#~ msgid "Identity & Cryptography"
|
||||
#~ msgstr "身份与加密"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "If any of the devices user of the types selected above have been used within this duration, this stage will be skipped."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/outposts/ServiceConnectionDockerForm.ts
|
||||
#: src/pages/outposts/ServiceConnectionKubernetesForm.ts
|
||||
msgid "If enabled, use the local connection. Required Docker socket/Kubernetes Integration."
|
||||
|
@ -2749,6 +2758,10 @@ msgstr "最后显示:{0}"
|
|||
msgid "Last sync: {0}"
|
||||
msgstr "上次同步:{0}"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Last validation threshold"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/applications/ApplicationViewPage.ts
|
||||
#: src/pages/applications/ApplicationViewPage.ts
|
||||
msgid "Launch"
|
||||
|
@ -3466,8 +3479,12 @@ msgid "Objects created"
|
|||
msgstr "已创建对象"
|
||||
|
||||
#: src/pages/stages/consent/ConsentStageForm.ts
|
||||
msgid "Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "偏移量,在此之后同意过期。(格式:hours=1;minutes=2;seconds=3)。"
|
||||
msgid "Offset after which consent expires."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/stages/consent/ConsentStageForm.ts
|
||||
#~ msgid "Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "偏移量,在此之后同意过期。(格式:hours=1;minutes=2;seconds=3)。"
|
||||
|
||||
#: src/elements/events/ObjectChangelog.ts
|
||||
#: src/elements/events/UserEvents.ts
|
||||
|
@ -4485,8 +4502,12 @@ msgid "Session duration"
|
|||
msgstr "会话持续时间"
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "在当前时间+此值时或之后,会话无效(格式:hours=1;minutes=2;seconds=3)。"
|
||||
#~ msgid "Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "在当前时间+此值时或之后,会话无效(格式:hours=1;minutes=2;seconds=3)。"
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Session not valid on or after current time + this value."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Session valid not on or after"
|
||||
|
@ -5293,6 +5314,10 @@ msgstr "您将通过其访问应用程序的外部 URL。包括任何非标准
|
|||
msgid "The external URL you'll authenticate at. The authentik core server should be reachable under this URL."
|
||||
msgstr "您将在其中进行身份验证的外部 URL。在此 URL 下应该可以访问身份验证核心服务器。"
|
||||
|
||||
#: src/elements/utils/TimeDeltaHelp.ts
|
||||
msgid "The following keywords are supported:"
|
||||
msgstr ""
|
||||
|
||||
#~ msgid "The following objects use {0}:"
|
||||
#~ msgstr "以下对象使用 {0}:"
|
||||
|
||||
|
@ -5402,8 +5427,12 @@ msgid "Time in minutes the token sent is valid."
|
|||
msgstr "发送的令牌的有效时间(以分钟为单位)。"
|
||||
|
||||
#: src/pages/sources/saml/SAMLSourceForm.ts
|
||||
msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "删除临时用户的时间偏移。这仅适用于您的 IDP 使用 NameID 格式为 “瞬态” 且用户未手动注销的情况。(格式:hours=1;minutes=2;seconds=3)。"
|
||||
msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/sources/saml/SAMLSourceForm.ts
|
||||
#~ msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3)."
|
||||
#~ msgstr "删除临时用户的时间偏移。这仅适用于您的 IDP 使用 NameID 格式为 “瞬态” 且用户未手动注销的情况。(格式:hours=1;minutes=2;seconds=3)。"
|
||||
|
||||
#~ msgid "Time-based One-Time Passwords"
|
||||
#~ msgstr "基于时间的一次性密码"
|
||||
|
|
|
@ -21,6 +21,7 @@ import { DEFAULT_CONFIG } from "../../../api/Config";
|
|||
import "../../../elements/forms/FormGroup";
|
||||
import "../../../elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
import "../../../elements/utils/TimeDeltaHelp";
|
||||
import { first, randomString } from "../../../utils";
|
||||
|
||||
@customElement("ak-provider-oauth2-form")
|
||||
|
@ -230,9 +231,7 @@ ${this.instance?.redirectUris}</textarea
|
|||
<p class="pf-c-form__helper-text">
|
||||
${t`If you are using an Implicit, client-side flow (where the token-endpoint isn't used), you probably want to increase this time.`}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`(Format: hours=-1;minutes=-2;seconds=-3).`}
|
||||
</p>
|
||||
<ak-utils-time-delta-help></ak-utils-time-delta-help>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Token validity`}
|
||||
|
@ -248,9 +247,7 @@ ${this.instance?.redirectUris}</textarea
|
|||
<p class="pf-c-form__helper-text">
|
||||
${t`Configure how long refresh tokens and their id_tokens are valid for.`}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`(Format: hours=-1;minutes=-2;seconds=-3).`}
|
||||
</p>
|
||||
<ak-utils-time-delta-help></ak-utils-time-delta-help>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${t`Scopes`} name="propertyMappings">
|
||||
<select class="pf-c-form-control" multiple>
|
||||
|
|
|
@ -25,6 +25,7 @@ import { DEFAULT_CONFIG } from "../../../api/Config";
|
|||
import "../../../elements/forms/FormGroup";
|
||||
import "../../../elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
import "../../../elements/utils/TimeDeltaHelp";
|
||||
import { first } from "../../../utils";
|
||||
|
||||
@customElement("ak-provider-proxy-form")
|
||||
|
@ -329,9 +330,7 @@ export class ProxyProviderFormPage extends ModelForm<ProxyProvider, number> {
|
|||
class="pf-c-form-control"
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">${t`Configure how long tokens are valid for.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`(Format: hours=-1;minutes=-2;seconds=-3).`}
|
||||
</p>
|
||||
<ak-utils-time-delta-help></ak-utils-time-delta-help>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-form-group>
|
||||
|
|
|
@ -21,6 +21,7 @@ import { DEFAULT_CONFIG } from "../../../api/Config";
|
|||
import "../../../elements/forms/FormGroup";
|
||||
import "../../../elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
import "../../../elements/utils/TimeDeltaHelp";
|
||||
|
||||
@customElement("ak-provider-saml-form")
|
||||
export class SAMLProviderFormPage extends ModelForm<SAMLProvider, number> {
|
||||
|
@ -299,9 +300,7 @@ export class SAMLProviderFormPage extends ModelForm<SAMLProvider, number> {
|
|||
<p class="pf-c-form__helper-text">
|
||||
${t`Configure the maximum allowed time drift for an assertion.`}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`(Format: hours=-1;minutes=-2;seconds=-3).`}
|
||||
</p>
|
||||
<ak-utils-time-delta-help></ak-utils-time-delta-help>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Assertion valid not on or after`}
|
||||
|
@ -315,8 +314,9 @@ export class SAMLProviderFormPage extends ModelForm<SAMLProvider, number> {
|
|||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3).`}
|
||||
${t`Assertion not valid on or after current time + this value.`}
|
||||
</p>
|
||||
<ak-utils-time-delta-help></ak-utils-time-delta-help>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Session valid not on or after`}
|
||||
|
@ -330,8 +330,9 @@ export class SAMLProviderFormPage extends ModelForm<SAMLProvider, number> {
|
|||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3).`}
|
||||
${t`Session not valid on or after current time + this value.`}
|
||||
</p>
|
||||
<ak-utils-time-delta-help></ak-utils-time-delta-help>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-form-element-horizontal
|
||||
|
|
|
@ -21,6 +21,7 @@ import { DEFAULT_CONFIG } from "../../../api/Config";
|
|||
import "../../../elements/forms/FormGroup";
|
||||
import "../../../elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
import "../../../elements/utils/TimeDeltaHelp";
|
||||
import { first } from "../../../utils";
|
||||
|
||||
@customElement("ak-source-saml-form")
|
||||
|
@ -243,8 +244,9 @@ export class SAMLSourceForm extends ModelForm<SAMLSource, string> {
|
|||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3).`}
|
||||
${t`Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually.`}
|
||||
</p>
|
||||
<ak-utils-time-delta-help></ak-utils-time-delta-help>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Digest algorithm`}
|
||||
|
|
|
@ -16,6 +16,7 @@ import { DEFAULT_CONFIG } from "../../../api/Config";
|
|||
import "../../../elements/forms/FormGroup";
|
||||
import "../../../elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
import "../../../elements/utils/TimeDeltaHelp";
|
||||
|
||||
@customElement("ak-stage-authenticator-validate-form")
|
||||
export class AuthenticatorValidateStageForm extends ModelForm<AuthenticatorValidateStage, string> {
|
||||
|
@ -123,6 +124,22 @@ export class AuthenticatorValidateStageForm extends ModelForm<AuthenticatorValid
|
|||
${t`Hold control/command to select multiple items.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Last validation threshold`}
|
||||
?required=${true}
|
||||
name="lastAuthThreshold"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${this.instance?.lastAuthThreshold || "seconds=0"}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`If any of the devices user of the types selected above have been used within this duration, this stage will be skipped.`}
|
||||
</p>
|
||||
<ak-utils-time-delta-help></ak-utils-time-delta-help>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Not configured action`}
|
||||
?required=${true}
|
||||
|
|
|
@ -10,6 +10,7 @@ import { DEFAULT_CONFIG } from "../../../api/Config";
|
|||
import "../../../elements/forms/FormGroup";
|
||||
import "../../../elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
import "../../../elements/utils/TimeDeltaHelp";
|
||||
|
||||
@customElement("ak-stage-consent-form")
|
||||
export class ConsentStageForm extends ModelForm<ConsentStage, string> {
|
||||
|
@ -113,8 +114,9 @@ export class ConsentStageForm extends ModelForm<ConsentStage, string> {
|
|||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3).`}
|
||||
${t`Offset after which consent expires.`}
|
||||
</p>
|
||||
<ak-utils-time-delta-help></ak-utils-time-delta-help>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
|
|
|
@ -9,6 +9,7 @@ import { DEFAULT_CONFIG } from "../../../api/Config";
|
|||
import "../../../elements/forms/FormGroup";
|
||||
import "../../../elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
import "../../../elements/utils/TimeDeltaHelp";
|
||||
import { first } from "../../../utils";
|
||||
|
||||
@customElement("ak-stage-user-login-form")
|
||||
|
@ -68,9 +69,7 @@ export class UserLoginStageForm extends ModelForm<UserLoginStage, string> {
|
|||
<p class="pf-c-form__helper-text">
|
||||
${t`Determines how long a session lasts. Default of 0 seconds means that the sessions lasts until the browser is closed.`}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`(Format: hours=1;minutes=2;seconds=3).`}
|
||||
</p>
|
||||
<ak-utils-time-delta-help></ak-utils-time-delta-help>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
|
|
|
@ -18,6 +18,9 @@ Using the `Not configured action`, you can choose what happens when a user does
|
|||
- Deny: Access is denied, the flow execution ends
|
||||
- Configure: This option requires a _Configuration stage_ to be set. The validation stage will be marked as successful, and the configuration stage will be injected into the flow.
|
||||
|
||||
By default, authenticator validation is required every time the flow containing this stage is executed. To only change this behavior, set _Last validation threshold_ to a non-zero value. (Requires authentik 2022.5)
|
||||
Keep in mind that when using Code-based devices (TOTP, Static and SMS), values lower than `seconds=30` cannot be used, as with the way TOTP devices are saved, there is no exact timestamp.
|
||||
|
||||
## Passwordless authentication
|
||||
|
||||
:::info
|
||||
|
|
Reference in New Issue