Compare commits
3 Commits
permission
...
enterprise
Author | SHA1 | Date | |
---|---|---|---|
ab42a62916 | |||
ef8d2bdd40 | |||
32f4e08eac |
@ -5,7 +5,7 @@
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socioeconomic status,
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
|
@ -3,8 +3,7 @@
|
||||
# Stage 1: Build website
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/node:22 AS website-builder
|
||||
|
||||
ENV NODE_ENV=production \
|
||||
GIT_UNAVAILABLE=true
|
||||
ENV NODE_ENV=production
|
||||
|
||||
WORKDIR /work/website
|
||||
|
||||
|
4
Makefile
4
Makefile
@ -4,7 +4,7 @@
|
||||
PWD = $(shell pwd)
|
||||
UID = $(shell id -u)
|
||||
GID = $(shell id -g)
|
||||
NPM_VERSION = $(shell python -m scripts.generate_semver)
|
||||
NPM_VERSION = $(shell poetry run python -m scripts.generate_semver)
|
||||
PY_SOURCES = authentik tests scripts lifecycle .github
|
||||
DOCKER_IMAGE ?= "authentik:test"
|
||||
|
||||
@ -145,7 +145,7 @@ gen-client-py: gen-clean-py ## Build and install the authentik API for Python
|
||||
docker run \
|
||||
--rm -v ${PWD}:/local \
|
||||
--user ${UID}:${GID} \
|
||||
docker.io/openapitools/openapi-generator-cli:v7.11.0 generate \
|
||||
docker.io/openapitools/openapi-generator-cli:v7.4.0 generate \
|
||||
-i /local/schema.yml \
|
||||
-g python \
|
||||
-o /local/${GEN_API_PY} \
|
||||
|
@ -5,7 +5,6 @@ from collections.abc import Iterable
|
||||
from drf_spectacular.utils import OpenApiResponse, extend_schema
|
||||
from rest_framework import mixins
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.fields import CharField, ReadOnlyField, SerializerMethodField
|
||||
from rest_framework.parsers import MultiPartParser
|
||||
from rest_framework.request import Request
|
||||
@ -155,17 +154,6 @@ class SourceViewSet(
|
||||
matching_sources.append(source_settings.validated_data)
|
||||
return Response(matching_sources)
|
||||
|
||||
def destroy(self, request: Request, *args, **kwargs):
|
||||
"""Prevent deletion of built-in sources"""
|
||||
instance: Source = self.get_object()
|
||||
|
||||
if instance.managed == Source.MANAGED_INBUILT:
|
||||
raise ValidationError(
|
||||
{"detail": "Built-in sources cannot be deleted"}, code="protected"
|
||||
)
|
||||
|
||||
return super().destroy(request, *args, **kwargs)
|
||||
|
||||
|
||||
class UserSourceConnectionSerializer(SourceSerializer):
|
||||
"""User source connection"""
|
||||
|
@ -32,5 +32,5 @@ class AuthentikCoreConfig(ManagedAppConfig):
|
||||
"name": "authentik Built-in",
|
||||
"slug": "authentik-built-in",
|
||||
},
|
||||
managed=Source.MANAGED_INBUILT,
|
||||
managed="goauthentik.io/sources/inbuilt",
|
||||
)
|
||||
|
@ -678,8 +678,6 @@ class SourceGroupMatchingModes(models.TextChoices):
|
||||
class Source(ManagedModel, SerializerModel, PolicyBindingModel):
|
||||
"""Base Authentication source, i.e. an OAuth Provider, SAML Remote or LDAP Server"""
|
||||
|
||||
MANAGED_INBUILT = "goauthentik.io/sources/inbuilt"
|
||||
|
||||
name = models.TextField(help_text=_("Source's display Name."))
|
||||
slug = models.SlugField(help_text=_("Internal source name, used in URLs."), unique=True)
|
||||
|
||||
|
@ -9,7 +9,7 @@ class AuthentikEnterpriseAuditConfig(EnterpriseConfig):
|
||||
"""Enterprise app config"""
|
||||
|
||||
name = "authentik.enterprise.audit"
|
||||
label = "authentik_enterprise_audit"
|
||||
label = "authentik_audit"
|
||||
verbose_name = "authentik Enterprise.Audit"
|
||||
default = True
|
||||
|
||||
|
107
authentik/enterprise/audit/models.py
Normal file
107
authentik/enterprise/audit/models.py
Normal file
@ -0,0 +1,107 @@
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
from authentik.lib.models import SerializerModel
|
||||
from django.db import models
|
||||
from uuid import uuid4
|
||||
from authentik.core.models import Group, User
|
||||
|
||||
|
||||
# # Names
|
||||
# Lifecycle
|
||||
# Access reviews
|
||||
# Access lifecycle
|
||||
# Governance
|
||||
# Audit
|
||||
# Compliance
|
||||
|
||||
# Lifecycle
|
||||
# Lifecycle review
|
||||
# Review
|
||||
# Access review
|
||||
# Compliance review
|
||||
# X Scheduled review
|
||||
|
||||
|
||||
# Only some objects supported?
|
||||
#
|
||||
# For disabling support:
|
||||
# Application
|
||||
# Provider
|
||||
# Outpost (simply setting the list of providers to empty in the outpost itself)
|
||||
# Flow
|
||||
# Users
|
||||
# Groups <- will get tricky
|
||||
# Roles
|
||||
# Sources
|
||||
# Tokens (api, app_pass)
|
||||
# Brands
|
||||
# Outpost integrations
|
||||
#
|
||||
# w/o disabling support
|
||||
# System Settings
|
||||
# everything else
|
||||
# would need to show in an audit dashboard cause not all have pages to get details
|
||||
|
||||
# "default" policy for objects, by default, everlasting
|
||||
|
||||
|
||||
class AuditPolicyFailAction(models.TextChoices):
|
||||
# For preview
|
||||
NOTHING = "nothing"
|
||||
# Disable the thing failing, HOW
|
||||
DISABLE = "disable"
|
||||
# Emit events
|
||||
WARN = "warn"
|
||||
|
||||
|
||||
class LifecycleRule(SerializerModel):
|
||||
pass
|
||||
|
||||
|
||||
class ReviewRule(SerializerModel):
|
||||
id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
|
||||
|
||||
# Check every 6 months, allow for daily/weekly/first of month, etc.
|
||||
interval = models.TextField() # timedelta
|
||||
# Preventive notification
|
||||
reminder_interval = models.TextField() # timedelta
|
||||
|
||||
# Must be checked by these
|
||||
groups = models.ManyToManyField(Group)
|
||||
users = models.ManyToManyField(User)
|
||||
|
||||
# How many of the above must approve
|
||||
required_approvals = models.IntegerField(default=1)
|
||||
|
||||
# How long to wait before executing fail action
|
||||
grace_period = models.TextField() # timedelta
|
||||
|
||||
# What to do if not reviewed in time
|
||||
fail_action = models.CharField(choices=AuditPolicyFailAction)
|
||||
|
||||
|
||||
class AuditPolicyBinding(SerializerModel):
|
||||
id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
|
||||
|
||||
# Many to many ? Bind users/groups here instead of on the policy ?
|
||||
policy = models.ForeignKey(AuditPolicy, on_delete=models.PROTECT)
|
||||
|
||||
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
|
||||
object_id = models.TextField(blank=True) # optional to apply on all objects of specific type
|
||||
content_object = GenericForeignKey("content_type", "object_id")
|
||||
|
||||
# valid -> waiting review -> valid
|
||||
# valid -> waiting review -> review overdue -> valid
|
||||
# valid -> waiting review -> review overdue -> failed -> valid
|
||||
# look at django-fsm or django-viewflow
|
||||
status = models.TextField()
|
||||
|
||||
class Meta:
|
||||
indexes = (
|
||||
models.Index(fields=["content_type"]),
|
||||
models.Index(fields=["content_type", "object_id"]),
|
||||
)
|
||||
|
||||
|
||||
class AuditHistory:
|
||||
pass
|
@ -1,6 +1,5 @@
|
||||
"""Base Kubernetes Reconciler"""
|
||||
|
||||
import re
|
||||
from dataclasses import asdict
|
||||
from json import dumps
|
||||
from typing import TYPE_CHECKING, Generic, TypeVar
|
||||
@ -68,8 +67,7 @@ class KubernetesObjectReconciler(Generic[T]):
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Get the name of the object this reconciler manages"""
|
||||
|
||||
base_name = (
|
||||
return (
|
||||
self.controller.outpost.config.object_naming_template
|
||||
% {
|
||||
"name": slugify(self.controller.outpost.name),
|
||||
@ -77,16 +75,6 @@ class KubernetesObjectReconciler(Generic[T]):
|
||||
}
|
||||
).lower()
|
||||
|
||||
formatted = slugify(base_name)
|
||||
formatted = re.sub(r"[^a-z0-9-]", "-", formatted)
|
||||
formatted = re.sub(r"-+", "-", formatted)
|
||||
formatted = formatted[:63]
|
||||
|
||||
if not formatted:
|
||||
formatted = f"outpost-{self.controller.outpost.uuid.hex}"[:63]
|
||||
|
||||
return formatted
|
||||
|
||||
def get_patched_reference_object(self) -> T:
|
||||
"""Get patched reference object"""
|
||||
reference = self.get_reference_object()
|
||||
@ -124,6 +112,7 @@ class KubernetesObjectReconciler(Generic[T]):
|
||||
try:
|
||||
current = self.retrieve()
|
||||
except (OpenApiException, HTTPError) as exc:
|
||||
|
||||
if isinstance(exc, ApiException) and exc.status == HttpResponseNotFound.status_code:
|
||||
self.logger.debug("Failed to get current, triggering recreate")
|
||||
raise NeedsRecreate from exc
|
||||
@ -167,6 +156,7 @@ class KubernetesObjectReconciler(Generic[T]):
|
||||
self.delete(current)
|
||||
self.logger.debug("Removing")
|
||||
except (OpenApiException, HTTPError) as exc:
|
||||
|
||||
if isinstance(exc, ApiException) and exc.status == HttpResponseNotFound.status_code:
|
||||
self.logger.debug("Failed to get current, assuming non-existent")
|
||||
return
|
||||
|
@ -61,14 +61,9 @@ class KubernetesController(BaseController):
|
||||
client: KubernetesClient
|
||||
connection: KubernetesServiceConnection
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
outpost: Outpost,
|
||||
connection: KubernetesServiceConnection,
|
||||
client: KubernetesClient | None = None,
|
||||
) -> None:
|
||||
def __init__(self, outpost: Outpost, connection: KubernetesServiceConnection) -> None:
|
||||
super().__init__(outpost, connection)
|
||||
self.client = client if client else KubernetesClient(connection)
|
||||
self.client = KubernetesClient(connection)
|
||||
self.reconcilers = {
|
||||
SecretReconciler.reconciler_name(): SecretReconciler,
|
||||
DeploymentReconciler.reconciler_name(): DeploymentReconciler,
|
||||
|
@ -1,44 +0,0 @@
|
||||
"""Kubernetes controller tests"""
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
from authentik.blueprints.tests import reconcile_app
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.outposts.apps import MANAGED_OUTPOST
|
||||
from authentik.outposts.controllers.k8s.deployment import DeploymentReconciler
|
||||
from authentik.outposts.controllers.kubernetes import KubernetesController
|
||||
from authentik.outposts.models import KubernetesServiceConnection, Outpost, OutpostType
|
||||
|
||||
|
||||
class KubernetesControllerTests(TestCase):
|
||||
"""Kubernetes controller tests"""
|
||||
|
||||
@reconcile_app("authentik_outposts")
|
||||
def setUp(self) -> None:
|
||||
self.outpost = Outpost.objects.create(
|
||||
name="test",
|
||||
type=OutpostType.PROXY,
|
||||
)
|
||||
self.integration = KubernetesServiceConnection(name="test")
|
||||
|
||||
def test_gen_name(self):
|
||||
"""Ensure the generated name is valid"""
|
||||
controller = KubernetesController(
|
||||
Outpost.objects.filter(managed=MANAGED_OUTPOST).first(),
|
||||
self.integration,
|
||||
# Pass something not-none as client so we don't
|
||||
# attempt to connect to K8s as that's not needed
|
||||
client=self,
|
||||
)
|
||||
rec = DeploymentReconciler(controller)
|
||||
self.assertEqual(rec.name, "ak-outpost-authentik-embedded-outpost")
|
||||
|
||||
controller.outpost.name = generate_id()
|
||||
self.assertLess(len(rec.name), 64)
|
||||
|
||||
# Test custom naming template
|
||||
_cfg = controller.outpost.config
|
||||
_cfg.object_naming_template = ""
|
||||
controller.outpost.config = _cfg
|
||||
self.assertEqual(rec.name, f"outpost-{controller.outpost.uuid.hex}")
|
||||
self.assertLess(len(rec.name), 64)
|
@ -254,10 +254,10 @@ class OAuthAuthorizationParams:
|
||||
raise AuthorizeError(self.redirect_uri, "invalid_scope", self.grant_type, self.state)
|
||||
if SCOPE_OFFLINE_ACCESS in self.scope:
|
||||
# https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess
|
||||
# Don't explicitly request consent with offline_access, as the spec allows for
|
||||
# "other conditions for processing the request permitting offline access to the
|
||||
# requested resources are in place"
|
||||
# which we interpret as "the admin picks an authorization flow with or without consent"
|
||||
if PROMPT_CONSENT not in self.prompt:
|
||||
# Instead of ignoring the `offline_access` scope when `prompt`
|
||||
# isn't set to `consent`, we set override it ourselves
|
||||
self.prompt.add(PROMPT_CONSENT)
|
||||
if self.response_type not in [
|
||||
ResponseTypes.CODE,
|
||||
ResponseTypes.CODE_TOKEN,
|
||||
|
@ -1,9 +1,9 @@
|
||||
"""RAC app config"""
|
||||
|
||||
from authentik.blueprints.apps import ManagedAppConfig
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AuthentikProviderRAC(ManagedAppConfig):
|
||||
class AuthentikProviderRAC(AppConfig):
|
||||
"""authentik rac app config"""
|
||||
|
||||
name = "authentik.providers.rac"
|
||||
|
@ -4,7 +4,8 @@ from asgiref.sync import async_to_sync
|
||||
from channels.layers import get_channel_layer
|
||||
from django.contrib.auth.signals import user_logged_out
|
||||
from django.core.cache import cache
|
||||
from django.db.models.signals import post_delete, post_save, pre_delete
|
||||
from django.db.models import Model
|
||||
from django.db.models.signals import post_save, pre_delete
|
||||
from django.dispatch import receiver
|
||||
from django.http import HttpRequest
|
||||
|
||||
@ -45,8 +46,12 @@ def pre_delete_connection_token_disconnect(sender, instance: ConnectionToken, **
|
||||
)
|
||||
|
||||
|
||||
@receiver([post_save, post_delete], sender=Endpoint)
|
||||
def post_save_post_delete_endpoint(**_):
|
||||
"""Clear user's endpoint cache upon endpoint creation or deletion"""
|
||||
@receiver(post_save, sender=Endpoint)
|
||||
def post_save_endpoint(sender: type[Model], instance, created: bool, **_):
|
||||
"""Clear user's endpoint cache upon endpoint creation"""
|
||||
if not created: # pragma: no cover
|
||||
return
|
||||
|
||||
# Delete user endpoint cache
|
||||
keys = cache.keys(user_endpoint_cache_key("*"))
|
||||
cache.delete_many(keys)
|
||||
|
@ -28,7 +28,6 @@ class SCIMProviderSerializer(ProviderSerializer):
|
||||
"url",
|
||||
"verify_certificates",
|
||||
"token",
|
||||
"compatibility_mode",
|
||||
"exclude_users_service_account",
|
||||
"filter_group",
|
||||
"dry_run",
|
||||
|
@ -22,7 +22,7 @@ from authentik.lib.sync.outgoing.exceptions import (
|
||||
from authentik.lib.utils.http import get_http_session
|
||||
from authentik.providers.scim.clients.exceptions import SCIMRequestException
|
||||
from authentik.providers.scim.clients.schema import ServiceProviderConfiguration
|
||||
from authentik.providers.scim.models import SCIMCompatibilityMode, SCIMProvider
|
||||
from authentik.providers.scim.models import SCIMProvider
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from django.db.models import Model
|
||||
@ -90,14 +90,9 @@ class SCIMClient[TModel: "Model", TConnection: "Model", TSchema: "BaseModel"](
|
||||
"""Get Service provider config"""
|
||||
default_config = ServiceProviderConfiguration.default()
|
||||
try:
|
||||
config = ServiceProviderConfiguration.model_validate(
|
||||
return ServiceProviderConfiguration.model_validate(
|
||||
self._request("GET", "/ServiceProviderConfig")
|
||||
)
|
||||
if self.provider.compatibility_mode == SCIMCompatibilityMode.AWS:
|
||||
config.patch.supported = False
|
||||
if self.provider.compatibility_mode == SCIMCompatibilityMode.SLACK:
|
||||
config.filter.supported = True
|
||||
return config
|
||||
except (ValidationError, SCIMRequestException, NotFoundSyncException) as exc:
|
||||
self.logger.warning("failed to get ServiceProviderConfig", exc=exc)
|
||||
return default_config
|
||||
|
@ -1,12 +1,10 @@
|
||||
"""User client"""
|
||||
|
||||
from django.db import transaction
|
||||
from django.utils.http import urlencode
|
||||
from pydantic import ValidationError
|
||||
|
||||
from authentik.core.models import User
|
||||
from authentik.lib.sync.mapper import PropertyMappingManager
|
||||
from authentik.lib.sync.outgoing.exceptions import ObjectExistsSyncException, StopSync
|
||||
from authentik.lib.sync.outgoing.exceptions import StopSync
|
||||
from authentik.policies.utils import delete_none_values
|
||||
from authentik.providers.scim.clients.base import SCIMClient
|
||||
from authentik.providers.scim.clients.schema import SCIM_USER_SCHEMA
|
||||
@ -57,35 +55,18 @@ class SCIMUserClient(SCIMClient[User, SCIMProviderUser, SCIMUserSchema]):
|
||||
def create(self, user: User):
|
||||
"""Create user from scratch and create a connection object"""
|
||||
scim_user = self.to_schema(user, None)
|
||||
with transaction.atomic():
|
||||
try:
|
||||
response = self._request(
|
||||
"POST",
|
||||
"/Users",
|
||||
json=scim_user.model_dump(
|
||||
mode="json",
|
||||
exclude_unset=True,
|
||||
),
|
||||
)
|
||||
except ObjectExistsSyncException as exc:
|
||||
if not self._config.filter.supported:
|
||||
raise exc
|
||||
users = self._request(
|
||||
"GET", f"/Users?{urlencode({'filter': f'userName eq {scim_user.userName}'})}"
|
||||
)
|
||||
users_res = users.get("Resources", [])
|
||||
if len(users_res) < 1:
|
||||
raise exc
|
||||
return SCIMProviderUser.objects.create(
|
||||
provider=self.provider, user=user, scim_id=users_res[0]["id"]
|
||||
)
|
||||
else:
|
||||
scim_id = response.get("id")
|
||||
if not scim_id or scim_id == "":
|
||||
raise StopSync("SCIM Response with missing or invalid `id`")
|
||||
return SCIMProviderUser.objects.create(
|
||||
provider=self.provider, user=user, scim_id=scim_id
|
||||
)
|
||||
response = self._request(
|
||||
"POST",
|
||||
"/Users",
|
||||
json=scim_user.model_dump(
|
||||
mode="json",
|
||||
exclude_unset=True,
|
||||
),
|
||||
)
|
||||
scim_id = response.get("id")
|
||||
if not scim_id or scim_id == "":
|
||||
raise StopSync("SCIM Response with missing or invalid `id`")
|
||||
return SCIMProviderUser.objects.create(provider=self.provider, user=user, scim_id=scim_id)
|
||||
|
||||
def update(self, user: User, connection: SCIMProviderUser):
|
||||
"""Update existing user"""
|
||||
|
@ -1,24 +0,0 @@
|
||||
# Generated by Django 5.0.12 on 2025-03-07 23:35
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_providers_scim", "0011_scimprovider_dry_run"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="scimprovider",
|
||||
name="compatibility_mode",
|
||||
field=models.CharField(
|
||||
choices=[("default", "Default"), ("aws", "AWS"), ("slack", "Slack")],
|
||||
default="default",
|
||||
help_text="Alter authentik behavior for vendor-specific SCIM implementations.",
|
||||
max_length=30,
|
||||
verbose_name="SCIM Compatibility Mode",
|
||||
),
|
||||
),
|
||||
]
|
@ -57,14 +57,6 @@ class SCIMProviderGroup(SerializerModel):
|
||||
return f"SCIM Provider Group {self.group_id} to {self.provider_id}"
|
||||
|
||||
|
||||
class SCIMCompatibilityMode(models.TextChoices):
|
||||
"""SCIM compatibility mode"""
|
||||
|
||||
DEFAULT = "default", _("Default")
|
||||
AWS = "aws", _("AWS")
|
||||
SLACK = "slack", _("Slack")
|
||||
|
||||
|
||||
class SCIMProvider(OutgoingSyncProvider, BackchannelProvider):
|
||||
"""SCIM 2.0 provider to create users and groups in external applications"""
|
||||
|
||||
@ -85,14 +77,6 @@ class SCIMProvider(OutgoingSyncProvider, BackchannelProvider):
|
||||
help_text=_("Property mappings used for group creation/updating."),
|
||||
)
|
||||
|
||||
compatibility_mode = models.CharField(
|
||||
max_length=30,
|
||||
choices=SCIMCompatibilityMode.choices,
|
||||
default=SCIMCompatibilityMode.DEFAULT,
|
||||
verbose_name=_("SCIM Compatibility Mode"),
|
||||
help_text=_("Alter authentik behavior for vendor-specific SCIM implementations."),
|
||||
)
|
||||
|
||||
@property
|
||||
def icon_url(self) -> str | None:
|
||||
return static("authentik/sources/scim.png")
|
||||
|
@ -68,6 +68,8 @@ class OAuth2Client(BaseOAuthClient):
|
||||
error_desc = self.get_request_arg("error_description", None)
|
||||
return {"error": error_desc or error or _("No token received.")}
|
||||
args = {
|
||||
"client_id": self.get_client_id(),
|
||||
"client_secret": self.get_client_secret(),
|
||||
"redirect_uri": callback,
|
||||
"code": code,
|
||||
"grant_type": "authorization_code",
|
||||
|
@ -28,7 +28,7 @@ def update_well_known_jwks(self: SystemTask):
|
||||
LOGGER.warning("Failed to update well_known", source=source, exc=exc, text=text)
|
||||
messages.append(f"Failed to update OIDC configuration for {source.slug}")
|
||||
continue
|
||||
config: dict = well_known_config.json()
|
||||
config = well_known_config.json()
|
||||
try:
|
||||
dirty = False
|
||||
source_attr_key = (
|
||||
@ -40,9 +40,7 @@ def update_well_known_jwks(self: SystemTask):
|
||||
for source_attr, config_key in source_attr_key:
|
||||
# Check if we're actually changing anything to only
|
||||
# save when something has changed
|
||||
if config_key not in config:
|
||||
continue
|
||||
if getattr(source, source_attr, "") != config.get(config_key, ""):
|
||||
if getattr(source, source_attr, "") != config[config_key]:
|
||||
dirty = True
|
||||
setattr(source, source_attr, config[config_key])
|
||||
except (IndexError, KeyError) as exc:
|
||||
|
@ -25,10 +25,8 @@ class RedditOAuth2Client(UserprofileHeaderAuthClient):
|
||||
|
||||
def get_access_token(self, **request_kwargs):
|
||||
"Fetch access token from callback request."
|
||||
request_kwargs["auth"] = HTTPBasicAuth(
|
||||
self.source.consumer_key, self.source.consumer_secret
|
||||
)
|
||||
return super().get_access_token(**request_kwargs)
|
||||
auth = HTTPBasicAuth(self.source.consumer_key, self.source.consumer_secret)
|
||||
return super().get_access_token(auth=auth)
|
||||
|
||||
|
||||
class RedditOAuth2Callback(OAuthCallback):
|
||||
|
@ -1,54 +0,0 @@
|
||||
# Generated by Django 5.0.12 on 2025-02-27 04:32
|
||||
|
||||
import authentik.lib.utils.time
|
||||
from authentik.lib.utils.time import timedelta_from_string
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def convert_integer_to_string_format(apps, schema_editor):
|
||||
db_alias = schema_editor.connection.alias
|
||||
EmailStage = apps.get_model("authentik_stages_email", "EmailStage")
|
||||
for stage in EmailStage.objects.using(db_alias).all():
|
||||
stage.token_expiry = f"minutes={stage.token_expiry}"
|
||||
stage.save(using=db_alias)
|
||||
|
||||
|
||||
def convert_string_to_integer_format(apps, schema_editor):
|
||||
db_alias = schema_editor.connection.alias
|
||||
EmailStage = apps.get_model("authentik_stages_email", "EmailStage")
|
||||
for stage in EmailStage.objects.using(db_alias).all():
|
||||
# Check if token_expiry is a string
|
||||
if isinstance(stage.token_expiry, str):
|
||||
try:
|
||||
# Use the timedelta_from_string utility to convert to timedelta
|
||||
# then convert to minutes by dividing seconds by 60
|
||||
td = timedelta_from_string(stage.token_expiry)
|
||||
minutes_value = int(td.total_seconds() / 60)
|
||||
stage.token_expiry = minutes_value
|
||||
stage.save(using=db_alias)
|
||||
except (ValueError, TypeError):
|
||||
# If the string can't be parsed or converted properly, skip
|
||||
pass
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_stages_email", "0004_emailstage_activate_user_on_success"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="emailstage",
|
||||
name="token_expiry",
|
||||
field=models.TextField(
|
||||
default="minutes=30",
|
||||
help_text="Time the token sent is valid (Format: hours=3,minutes=17,seconds=300).",
|
||||
validators=[authentik.lib.utils.time.timedelta_string_validator],
|
||||
),
|
||||
),
|
||||
migrations.RunPython(
|
||||
convert_integer_to_string_format,
|
||||
convert_string_to_integer_format,
|
||||
),
|
||||
]
|
@ -14,7 +14,6 @@ from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.flows.models import Stage
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.utils.time import timedelta_string_validator
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
@ -75,10 +74,8 @@ class EmailStage(Stage):
|
||||
default=False, help_text=_("Activate users upon completion of stage.")
|
||||
)
|
||||
|
||||
token_expiry = models.TextField(
|
||||
default="minutes=30",
|
||||
validators=[timedelta_string_validator],
|
||||
help_text=_("Time the token sent is valid (Format: hours=3,minutes=17,seconds=300)."),
|
||||
token_expiry = models.IntegerField(
|
||||
default=30, help_text=_("Time in minutes the token sent is valid.")
|
||||
)
|
||||
subject = models.TextField(default="authentik")
|
||||
template = models.TextField(default=EmailTemplates.PASSWORD_RESET)
|
||||
|
@ -22,7 +22,6 @@ from authentik.flows.planner import PLAN_CONTEXT_IS_RESTORED, PLAN_CONTEXT_PENDI
|
||||
from authentik.flows.stage import ChallengeStageView
|
||||
from authentik.flows.views.executor import QS_KEY_TOKEN, QS_QUERY
|
||||
from authentik.lib.utils.errors import exception_to_string
|
||||
from authentik.lib.utils.time import timedelta_from_string
|
||||
from authentik.stages.email.models import EmailStage
|
||||
from authentik.stages.email.tasks import send_mails
|
||||
from authentik.stages.email.utils import TemplateEmailMessage
|
||||
@ -74,8 +73,8 @@ class EmailStageView(ChallengeStageView):
|
||||
"""Get token"""
|
||||
pending_user = self.get_pending_user()
|
||||
current_stage: EmailStage = self.executor.current_stage
|
||||
valid_delta = timedelta_from_string(current_stage.token_expiry) + timedelta(
|
||||
minutes=1
|
||||
valid_delta = timedelta(
|
||||
minutes=current_stage.token_expiry + 1
|
||||
) # + 1 because django timesince always rounds down
|
||||
identifier = slugify(f"ak-email-stage-{current_stage.name}-{str(uuid4())}")
|
||||
# Don't check for validity here, we only care if the token exists
|
||||
|
@ -57,7 +57,7 @@ entries:
|
||||
use_ssl: false
|
||||
timeout: 10
|
||||
from_address: system@authentik.local
|
||||
token_expiry: minutes=30
|
||||
token_expiry: 30
|
||||
subject: authentik
|
||||
template: email/password_reset.html
|
||||
activate_user_on_success: true
|
||||
|
@ -6661,16 +6661,6 @@
|
||||
"title": "Token",
|
||||
"description": "Authentication token"
|
||||
},
|
||||
"compatibility_mode": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"default",
|
||||
"aws",
|
||||
"slack"
|
||||
],
|
||||
"title": "SCIM Compatibility Mode",
|
||||
"description": "Alter authentik behavior for vendor-specific SCIM implementations."
|
||||
},
|
||||
"exclude_users_service_account": {
|
||||
"type": "boolean",
|
||||
"title": "Exclude users service account"
|
||||
@ -11379,10 +11369,11 @@
|
||||
"title": "From address"
|
||||
},
|
||||
"token_expiry": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"type": "integer",
|
||||
"minimum": -2147483648,
|
||||
"maximum": 2147483647,
|
||||
"title": "Token expiry",
|
||||
"description": "Time the token sent is valid (Format: hours=3,minutes=17,seconds=300)."
|
||||
"description": "Time in minutes the token sent is valid."
|
||||
},
|
||||
"subject": {
|
||||
"type": "string",
|
||||
|
10
go.mod
10
go.mod
@ -6,7 +6,7 @@ toolchain go1.24.0
|
||||
|
||||
require (
|
||||
beryju.io/ldap v0.1.0
|
||||
github.com/coreos/go-oidc/v3 v3.13.0
|
||||
github.com/coreos/go-oidc/v3 v3.12.0
|
||||
github.com/getsentry/sentry-go v0.31.1
|
||||
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
|
||||
github.com/go-ldap/ldap/v3 v3.4.10
|
||||
@ -29,7 +29,7 @@ require (
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/wwt/guac v1.3.2
|
||||
goauthentik.io/api/v3 v3.2025021.4
|
||||
goauthentik.io/api/v3 v3.2025021.2
|
||||
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
||||
golang.org/x/oauth2 v0.28.0
|
||||
golang.org/x/sync v0.12.0
|
||||
@ -76,9 +76,9 @@ require (
|
||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||
golang.org/x/crypto v0.36.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
golang.org/x/crypto v0.32.0 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
google.golang.org/protobuf v1.36.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
22
go.sum
22
go.sum
@ -55,8 +55,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/coreos/go-oidc/v3 v3.13.0 h1:M66zd0pcc5VxvBNM4pB331Wrsanby+QomQYjN8HamW8=
|
||||
github.com/coreos/go-oidc/v3 v3.13.0/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU=
|
||||
github.com/coreos/go-oidc/v3 v3.12.0 h1:sJk+8G2qq94rDI6ehZ71Bol3oUHy63qNYmkiSjrc/Jo=
|
||||
github.com/coreos/go-oidc/v3 v3.12.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
@ -299,8 +299,8 @@ go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
goauthentik.io/api/v3 v3.2025021.4 h1:KFap2KW+8CwhOxjBkRnRB4flvuHEMw24+fZei9dOhzw=
|
||||
goauthentik.io/api/v3 v3.2025021.4/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||
goauthentik.io/api/v3 v3.2025021.2 h1:9y87piH47omtkWxQpKZaKai/+jh+cJdLxj5MC2Y/ZLI=
|
||||
goauthentik.io/api/v3 v3.2025021.2/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
@ -313,8 +313,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@ -386,9 +386,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
||||
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@ -450,8 +449,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@ -472,9 +471,8 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
8
lifecycle/aws/package-lock.json
generated
8
lifecycle/aws/package-lock.json
generated
@ -9,7 +9,7 @@
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"aws-cdk": "^2.1004.0",
|
||||
"aws-cdk": "^2.1003.0",
|
||||
"cross-env": "^7.0.3"
|
||||
},
|
||||
"engines": {
|
||||
@ -17,9 +17,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk": {
|
||||
"version": "2.1004.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1004.0.tgz",
|
||||
"integrity": "sha512-3E5ICmSc7ZCZCwLX7NY+HFmmdUYgRaL+67h/BDoDQmkhx9StC8wG4xgzHFY9k8WQS0+ib/MP28f2d9yzHtQLlQ==",
|
||||
"version": "2.1003.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1003.0.tgz",
|
||||
"integrity": "sha512-FORPDGW8oUg4tXFlhX+lv/j+152LO9wwi3/CwNr1WY3c3HwJUtc0fZGb2B3+Fzy6NhLWGHJclUsJPEhjEt8Nhg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
|
@ -10,7 +10,7 @@
|
||||
"node": ">=20"
|
||||
},
|
||||
"devDependencies": {
|
||||
"aws-cdk": "^2.1004.0",
|
||||
"aws-cdk": "^2.1003.0",
|
||||
"cross-env": "^7.0.3"
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-03-13 00:10+0000\n"
|
||||
"POT-Creation-Date: 2025-03-02 00:10+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -1883,18 +1883,6 @@ msgstr ""
|
||||
msgid "SAML Providers from Metadata"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "Default"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "AWS"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "Slack"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "Base URL to SCIM requests, usually ends in /v2"
|
||||
msgstr ""
|
||||
@ -1903,14 +1891,6 @@ msgstr ""
|
||||
msgid "Authentication token"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "SCIM Compatibility Mode"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "Alter authentik behavior for vendor-specific SCIM implementations."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "SCIM Provider"
|
||||
msgstr ""
|
||||
@ -2555,7 +2535,6 @@ msgid ""
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/authenticator_email/models.py
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Time the token sent is valid (Format: hours=3,minutes=17,seconds=300)."
|
||||
msgstr ""
|
||||
|
||||
@ -2894,6 +2873,10 @@ msgstr ""
|
||||
msgid "Activate users upon completion of stage."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Time in minutes the token sent is valid."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Email Stage"
|
||||
msgstr ""
|
||||
|
@ -9,9 +9,9 @@
|
||||
# Kyllian Delaye-Maillot, 2023
|
||||
# Manuel Viens, 2023
|
||||
# Mordecai, 2023
|
||||
# Charles Leclerc, 2024
|
||||
# nerdinator <florian.dupret@gmail.com>, 2024
|
||||
# Tina, 2024
|
||||
# Charles Leclerc, 2025
|
||||
# Marc Schmitt, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
@ -19,7 +19,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-03-13 00:10+0000\n"
|
||||
"POT-Creation-Date: 2025-03-02 00:10+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: Marc Schmitt, 2025\n"
|
||||
"Language-Team: French (https://app.transifex.com/authentik/teams/119923/fr/)\n"
|
||||
@ -2097,18 +2097,6 @@ msgstr "Fournisseur SAML depuis métadonnées"
|
||||
msgid "SAML Providers from Metadata"
|
||||
msgstr "Fournisseurs SAML depuis métadonnées"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "Default"
|
||||
msgstr "Par défaut"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "AWS"
|
||||
msgstr "AWS"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "Slack"
|
||||
msgstr "Slack"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "Base URL to SCIM requests, usually ends in /v2"
|
||||
msgstr "URL de base pour les requêtes SCIM, se terminant généralement par /v2"
|
||||
@ -2117,16 +2105,6 @@ msgstr "URL de base pour les requêtes SCIM, se terminant généralement par /v2
|
||||
msgid "Authentication token"
|
||||
msgstr "Jeton d'authentification"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "SCIM Compatibility Mode"
|
||||
msgstr "Mode de compatibilité SCIM"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "Alter authentik behavior for vendor-specific SCIM implementations."
|
||||
msgstr ""
|
||||
"Change le comportement d'authentik en fonction des spécificités "
|
||||
"d'implémentations des fournisseurs SCIM."
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "SCIM Provider"
|
||||
msgstr "Fournisseur SCIM"
|
||||
@ -2819,7 +2797,6 @@ msgstr ""
|
||||
"les paramètres de connexion ci-dessous seront ignorés."
|
||||
|
||||
#: authentik/stages/authenticator_email/models.py
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Time the token sent is valid (Format: hours=3,minutes=17,seconds=300)."
|
||||
msgstr ""
|
||||
"Durée de validité du jeton envoyé (Format : hours=3,minutes=17,seconds=300)."
|
||||
@ -3191,6 +3168,10 @@ msgstr "Confirmation du Compte"
|
||||
msgid "Activate users upon completion of stage."
|
||||
msgstr "Activer les utilisateurs à la complétion de l'étape."
|
||||
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Time in minutes the token sent is valid."
|
||||
msgstr "Temps en minutes durant lequel le jeton envoyé est valide."
|
||||
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Email Stage"
|
||||
msgstr "Étape Email"
|
||||
|
Binary file not shown.
@ -7,7 +7,7 @@
|
||||
# Chen Zhikai, 2022
|
||||
# 刘松, 2022
|
||||
# Tianhao Chai <cth451@gmail.com>, 2024
|
||||
# Jens L. <jens@goauthentik.io>, 2025
|
||||
# Jens L. <jens@goauthentik.io>, 2024
|
||||
# deluxghost, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
@ -15,7 +15,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-03-13 00:10+0000\n"
|
||||
"POT-Creation-Date: 2025-03-02 00:10+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: deluxghost, 2025\n"
|
||||
"Language-Team: Chinese Simplified (https://app.transifex.com/authentik/teams/119923/zh-Hans/)\n"
|
||||
@ -1909,18 +1909,6 @@ msgstr "来自元数据的 SAML 提供程序"
|
||||
msgid "SAML Providers from Metadata"
|
||||
msgstr "来自元数据的 SAML 提供程序"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "Default"
|
||||
msgstr "默认"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "AWS"
|
||||
msgstr "AWS"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "Slack"
|
||||
msgstr "Slack"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "Base URL to SCIM requests, usually ends in /v2"
|
||||
msgstr "SCIM 请求的基础 URL,通常以 /v2 结尾"
|
||||
@ -1929,14 +1917,6 @@ msgstr "SCIM 请求的基础 URL,通常以 /v2 结尾"
|
||||
msgid "Authentication token"
|
||||
msgstr "身份验证令牌"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "SCIM Compatibility Mode"
|
||||
msgstr "SCIM 兼容模式"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "Alter authentik behavior for vendor-specific SCIM implementations."
|
||||
msgstr "更改 authentik 的行为,以兼容特定厂商的 SCIM 实现。"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "SCIM Provider"
|
||||
msgstr "SCIM 提供程序"
|
||||
@ -2591,7 +2571,6 @@ msgid ""
|
||||
msgstr "启用后,将使用全局电子邮件连接设置,下面的连接设置将被忽略。"
|
||||
|
||||
#: authentik/stages/authenticator_email/models.py
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Time the token sent is valid (Format: hours=3,minutes=17,seconds=300)."
|
||||
msgstr "发出令牌有效的时间(格式:hours=3,minutes=17,seconds=300)。"
|
||||
|
||||
@ -2941,6 +2920,10 @@ msgstr "账户确认"
|
||||
msgid "Activate users upon completion of stage."
|
||||
msgstr "完成阶段后激活用户。"
|
||||
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Time in minutes the token sent is valid."
|
||||
msgstr "发出令牌的有效时间(单位为分钟)。"
|
||||
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Email Stage"
|
||||
msgstr "电子邮件阶段"
|
||||
|
Binary file not shown.
@ -6,7 +6,7 @@
|
||||
# Translators:
|
||||
# Chen Zhikai, 2022
|
||||
# 刘松, 2022
|
||||
# Jens L. <jens@goauthentik.io>, 2025
|
||||
# Jens L. <jens@goauthentik.io>, 2024
|
||||
# deluxghost, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
@ -14,7 +14,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-03-13 00:10+0000\n"
|
||||
"POT-Creation-Date: 2025-03-02 00:10+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: deluxghost, 2025\n"
|
||||
"Language-Team: Chinese (China) (https://app.transifex.com/authentik/teams/119923/zh_CN/)\n"
|
||||
@ -1908,18 +1908,6 @@ msgstr "来自元数据的 SAML 提供程序"
|
||||
msgid "SAML Providers from Metadata"
|
||||
msgstr "来自元数据的 SAML 提供程序"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "Default"
|
||||
msgstr "默认"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "AWS"
|
||||
msgstr "AWS"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "Slack"
|
||||
msgstr "Slack"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "Base URL to SCIM requests, usually ends in /v2"
|
||||
msgstr "SCIM 请求的基础 URL,通常以 /v2 结尾"
|
||||
@ -1928,14 +1916,6 @@ msgstr "SCIM 请求的基础 URL,通常以 /v2 结尾"
|
||||
msgid "Authentication token"
|
||||
msgstr "身份验证令牌"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "SCIM Compatibility Mode"
|
||||
msgstr "SCIM 兼容模式"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "Alter authentik behavior for vendor-specific SCIM implementations."
|
||||
msgstr "更改 authentik 的行为,以兼容特定厂商的 SCIM 实现。"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "SCIM Provider"
|
||||
msgstr "SCIM 提供程序"
|
||||
@ -2590,7 +2570,6 @@ msgid ""
|
||||
msgstr "启用后,将使用全局电子邮件连接设置,下面的连接设置将被忽略。"
|
||||
|
||||
#: authentik/stages/authenticator_email/models.py
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Time the token sent is valid (Format: hours=3,minutes=17,seconds=300)."
|
||||
msgstr "发出令牌有效的时间(格式:hours=3,minutes=17,seconds=300)。"
|
||||
|
||||
@ -2940,6 +2919,10 @@ msgstr "账户确认"
|
||||
msgid "Activate users upon completion of stage."
|
||||
msgstr "完成阶段后激活用户。"
|
||||
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Time in minutes the token sent is valid."
|
||||
msgstr "发出令牌的有效时间(单位为分钟)。"
|
||||
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Email Stage"
|
||||
msgstr "电子邮件阶段"
|
||||
|
169
poetry.lock
generated
169
poetry.lock
generated
@ -358,18 +358,18 @@ visualize = ["Twisted (>=16.1.1)", "graphviz (>0.5.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "aws-cdk-asset-awscli-v1"
|
||||
version = "2.2.227"
|
||||
version = "2.2.212"
|
||||
description = "A library that contains the AWS CLI for use in Lambda Layers"
|
||||
optional = false
|
||||
python-versions = "~=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "aws_cdk.asset_awscli_v1-2.2.227-py3-none-any.whl", hash = "sha256:5160cd515d94d0da252806cd853a3e861e1b76aa553e1c90aec2bd712fa3df1b"},
|
||||
{file = "aws_cdk_asset_awscli_v1-2.2.227.tar.gz", hash = "sha256:0fa4cf382e712121b8bbe11532854018abdba19964ded9c0aa9aace4383816b6"},
|
||||
{file = "aws_cdk.asset_awscli_v1-2.2.212-py3-none-any.whl", hash = "sha256:12161e2d528698957bc2c0f53d2f5e81de54f8ad0e4b94316634bdc1db50f539"},
|
||||
{file = "aws_cdk_asset_awscli_v1-2.2.212.tar.gz", hash = "sha256:3a4374562f37c9cd3f59cb45173a18ef0f781c0f1df187773662a1dd14cc18fd"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
jsii = ">=1.108.0,<2.0.0"
|
||||
jsii = ">=1.105.0,<2.0.0"
|
||||
publication = ">=0.0.3"
|
||||
typeguard = ">=2.13.3,<4.3.0"
|
||||
|
||||
@ -409,22 +409,22 @@ typeguard = ">=2.13.3,<4.3.0"
|
||||
|
||||
[[package]]
|
||||
name = "aws-cdk-lib"
|
||||
version = "2.184.0"
|
||||
version = "2.182.0"
|
||||
description = "Version 2 of the AWS Cloud Development Kit library"
|
||||
optional = false
|
||||
python-versions = "~=3.9"
|
||||
python-versions = "~=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "aws_cdk_lib-2.184.0-py3-none-any.whl", hash = "sha256:b714691e5a53d7d6dc3ca9a637c603921ccb31d31ec5cc206c96ae132be410ca"},
|
||||
{file = "aws_cdk_lib-2.184.0.tar.gz", hash = "sha256:40b26e3eb7de23260c74a7cbbf8345104bb28301586859d2c5ea5cce108db9c0"},
|
||||
{file = "aws_cdk_lib-2.182.0-py3-none-any.whl", hash = "sha256:73b46fb789c7fe138f5ec15afa23c588fee706827edd056dd5fa8571e3d725dd"},
|
||||
{file = "aws_cdk_lib-2.182.0.tar.gz", hash = "sha256:907a2969d7c48605f597b47e47adb0b58ac17bb8de71d4a97761513c76cb3aa8"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
"aws-cdk.asset-awscli-v1" = ">=2.2.227,<3.0.0"
|
||||
"aws-cdk.asset-awscli-v1" = ">=2.2.208,<3.0.0"
|
||||
"aws-cdk.asset-node-proxy-agent-v6" = ">=2.1.0,<3.0.0"
|
||||
"aws-cdk.cloud-assembly-schema" = ">=40.7.0,<41.0.0"
|
||||
"aws-cdk.cloud-assembly-schema" = ">=40.6.0,<41.0.0"
|
||||
constructs = ">=10.0.0,<11.0.0"
|
||||
jsii = ">=1.109.0,<2.0.0"
|
||||
jsii = ">=1.106.0,<2.0.0"
|
||||
publication = ">=0.0.3"
|
||||
typeguard = ">=2.13.3,<4.3.0"
|
||||
|
||||
@ -1406,14 +1406,14 @@ dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"]
|
||||
|
||||
[[package]]
|
||||
name = "django"
|
||||
version = "5.0.13"
|
||||
version = "5.0.12"
|
||||
description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main", "dev"]
|
||||
files = [
|
||||
{file = "Django-5.0.13-py3-none-any.whl", hash = "sha256:b983238dfa2eb2e6b27ebb815e14f0741cf186606eb7bcd857e740174017c50e"},
|
||||
{file = "Django-5.0.13.tar.gz", hash = "sha256:f9d4b7b87a9dae248d5f20cec940cf7290e07d508d6d8432e3c2cabf09b3b0ff"},
|
||||
{file = "Django-5.0.12-py3-none-any.whl", hash = "sha256:3566604af111f586a1c9d49cb14ba6c607a0ccbbf87f57d98872cd8aae7d48ad"},
|
||||
{file = "Django-5.0.12.tar.gz", hash = "sha256:05097ea026cceb2db4db0655ecf77cc96b0753ac6a367280e458e603f6556f53"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -1611,7 +1611,7 @@ Django = ">=2.1,<5.1"
|
||||
type = "git"
|
||||
url = "https://github.com/rissson/django-tenants.git"
|
||||
reference = "authentik-fixes"
|
||||
resolved_reference = "156e53a6f5902d74b73dd9d0192fffaa2587a740"
|
||||
resolved_reference = "a7f37c53f62f355a00142473ff1e3451bb794eca"
|
||||
|
||||
[[package]]
|
||||
name = "djangorestframework"
|
||||
@ -2033,14 +2033,14 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"]
|
||||
|
||||
[[package]]
|
||||
name = "google-api-python-client"
|
||||
version = "2.164.0"
|
||||
version = "2.163.0"
|
||||
description = "Google API Client Library for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "google_api_python_client-2.164.0-py2.py3-none-any.whl", hash = "sha256:b2037c3d280793c8d5180b04317b16be4acd5f77af5dfa7213ace32d140a9ffe"},
|
||||
{file = "google_api_python_client-2.164.0.tar.gz", hash = "sha256:116f5a05dfb95ed7f7ea0d0f561fc5464146709c583226cc814690f9bb221492"},
|
||||
{file = "google_api_python_client-2.163.0-py2.py3-none-any.whl", hash = "sha256:080e8bc0669cb4c1fb8efb8da2f5b91a2625d8f0e7796cfad978f33f7016c6c4"},
|
||||
{file = "google_api_python_client-2.163.0.tar.gz", hash = "sha256:88dee87553a2d82176e2224648bf89272d536c8f04dcdda37ef0a71473886dd7"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -2383,14 +2383,14 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "importlib-metadata"
|
||||
version = "8.6.1"
|
||||
version = "8.5.0"
|
||||
description = "Read metadata from Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main", "dev"]
|
||||
files = [
|
||||
{file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"},
|
||||
{file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"},
|
||||
{file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"},
|
||||
{file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -2402,7 +2402,7 @@ cover = ["pytest-cov"]
|
||||
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
|
||||
enabler = ["pytest-enabler (>=2.2)"]
|
||||
perf = ["ipython"]
|
||||
test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"]
|
||||
test = ["flufl.flake8", "importlib-resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"]
|
||||
type = ["pytest-mypy"]
|
||||
|
||||
[[package]]
|
||||
@ -2495,14 +2495,14 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "jsii"
|
||||
version = "1.109.0"
|
||||
version = "1.108.0"
|
||||
description = "Python client for jsii runtime"
|
||||
optional = false
|
||||
python-versions = "~=3.9"
|
||||
python-versions = "~=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "jsii-1.109.0-py3-none-any.whl", hash = "sha256:100bb48c7f74b8e22b3182c5466db1c32565ddb681ed5e2bf556076a734d3f07"},
|
||||
{file = "jsii-1.109.0.tar.gz", hash = "sha256:85e0deca8089e2918776541e986d5abab90a66d4330eedfc14e8a060dd507bad"},
|
||||
{file = "jsii-1.108.0-py3-none-any.whl", hash = "sha256:d6c99671ab44520069ad6198e3b07379ae9c075bcb53b8a16455c1beb10288ea"},
|
||||
{file = "jsii-1.108.0.tar.gz", hash = "sha256:f1053a414ac117c6ecae7208c5ca4cb6d10ca3420c69e30f8b9cca64cc37e61b"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -3265,14 +3265,14 @@ dev = ["bumpver", "isort", "mypy", "pylint", "pytest", "yapf"]
|
||||
|
||||
[[package]]
|
||||
name = "msgraph-sdk"
|
||||
version = "1.24.0"
|
||||
version = "1.23.0"
|
||||
description = "The Microsoft Graph Python SDK"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "msgraph_sdk-1.24.0-py3-none-any.whl", hash = "sha256:7cd2dd95c3e2b4d565fe11b019d26102723d252022b53dc854697378dc983538"},
|
||||
{file = "msgraph_sdk-1.24.0.tar.gz", hash = "sha256:d401160cfdab239867faf3b7e6984632dd11524d893a0c816db2d5a64adda650"},
|
||||
{file = "msgraph_sdk-1.23.0-py3-none-any.whl", hash = "sha256:58e0047b4ca59fd82022c02cd73fec0170a3d84f3b76721e3db2a0314df9a58a"},
|
||||
{file = "msgraph_sdk-1.23.0.tar.gz", hash = "sha256:6dd1ba9a46f5f0ce8599fd9610133adbd9d1493941438b5d3632fce9e55ed607"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -3448,52 +3448,52 @@ resolved_reference = "20d69d9cc50a0fef31605b46f06da0c94f1ec3cf"
|
||||
|
||||
[[package]]
|
||||
name = "opentelemetry-api"
|
||||
version = "1.31.0"
|
||||
version = "1.28.0"
|
||||
description = "OpenTelemetry Python API"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "opentelemetry_api-1.31.0-py3-none-any.whl", hash = "sha256:145b72c6c16977c005c568ec32f4946054ab793d8474a17fd884b0397582c5f2"},
|
||||
{file = "opentelemetry_api-1.31.0.tar.gz", hash = "sha256:d8da59e83e8e3993b4726e4c1023cd46f57c4d5a73142e239247e7d814309de1"},
|
||||
{file = "opentelemetry_api-1.28.0-py3-none-any.whl", hash = "sha256:8457cd2c59ea1bd0988560f021656cecd254ad7ef6be4ba09dbefeca2409ce52"},
|
||||
{file = "opentelemetry_api-1.28.0.tar.gz", hash = "sha256:578610bcb8aa5cdcb11169d136cc752958548fb6ccffb0969c1036b0ee9e5353"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
deprecated = ">=1.2.6"
|
||||
importlib-metadata = ">=6.0,<8.7.0"
|
||||
importlib-metadata = ">=6.0,<=8.5.0"
|
||||
|
||||
[[package]]
|
||||
name = "opentelemetry-sdk"
|
||||
version = "1.31.0"
|
||||
version = "1.28.0"
|
||||
description = "OpenTelemetry Python SDK"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "opentelemetry_sdk-1.31.0-py3-none-any.whl", hash = "sha256:97c9a03865e69723725fb64fe04343a488c3e61e684eb804bd7d6da2215dfc60"},
|
||||
{file = "opentelemetry_sdk-1.31.0.tar.gz", hash = "sha256:452d7d5b3c1db2e5e4cb64abede0ddd20690cb244a559c73a59652fdf6726070"},
|
||||
{file = "opentelemetry_sdk-1.28.0-py3-none-any.whl", hash = "sha256:4b37da81d7fad67f6683c4420288c97f4ed0d988845d5886435f428ec4b8429a"},
|
||||
{file = "opentelemetry_sdk-1.28.0.tar.gz", hash = "sha256:41d5420b2e3fb7716ff4981b510d551eff1fc60eb5a95cf7335b31166812a893"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
opentelemetry-api = "1.31.0"
|
||||
opentelemetry-semantic-conventions = "0.52b0"
|
||||
opentelemetry-api = "1.28.0"
|
||||
opentelemetry-semantic-conventions = "0.49b0"
|
||||
typing-extensions = ">=3.7.4"
|
||||
|
||||
[[package]]
|
||||
name = "opentelemetry-semantic-conventions"
|
||||
version = "0.52b0"
|
||||
version = "0.49b0"
|
||||
description = "OpenTelemetry Semantic Conventions"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "opentelemetry_semantic_conventions-0.52b0-py3-none-any.whl", hash = "sha256:4d843652ae1f9f3c0d4d8df0bfef740627c90495ac043fc33f0a04bad3b606e2"},
|
||||
{file = "opentelemetry_semantic_conventions-0.52b0.tar.gz", hash = "sha256:f8bc8873a69d0a2f45746c31980baad2bb10ccee16b1816497ccf99417770386"},
|
||||
{file = "opentelemetry_semantic_conventions-0.49b0-py3-none-any.whl", hash = "sha256:0458117f6ead0b12e3221813e3e511d85698c31901cac84682052adb9c17c7cd"},
|
||||
{file = "opentelemetry_semantic_conventions-0.49b0.tar.gz", hash = "sha256:dbc7b28339e5390b6b28e022835f9bac4e134a80ebf640848306d3c5192557e8"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
deprecated = ">=1.2.6"
|
||||
opentelemetry-api = "1.31.0"
|
||||
opentelemetry-api = "1.28.0"
|
||||
|
||||
[[package]]
|
||||
name = "orjson"
|
||||
@ -3880,24 +3880,24 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "psycopg"
|
||||
version = "3.2.6"
|
||||
version = "3.2.5"
|
||||
description = "PostgreSQL database adapter for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "psycopg-3.2.6-py3-none-any.whl", hash = "sha256:f3ff5488525890abb0566c429146add66b329e20d6d4835662b920cbbf90ac58"},
|
||||
{file = "psycopg-3.2.6.tar.gz", hash = "sha256:16fa094efa2698f260f2af74f3710f781e4a6f226efe9d1fd0c37f384639ed8a"},
|
||||
{file = "psycopg-3.2.5-py3-none-any.whl", hash = "sha256:b782130983e5b3de30b4c529623d3687033b4dafa05bb661fc6bf45837ca5879"},
|
||||
{file = "psycopg-3.2.5.tar.gz", hash = "sha256:f5f750611c67cb200e85b408882f29265c66d1de7f813add4f8125978bfd70e8"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
psycopg-c = {version = "3.2.6", optional = true, markers = "implementation_name != \"pypy\" and extra == \"c\""}
|
||||
psycopg-c = {version = "3.2.5", optional = true, markers = "implementation_name != \"pypy\" and extra == \"c\""}
|
||||
typing-extensions = {version = ">=4.6", markers = "python_version < \"3.13\""}
|
||||
tzdata = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
|
||||
[package.extras]
|
||||
binary = ["psycopg-binary (==3.2.6) ; implementation_name != \"pypy\""]
|
||||
c = ["psycopg-c (==3.2.6) ; implementation_name != \"pypy\""]
|
||||
binary = ["psycopg-binary (==3.2.5) ; implementation_name != \"pypy\""]
|
||||
c = ["psycopg-c (==3.2.5) ; implementation_name != \"pypy\""]
|
||||
dev = ["ast-comments (>=1.1.2)", "black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "isort-psycopg", "isort[colors] (>=6.0)", "mypy (>=1.14)", "pre-commit (>=4.0.1)", "types-setuptools (>=57.4)", "wheel (>=0.37)"]
|
||||
docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"]
|
||||
pool = ["psycopg-pool"]
|
||||
@ -3905,14 +3905,14 @@ test = ["anyio (>=4.0)", "mypy (>=1.14)", "pproxy (>=2.7)", "pytest (>=6.2.5)",
|
||||
|
||||
[[package]]
|
||||
name = "psycopg-c"
|
||||
version = "3.2.6"
|
||||
version = "3.2.5"
|
||||
description = "PostgreSQL database adapter for Python -- C optimisation distribution"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
markers = "implementation_name != \"pypy\""
|
||||
files = [
|
||||
{file = "psycopg_c-3.2.6.tar.gz", hash = "sha256:b5fd4ce70f82766a122ca5076a36c4d5818eaa9df9bf76870bc83a064ffaed3a"},
|
||||
{file = "psycopg_c-3.2.5.tar.gz", hash = "sha256:57ad4cfd28de278c424aaceb1f2ad5c7910466e315dfe84e403f3c7a0a2ce81b"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4185,19 +4185,18 @@ tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "pyopenssl"
|
||||
version = "25.0.0"
|
||||
version = "24.3.0"
|
||||
description = "Python wrapper module around the OpenSSL library"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main", "dev"]
|
||||
files = [
|
||||
{file = "pyOpenSSL-25.0.0-py3-none-any.whl", hash = "sha256:424c247065e46e76a37411b9ab1782541c23bb658bf003772c3405fbaa128e90"},
|
||||
{file = "pyopenssl-25.0.0.tar.gz", hash = "sha256:cd2cef799efa3936bb08e8ccb9433a575722b9dd986023f1cabc4ae64e9dac16"},
|
||||
{file = "pyOpenSSL-24.3.0-py3-none-any.whl", hash = "sha256:e474f5a473cd7f92221cc04976e48f4d11502804657a08a989fb3be5514c904a"},
|
||||
{file = "pyopenssl-24.3.0.tar.gz", hash = "sha256:49f7a019577d834746bc55c5fce6ecbcec0f2b4ec5ce1cf43a9a173b8138bb36"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
cryptography = ">=41.0.5,<45"
|
||||
typing-extensions = {version = ">=4.9", markers = "python_version < \"3.13\" and python_version >= \"3.8\""}
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx (!=5.2.0,!=5.2.0.post0,!=7.2.5)", "sphinx_rtd_theme"]
|
||||
@ -4749,30 +4748,30 @@ pyasn1 = ">=0.1.3"
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.9.10"
|
||||
version = "0.9.9"
|
||||
description = "An extremely fast Python linter and code formatter, written in Rust."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "ruff-0.9.10-py3-none-linux_armv6l.whl", hash = "sha256:eb4d25532cfd9fe461acc83498361ec2e2252795b4f40b17e80692814329e42d"},
|
||||
{file = "ruff-0.9.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:188a6638dab1aa9bb6228a7302387b2c9954e455fb25d6b4470cb0641d16759d"},
|
||||
{file = "ruff-0.9.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5284dcac6b9dbc2fcb71fdfc26a217b2ca4ede6ccd57476f52a587451ebe450d"},
|
||||
{file = "ruff-0.9.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47678f39fa2a3da62724851107f438c8229a3470f533894b5568a39b40029c0c"},
|
||||
{file = "ruff-0.9.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99713a6e2766b7a17147b309e8c915b32b07a25c9efd12ada79f217c9c778b3e"},
|
||||
{file = "ruff-0.9.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524ee184d92f7c7304aa568e2db20f50c32d1d0caa235d8ddf10497566ea1a12"},
|
||||
{file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:df92aeac30af821f9acf819fc01b4afc3dfb829d2782884f8739fb52a8119a16"},
|
||||
{file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de42e4edc296f520bb84954eb992a07a0ec5a02fecb834498415908469854a52"},
|
||||
{file = "ruff-0.9.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d257f95b65806104b6b1ffca0ea53f4ef98454036df65b1eda3693534813ecd1"},
|
||||
{file = "ruff-0.9.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60dec7201c0b10d6d11be00e8f2dbb6f40ef1828ee75ed739923799513db24c"},
|
||||
{file = "ruff-0.9.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d838b60007da7a39c046fcdd317293d10b845001f38bcb55ba766c3875b01e43"},
|
||||
{file = "ruff-0.9.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ccaf903108b899beb8e09a63ffae5869057ab649c1e9231c05ae354ebc62066c"},
|
||||
{file = "ruff-0.9.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f9567d135265d46e59d62dc60c0bfad10e9a6822e231f5b24032dba5a55be6b5"},
|
||||
{file = "ruff-0.9.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5f202f0d93738c28a89f8ed9eaba01b7be339e5d8d642c994347eaa81c6d75b8"},
|
||||
{file = "ruff-0.9.10-py3-none-win32.whl", hash = "sha256:bfb834e87c916521ce46b1788fbb8484966e5113c02df216680102e9eb960029"},
|
||||
{file = "ruff-0.9.10-py3-none-win_amd64.whl", hash = "sha256:f2160eeef3031bf4b17df74e307d4c5fb689a6f3a26a2de3f7ef4044e3c484f1"},
|
||||
{file = "ruff-0.9.10-py3-none-win_arm64.whl", hash = "sha256:5fd804c0327a5e5ea26615550e706942f348b197d5475ff34c19733aee4b2e69"},
|
||||
{file = "ruff-0.9.10.tar.gz", hash = "sha256:9bacb735d7bada9cfb0f2c227d3658fc443d90a727b47f206fb33f52f3c0eac7"},
|
||||
{file = "ruff-0.9.9-py3-none-linux_armv6l.whl", hash = "sha256:628abb5ea10345e53dff55b167595a159d3e174d6720bf19761f5e467e68d367"},
|
||||
{file = "ruff-0.9.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b6cd1428e834b35d7493354723543b28cc11dc14d1ce19b685f6e68e07c05ec7"},
|
||||
{file = "ruff-0.9.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5ee162652869120ad260670706f3cd36cd3f32b0c651f02b6da142652c54941d"},
|
||||
{file = "ruff-0.9.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3aa0f6b75082c9be1ec5a1db78c6d4b02e2375c3068438241dc19c7c306cc61a"},
|
||||
{file = "ruff-0.9.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:584cc66e89fb5f80f84b05133dd677a17cdd86901d6479712c96597a3f28e7fe"},
|
||||
{file = "ruff-0.9.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf3369325761a35aba75cd5c55ba1b5eb17d772f12ab168fbfac54be85cf18c"},
|
||||
{file = "ruff-0.9.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3403a53a32a90ce929aa2f758542aca9234befa133e29f4933dcef28a24317be"},
|
||||
{file = "ruff-0.9.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:18454e7fa4e4d72cffe28a37cf6a73cb2594f81ec9f4eca31a0aaa9ccdfb1590"},
|
||||
{file = "ruff-0.9.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fadfe2c88724c9617339f62319ed40dcdadadf2888d5afb88bf3adee7b35bfb"},
|
||||
{file = "ruff-0.9.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6df104d08c442a1aabcfd254279b8cc1e2cbf41a605aa3e26610ba1ec4acf0b0"},
|
||||
{file = "ruff-0.9.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d7c62939daf5b2a15af48abbd23bea1efdd38c312d6e7c4cedf5a24e03207e17"},
|
||||
{file = "ruff-0.9.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9494ba82a37a4b81b6a798076e4a3251c13243fc37967e998efe4cce58c8a8d1"},
|
||||
{file = "ruff-0.9.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:4efd7a96ed6d36ef011ae798bf794c5501a514be369296c672dab7921087fa57"},
|
||||
{file = "ruff-0.9.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ab90a7944c5a1296f3ecb08d1cbf8c2da34c7e68114b1271a431a3ad30cb660e"},
|
||||
{file = "ruff-0.9.9-py3-none-win32.whl", hash = "sha256:6b4c376d929c25ecd6d87e182a230fa4377b8e5125a4ff52d506ee8c087153c1"},
|
||||
{file = "ruff-0.9.9-py3-none-win_amd64.whl", hash = "sha256:837982ea24091d4c1700ddb2f63b7070e5baec508e43b01de013dc7eff974ff1"},
|
||||
{file = "ruff-0.9.9-py3-none-win_arm64.whl", hash = "sha256:3ac78f127517209fe6d96ab00f3ba97cafe38718b23b1db3e96d8b2d39e37ddf"},
|
||||
{file = "ruff-0.9.9.tar.gz", hash = "sha256:0062ed13f22173e85f8f7056f9a24016e692efeea8704d1a5e8011b8aa850933"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5121,14 +5120,14 @@ pbr = ">=2.0.0,<2.1.0 || >2.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "structlog"
|
||||
version = "25.2.0"
|
||||
version = "25.1.0"
|
||||
description = "Structured Logging for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "structlog-25.2.0-py3-none-any.whl", hash = "sha256:0fecea2e345d5d491b72f3db2e5fcd6393abfc8cd06a4851f21fcd4d1a99f437"},
|
||||
{file = "structlog-25.2.0.tar.gz", hash = "sha256:d9f9776944207d1035b8b26072b9b140c63702fd7aa57c2f85d28ab701bd8e92"},
|
||||
{file = "structlog-25.1.0-py3-none-any.whl", hash = "sha256:843fe4f254540329f380812cbe612e1af5ec5b8172205ae634679cd35a6d6321"},
|
||||
{file = "structlog-25.1.0.tar.gz", hash = "sha256:2ef2a572e0e27f09664965d31a576afe64e46ac6084ef5cec3c2b8cd6e4e3ad3"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
@ -5229,14 +5228,14 @@ wsproto = ">=0.14"
|
||||
|
||||
[[package]]
|
||||
name = "twilio"
|
||||
version = "9.5.0"
|
||||
version = "9.4.6"
|
||||
description = "Twilio API client and TwiML generator"
|
||||
optional = false
|
||||
python-versions = ">=3.7.0"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "twilio-9.5.0-py2.py3-none-any.whl", hash = "sha256:e6d0ccf9162a83acfa6d21a02e90a22fdbc53f4269be3402ba579f13b2a259fc"},
|
||||
{file = "twilio-9.5.0.tar.gz", hash = "sha256:633d213c21b394297a27a92f20498adb1c4cd2f6fc3f4e2bfcc7d787b29fc034"},
|
||||
{file = "twilio-9.4.6-py2.py3-none-any.whl", hash = "sha256:6d7d677fa9ded4ee0c366ad0155a1e0af51e129109af603b6ec9cdc8826a5c37"},
|
||||
{file = "twilio-9.4.6.tar.gz", hash = "sha256:ff33a6c3609f4a0769d02c4eb75f7ab55ff2ba962762b076cd39ef7da56fdaa4"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -5650,21 +5649,21 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "webauthn"
|
||||
version = "2.5.2"
|
||||
version = "2.5.1"
|
||||
description = "Pythonic WebAuthn"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "webauthn-2.5.2-py3-none-any.whl", hash = "sha256:44246e496e617eb5e2f51165046b9f0197fcdf470f69cd6734061a27ba365f8e"},
|
||||
{file = "webauthn-2.5.2.tar.gz", hash = "sha256:09c13dfc1c68c810f32fa4d89b1d37acb9f9ae9091c9d7019e313be4525a95ef"},
|
||||
{file = "webauthn-2.5.1-py3-none-any.whl", hash = "sha256:86d1faa11ec26ebe49b9388d8c3d09bff4dca6c23d3c7e2dd066e99896d694f0"},
|
||||
{file = "webauthn-2.5.1.tar.gz", hash = "sha256:f1b7447bae1056e110a9e71ff287f639d05d4d14589911d75fea255c3a03aff0"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
asn1crypto = ">=1.5.1"
|
||||
cbor2 = ">=5.6.5"
|
||||
cryptography = ">=44.0.2"
|
||||
pyOpenSSL = ">=25.0.0"
|
||||
cryptography = ">=43.0.3"
|
||||
pyOpenSSL = ">=24.2.1"
|
||||
|
||||
[[package]]
|
||||
name = "websocket-client"
|
||||
|
@ -17,16 +17,11 @@ skip = [
|
||||
"go.sum",
|
||||
"locale",
|
||||
"**/dist",
|
||||
"**/storybook-static",
|
||||
"**/web/src/locales",
|
||||
"**/web/xliff",
|
||||
"./web/storybook-static",
|
||||
"./website/build",
|
||||
"./gen-ts-api",
|
||||
"./gen-py-api",
|
||||
"./gen-go-api",
|
||||
"*.api.mdx",
|
||||
"./htmlcov",
|
||||
]
|
||||
dictionary = ".github/codespell-dictionary.txt,-"
|
||||
ignore-words = ".github/codespell-words.txt"
|
||||
|
43
schema.yml
43
schema.yml
@ -35146,7 +35146,7 @@ paths:
|
||||
- in: query
|
||||
name: token_expiry
|
||||
schema:
|
||||
type: string
|
||||
type: integer
|
||||
- in: query
|
||||
name: use_global_settings
|
||||
schema:
|
||||
@ -41582,12 +41582,6 @@ components:
|
||||
- confidential
|
||||
- public
|
||||
type: string
|
||||
CompatibilityModeEnum:
|
||||
enum:
|
||||
- default
|
||||
- aws
|
||||
- slack
|
||||
type: string
|
||||
Config:
|
||||
type: object
|
||||
description: Serialize authentik Config into DRF Object
|
||||
@ -42780,8 +42774,10 @@ components:
|
||||
format: email
|
||||
maxLength: 254
|
||||
token_expiry:
|
||||
type: string
|
||||
description: 'Time the token sent is valid (Format: hours=3,minutes=17,seconds=300).'
|
||||
type: integer
|
||||
maximum: 2147483647
|
||||
minimum: -2147483648
|
||||
description: Time in minutes the token sent is valid.
|
||||
subject:
|
||||
type: string
|
||||
template:
|
||||
@ -42837,9 +42833,10 @@ components:
|
||||
minLength: 1
|
||||
maxLength: 254
|
||||
token_expiry:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: 'Time the token sent is valid (Format: hours=3,minutes=17,seconds=300).'
|
||||
type: integer
|
||||
maximum: 2147483647
|
||||
minimum: -2147483648
|
||||
description: Time in minutes the token sent is valid.
|
||||
subject:
|
||||
type: string
|
||||
minLength: 1
|
||||
@ -50392,9 +50389,10 @@ components:
|
||||
minLength: 1
|
||||
maxLength: 254
|
||||
token_expiry:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: 'Time the token sent is valid (Format: hours=3,minutes=17,seconds=300).'
|
||||
type: integer
|
||||
maximum: 2147483647
|
||||
minimum: -2147483648
|
||||
description: Time in minutes the token sent is valid.
|
||||
subject:
|
||||
type: string
|
||||
minLength: 1
|
||||
@ -52447,11 +52445,6 @@ components:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Authentication token
|
||||
compatibility_mode:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/CompatibilityModeEnum'
|
||||
title: SCIM Compatibility Mode
|
||||
description: Alter authentik behavior for vendor-specific SCIM implementations.
|
||||
exclude_users_service_account:
|
||||
type: boolean
|
||||
filter_group:
|
||||
@ -55852,11 +55845,6 @@ components:
|
||||
token:
|
||||
type: string
|
||||
description: Authentication token
|
||||
compatibility_mode:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/CompatibilityModeEnum'
|
||||
title: SCIM Compatibility Mode
|
||||
description: Alter authentik behavior for vendor-specific SCIM implementations.
|
||||
exclude_users_service_account:
|
||||
type: boolean
|
||||
filter_group:
|
||||
@ -55947,11 +55935,6 @@ components:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Authentication token
|
||||
compatibility_mode:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/CompatibilityModeEnum'
|
||||
title: SCIM Compatibility Mode
|
||||
description: Alter authentik behavior for vendor-specific SCIM implementations.
|
||||
exclude_users_service_account:
|
||||
type: boolean
|
||||
filter_group:
|
||||
|
22
web/package-lock.json
generated
22
web/package-lock.json
generated
@ -23,7 +23,7 @@
|
||||
"@floating-ui/dom": "^1.6.11",
|
||||
"@formatjs/intl-listformat": "^7.5.7",
|
||||
"@fortawesome/fontawesome-free": "^6.6.0",
|
||||
"@goauthentik/api": "^2025.2.1-1741798605",
|
||||
"@goauthentik/api": "^2025.2.1-1740858273",
|
||||
"@lit-labs/ssr": "^3.2.2",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@lit/localize": "^0.12.2",
|
||||
@ -826,10 +826,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime-corejs3": {
|
||||
"version": "7.26.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.26.10.tgz",
|
||||
"integrity": "sha512-uITFQYO68pMEYR46AHgQoyBg7KPPJDAbGn4jUTIRgCFJIp88MIBUianVOplhZDEec07bp9zIyr4Kp0FCyQzmWg==",
|
||||
"license": "MIT",
|
||||
"version": "7.25.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.25.7.tgz",
|
||||
"integrity": "sha512-gMmIEhg35sXk9Te5qbGp3W9YKrvLt3HV658/d3odWrHSqT0JeG5OzsJWFHRLiOohRyjRsJc/x03DhJm3i8VJxg==",
|
||||
"dependencies": {
|
||||
"core-js-pure": "^3.30.2",
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
@ -1817,9 +1816,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@goauthentik/api": {
|
||||
"version": "2025.2.1-1741798605",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2025.2.1-1741798605.tgz",
|
||||
"integrity": "sha512-Go0Iij1q/imohOSqxj43pvju8D+OFH7iNBBg6FO1ytd9pcHi1QY7/jq1vy1HKWZw7oZ2fY6hZnSe+keRnBA0Fg=="
|
||||
"version": "2025.2.1-1740858273",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2025.2.1-1740858273.tgz",
|
||||
"integrity": "sha512-dOY32RKJSy3fYteuL4h13NaRVznq0O9Z0IXdwUDwkCQ4GoBfqUaPIFb55tFvRNrdNZG0efor0PIGISxoHpRJwA=="
|
||||
},
|
||||
"node_modules/@goauthentik/web": {
|
||||
"resolved": "",
|
||||
@ -18235,10 +18234,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prismjs": {
|
||||
"version": "1.30.0",
|
||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
|
||||
"integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==",
|
||||
"license": "MIT",
|
||||
"version": "1.29.0",
|
||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz",
|
||||
"integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
"@floating-ui/dom": "^1.6.11",
|
||||
"@formatjs/intl-listformat": "^7.5.7",
|
||||
"@fortawesome/fontawesome-free": "^6.6.0",
|
||||
"@goauthentik/api": "^2025.2.1-1741798605",
|
||||
"@goauthentik/api": "^2025.2.1-1740858273",
|
||||
"@lit-labs/ssr": "^3.2.2",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@lit/localize": "^0.12.2",
|
||||
|
@ -28,7 +28,6 @@ import { when } from "lit/directives/when.js";
|
||||
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||
import PFDivider from "@patternfly/patternfly/components/Divider/divider.css";
|
||||
import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
||||
import PFFlex from "@patternfly/patternfly/layouts/Flex/flex.css";
|
||||
import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
@ -55,7 +54,6 @@ export class AdminOverviewPage extends AdminOverviewBase {
|
||||
return [
|
||||
PFBase,
|
||||
PFGrid,
|
||||
PFFlex,
|
||||
PFPage,
|
||||
PFContent,
|
||||
PFDivider,
|
||||
@ -69,6 +67,13 @@ export class AdminOverviewPage extends AdminOverviewBase {
|
||||
.card-container {
|
||||
max-height: 10em;
|
||||
}
|
||||
.ak-external-link {
|
||||
display: inline-block;
|
||||
margin-left: 0.175rem;
|
||||
vertical-align: super;
|
||||
line-height: normal;
|
||||
font-size: var(--pf-global--icon--FontSize--sm);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
@ -94,34 +99,43 @@ export class AdminOverviewPage extends AdminOverviewBase {
|
||||
return html`<ak-page-header description=${msg("General system status")} ?hasIcon=${false}>
|
||||
<span slot="header"> ${msg(str`Welcome, ${name || ""}.`)} </span>
|
||||
</ak-page-header>
|
||||
|
||||
<section class="pf-c-page__main-section">
|
||||
<div class="pf-l-grid pf-m-gutter">
|
||||
${this.renderCards()}
|
||||
<div class="pf-l-grid__item pf-m-9-col pf-m-3-row">
|
||||
<!-- row 1 -->
|
||||
<div
|
||||
class="pf-l-grid__item pf-m-12-col pf-m-6-col-on-xl pf-m-6-col-on-2xl pf-l-grid pf-m-gutter"
|
||||
>
|
||||
<div class="pf-l-grid__item pf-m-12-col pf-m-6-col-on-xl pf-m-4-col-on-2xl">
|
||||
<ak-quick-actions-card .actions=${this.quickActions}>
|
||||
</ak-quick-actions-card>
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-m-12-col pf-m-6-col-on-xl pf-m-4-col-on-2xl">
|
||||
<ak-aggregate-card
|
||||
icon="pf-icon pf-icon-zone"
|
||||
header=${msg("Outpost status")}
|
||||
headerLink="#/outpost/outposts"
|
||||
>
|
||||
<ak-admin-status-chart-outpost></ak-admin-status-chart-outpost>
|
||||
</ak-aggregate-card>
|
||||
</div>
|
||||
<div
|
||||
class="pf-l-grid__item pf-m-12-col pf-m-12-col-on-xl pf-m-4-col-on-2xl"
|
||||
>
|
||||
<ak-aggregate-card icon="fa fa-sync-alt" header=${msg("Sync status")}>
|
||||
<ak-admin-status-chart-sync></ak-admin-status-chart-sync>
|
||||
</ak-aggregate-card>
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-m-12-col">
|
||||
<hr class="pf-c-divider" />
|
||||
</div>
|
||||
${this.renderCards()}
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-m-12-col pf-m-6-col-on-xl">
|
||||
<ak-recent-events pageSize="6"></ak-recent-events>
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-m-6-col pf-m-3-col-on-md pf-m-3-col-on-xl">
|
||||
<ak-quick-actions-card .actions=${this.quickActions}>
|
||||
</ak-quick-actions-card>
|
||||
<div class="pf-l-grid__item pf-m-12-col">
|
||||
<hr class="pf-c-divider" />
|
||||
</div>
|
||||
|
||||
<div class="pf-l-grid__item pf-m-6-col pf-m-3-col-on-md pf-m-3-col-on-xl">
|
||||
<ak-aggregate-card
|
||||
icon="pf-icon pf-icon-zone"
|
||||
header=${msg("Outpost status")}
|
||||
headerLink="#/outpost/outposts"
|
||||
>
|
||||
<ak-admin-status-chart-outpost></ak-admin-status-chart-outpost>
|
||||
</ak-aggregate-card>
|
||||
</div>
|
||||
|
||||
<div class="pf-l-grid__item pf-m-6-col pf-m-3-col-on-md pf-m-3-col-on-xl">
|
||||
<ak-aggregate-card icon="fa fa-sync-alt" header=${msg("Sync status")}>
|
||||
<ak-admin-status-chart-sync></ak-admin-status-chart-sync>
|
||||
</ak-aggregate-card>
|
||||
</div>
|
||||
|
||||
<!-- row 3 -->
|
||||
<div
|
||||
class="pf-l-grid__item pf-m-12-col pf-m-6-col-on-xl pf-m-8-col-on-2xl big-graph-container"
|
||||
|
@ -1,15 +1,10 @@
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import { PFSize } from "@goauthentik/common/enums.js";
|
||||
import {
|
||||
APIError,
|
||||
parseAPIResponseError,
|
||||
pluckErrorDetail,
|
||||
} from "@goauthentik/common/errors/network";
|
||||
import { AggregateCard } from "@goauthentik/elements/cards/AggregateCard";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { PropertyValues, TemplateResult, html, nothing } from "lit";
|
||||
import { state } from "lit/decorators.js";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { until } from "lit/directives/until.js";
|
||||
|
||||
import { ResponseError } from "@goauthentik/api";
|
||||
|
||||
@ -18,143 +13,46 @@ export interface AdminStatus {
|
||||
message?: TemplateResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract base class for admin status cards with robust state management
|
||||
*
|
||||
* @template T - Type of the primary data value used in the card
|
||||
*/
|
||||
export abstract class AdminStatusCard<T> extends AggregateCard {
|
||||
// Current data value state
|
||||
@state()
|
||||
value?: T;
|
||||
|
||||
// Current status state derived from value
|
||||
@state()
|
||||
protected status?: AdminStatus;
|
||||
|
||||
// Current error state if any request fails
|
||||
@state()
|
||||
protected error?: APIError;
|
||||
|
||||
// Abstract methods to be implemented by subclasses
|
||||
abstract getPrimaryValue(): Promise<T>;
|
||||
|
||||
abstract getStatus(value: T): Promise<AdminStatus>;
|
||||
|
||||
value?: T;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
// Proper binding for event handler
|
||||
this.fetchData = this.fetchData.bind(this);
|
||||
// Register refresh event listener
|
||||
this.addEventListener(EVENT_REFRESH, this.fetchData);
|
||||
this.addEventListener(EVENT_REFRESH, () => {
|
||||
this.requestUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
// Lifecycle method: Called when component is added to DOM
|
||||
connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
// Initial data fetch
|
||||
this.fetchData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch primary data and handle errors
|
||||
*/
|
||||
private fetchData() {
|
||||
this.getPrimaryValue()
|
||||
.then((value: T) => {
|
||||
this.value = value; // Triggers shouldUpdate
|
||||
this.error = undefined;
|
||||
})
|
||||
.catch(async (error) => {
|
||||
this.status = undefined;
|
||||
this.error = await parseAPIResponseError(error);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Lit lifecycle method: Determine if component should update
|
||||
*
|
||||
* @param changed - Map of changed properties
|
||||
* @returns boolean indicating if update should proceed
|
||||
*/
|
||||
shouldUpdate(changed: PropertyValues<this>) {
|
||||
if (changed.has("value") && this.value !== undefined) {
|
||||
// When value changes, fetch new status
|
||||
this.getStatus(this.value)
|
||||
.then((status) => {
|
||||
this.status = status;
|
||||
this.error = undefined;
|
||||
})
|
||||
.catch(async (error: ResponseError) => {
|
||||
this.status = undefined;
|
||||
this.error = await parseAPIResponseError(error);
|
||||
});
|
||||
|
||||
// Prevent immediate re-render if only value changed
|
||||
if (changed.size === 1) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the primary value display
|
||||
*
|
||||
* @returns TemplateResult displaying the value
|
||||
*/
|
||||
protected renderValue(): TemplateResult {
|
||||
renderValue(): TemplateResult {
|
||||
return html`${this.value}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render status state
|
||||
*
|
||||
* @param status - AdminStatus object containing icon and message
|
||||
* @returns TemplateResult for status display
|
||||
*/
|
||||
private renderStatus(status: AdminStatus): TemplateResult {
|
||||
return html`
|
||||
<p><i class="${status.icon}"></i> ${this.renderValue()}</p>
|
||||
${status.message ? html`<p class="subtext">${status.message}</p>` : nothing}
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render error state
|
||||
*
|
||||
* @param error - Error message to display
|
||||
* @returns TemplateResult for error display
|
||||
*/
|
||||
private renderError(error: string): TemplateResult {
|
||||
return html`
|
||||
<p><i class="fa fa-times"></i> ${msg("Failed to fetch")}</p>
|
||||
<p class="subtext">${error}</p>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render loading state
|
||||
*
|
||||
* @returns TemplateResult for loading spinner
|
||||
*/
|
||||
private renderLoading(): TemplateResult {
|
||||
return html`<ak-spinner size="${PFSize.Large}"></ak-spinner>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main render method that selects appropriate state display
|
||||
*
|
||||
* @returns TemplateResult for current component state
|
||||
*/
|
||||
renderInner(): TemplateResult {
|
||||
return html`
|
||||
<p class="center-value">
|
||||
${
|
||||
this.status
|
||||
? this.renderStatus(this.status) // Status available
|
||||
: this.error
|
||||
? this.renderError(pluckErrorDetail(this.error)) // Error state
|
||||
: this.renderLoading() // Loading state
|
||||
}
|
||||
</p>
|
||||
`;
|
||||
return html`<p class="center-value">
|
||||
${until(
|
||||
this.getPrimaryValue()
|
||||
.then((v) => {
|
||||
this.value = v;
|
||||
return this.getStatus(v);
|
||||
})
|
||||
.then((status) => {
|
||||
return html`<p><i class="${status.icon}"></i> ${this.renderValue()}</p>
|
||||
${status.message
|
||||
? html`<p class="subtext">${status.message}</p>`
|
||||
: html``}`;
|
||||
})
|
||||
.catch((exc: ResponseError) => {
|
||||
return html` <p>
|
||||
<i class="fa fa-times"></i> ${exc.response.statusText}
|
||||
</p>
|
||||
<p class="subtext">${msg("Failed to fetch")}</p>`;
|
||||
}),
|
||||
html`<ak-spinner size="${PFSize.Large}"></ak-spinner>`,
|
||||
)}
|
||||
</p>`;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { EventUser, formatGeoEvent } from "@goauthentik/admin/events/utils";
|
||||
import { EventGeo, EventUser } from "@goauthentik/admin/events/utils";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EventWithContext } from "@goauthentik/common/events";
|
||||
import { actionToLabel } from "@goauthentik/common/labels";
|
||||
@ -10,7 +10,6 @@ import "@goauthentik/elements/buttons/ModalButton";
|
||||
import "@goauthentik/elements/buttons/SpinnerButton";
|
||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
|
||||
import { Table, TableColumn } from "@goauthentik/elements/table/Table";
|
||||
import { SlottedTemplateResult } from "@goauthentik/elements/types";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
@ -39,22 +38,6 @@ export class RecentEventsCard extends Table<Event> {
|
||||
return super.styles.concat(
|
||||
PFCard,
|
||||
css`
|
||||
.pf-c-table__sort.pf-m-selected {
|
||||
background-color: var(--pf-global--BackgroundColor--dark-400);
|
||||
border-block-end: var(--pf-global--BorderWidth--xl) solid var(--ak-accent);
|
||||
|
||||
.pf-c-table__button {
|
||||
--pf-c-table__sort__button__text--Color: var(--ak-accent);
|
||||
color: var(--pf-c-nav__link--m-current--Color);
|
||||
|
||||
.pf-c-table__text {
|
||||
--pf-c-table__sort__button__text--Color: var(
|
||||
--pf-c-nav__link--m-current--Color
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pf-c-card__title {
|
||||
--pf-c-card__title--FontFamily: var(
|
||||
--pf-global--FontFamily--heading--sans-serif
|
||||
@ -62,47 +45,7 @@ export class RecentEventsCard extends Table<Event> {
|
||||
--pf-c-card__title--FontSize: var(--pf-global--FontSize--md);
|
||||
--pf-c-card__title--FontWeight: var(--pf-global--FontWeight--bold);
|
||||
}
|
||||
|
||||
td[role="cell"] .ip-address {
|
||||
max-width: 18ch;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
th[role="columnheader"]:nth-child(3) {
|
||||
--pf-c-table--cell--MinWidth: fit-content;
|
||||
--pf-c-table--cell--MaxWidth: none;
|
||||
--pf-c-table--cell--Width: 1%;
|
||||
--pf-c-table--cell--Overflow: visible;
|
||||
--pf-c-table--cell--TextOverflow: clip;
|
||||
--pf-c-table--cell--WhiteSpace: nowrap;
|
||||
}
|
||||
|
||||
.group-header {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
gap: var(--pf-global--spacer--sm);
|
||||
font-weight: var(--pf-global--FontWeight--bold);
|
||||
font-size: var(--pf-global--FontSize--md);
|
||||
font-variant: all-petite-caps;
|
||||
}
|
||||
|
||||
.pf-c-table thead:not(:first-child) {
|
||||
background: hsl(0deg 0% 0% / 10%);
|
||||
|
||||
> tr {
|
||||
border-block-end: 2px solid
|
||||
var(
|
||||
--pf-c-page__header-tools--c-button--m-selected--before--BackgroundColor
|
||||
);
|
||||
font-family: var(--pf-global--FontFamily--heading--sans-serif);
|
||||
}
|
||||
}
|
||||
|
||||
tbody * {
|
||||
* {
|
||||
word-break: break-all;
|
||||
}
|
||||
`,
|
||||
@ -125,57 +68,20 @@ export class RecentEventsCard extends Table<Event> {
|
||||
</div>`;
|
||||
}
|
||||
|
||||
override groupBy(items: Event[]): [SlottedTemplateResult, Event[]][] {
|
||||
const groupedByDay = new Map<string, Event[]>();
|
||||
|
||||
for (const item of items) {
|
||||
const day = new Date(item.created);
|
||||
day.setHours(0, 0, 0, 0);
|
||||
const serializedDay = day.toISOString();
|
||||
|
||||
let dayEvents = groupedByDay.get(serializedDay);
|
||||
if (!dayEvents) {
|
||||
dayEvents = [];
|
||||
groupedByDay.set(serializedDay, dayEvents);
|
||||
}
|
||||
|
||||
dayEvents.push(item);
|
||||
}
|
||||
|
||||
return Array.from(groupedByDay, ([serializedDay, events]) => {
|
||||
const day = new Date(serializedDay);
|
||||
return [
|
||||
html` <div class="pf-c-content group-header">
|
||||
<div>${getRelativeTime(day)}</div>
|
||||
<small>${day.toLocaleDateString()}</small>
|
||||
</div>`,
|
||||
events,
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
row(item: EventWithContext): SlottedTemplateResult[] {
|
||||
row(item: EventWithContext): TemplateResult[] {
|
||||
return [
|
||||
html`<div><a href="${`#/events/log/${item.pk}`}">${actionToLabel(item.action)}</a></div>
|
||||
<small class="pf-m-monospace">${item.app}</small>`,
|
||||
<small>${item.app}</small>`,
|
||||
EventUser(item),
|
||||
|
||||
html`<time datetime="${item.created.toISOString()}" class="pf-c-content">
|
||||
<div><small>${item.created.toLocaleTimeString()}</small></div>
|
||||
</time>`,
|
||||
|
||||
html`<div class="ip-address pf-m-monospace">${item.clientIp || msg("-")}</div>
|
||||
<small class="geographic-location">${formatGeoEvent(item)}</small>`,
|
||||
|
||||
html`<div>${getRelativeTime(item.created)}</div>
|
||||
<small>${item.created.toLocaleString()}</small>`,
|
||||
html` <div>${item.clientIp || msg("-")}</div>
|
||||
<small>${EventGeo(item)}</small>`,
|
||||
html`<span>${item.brand?.name || msg("-")}</span>`,
|
||||
];
|
||||
}
|
||||
|
||||
renderEmpty(inner?: SlottedTemplateResult): TemplateResult {
|
||||
if (this.error) {
|
||||
return super.renderEmpty(inner);
|
||||
}
|
||||
|
||||
renderEmpty(): TemplateResult {
|
||||
return super.renderEmpty(
|
||||
html`<ak-empty-state header=${msg("No Events found.")}>
|
||||
<div slot="body">${msg("No matching events could be found.")}</div>
|
||||
|
@ -30,13 +30,11 @@ export class OutpostStatusChart extends AKChart<SummarizedSyncStatus[]> {
|
||||
const api = new OutpostsApi(DEFAULT_CONFIG);
|
||||
const outposts = await api.outpostsInstancesList({});
|
||||
const outpostStats: SummarizedSyncStatus[] = [];
|
||||
|
||||
await Promise.all(
|
||||
outposts.results.map(async (element) => {
|
||||
const health = await api.outpostsInstancesHealthList({
|
||||
uuid: element.pk || "",
|
||||
});
|
||||
|
||||
const singleStats: SummarizedSyncStatus = {
|
||||
unsynced: 0,
|
||||
healthy: 0,
|
||||
@ -44,11 +42,9 @@ export class OutpostStatusChart extends AKChart<SummarizedSyncStatus[]> {
|
||||
total: health.length,
|
||||
label: element.name,
|
||||
};
|
||||
|
||||
if (health.length === 0) {
|
||||
singleStats.unsynced += 1;
|
||||
}
|
||||
|
||||
health.forEach((h) => {
|
||||
if (h.versionOutdated) {
|
||||
singleStats.failed += 1;
|
||||
@ -56,14 +52,11 @@ export class OutpostStatusChart extends AKChart<SummarizedSyncStatus[]> {
|
||||
singleStats.healthy += 1;
|
||||
}
|
||||
});
|
||||
|
||||
outpostStats.push(singleStats);
|
||||
}),
|
||||
);
|
||||
|
||||
this.centerText = outposts.pagination.count.toString();
|
||||
outpostStats.sort((a, b) => a.label.localeCompare(b.label));
|
||||
|
||||
return outpostStats;
|
||||
}
|
||||
|
||||
|
@ -14,9 +14,9 @@ import { property, query } from "lit/decorators.js";
|
||||
import { ValidationError } from "@goauthentik/api";
|
||||
|
||||
import {
|
||||
ApplicationTransactionValidationError,
|
||||
type ApplicationWizardState,
|
||||
type ApplicationWizardStateUpdate,
|
||||
ExtendedValidationError,
|
||||
} from "./types";
|
||||
|
||||
export class ApplicationWizardStep extends WizardStep {
|
||||
@ -48,7 +48,7 @@ export class ApplicationWizardStep extends WizardStep {
|
||||
}
|
||||
|
||||
protected removeErrors(
|
||||
keyToDelete: keyof ApplicationTransactionValidationError,
|
||||
keyToDelete: keyof ExtendedValidationError,
|
||||
): ValidationError | undefined {
|
||||
if (!this.wizard.errors) {
|
||||
return undefined;
|
||||
|
@ -58,7 +58,7 @@ export class ApplicationWizardBindingsStep extends ApplicationWizardStep {
|
||||
get bindingsAsColumns() {
|
||||
return this.wizard.bindings.map((binding, index) => {
|
||||
const { order, enabled, timeout } = binding;
|
||||
const isSet = P.union(P.string.minLength(1), P.number);
|
||||
const isSet = P.string.minLength(1);
|
||||
const policy = match(binding)
|
||||
.with({ policy: isSet }, (v) => msg(str`Policy ${v.policyObj?.name}`))
|
||||
.with({ group: isSet }, (v) => msg(str`Group ${v.groupObj?.name}`))
|
||||
|
@ -1,7 +1,7 @@
|
||||
import "@goauthentik/admin/applications/wizard/ak-wizard-title.js";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import { parseAPIResponseError } from "@goauthentik/common/errors/network";
|
||||
import { parseAPIError } from "@goauthentik/common/errors";
|
||||
import { WizardNavigationEvent } from "@goauthentik/components/ak-wizard/events.js";
|
||||
import { type WizardButton } from "@goauthentik/components/ak-wizard/types";
|
||||
import { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter";
|
||||
@ -33,7 +33,7 @@ import {
|
||||
} from "@goauthentik/api";
|
||||
|
||||
import { ApplicationWizardStep } from "../ApplicationWizardStep.js";
|
||||
import { ApplicationTransactionValidationError, OneOfProvider } from "../types.js";
|
||||
import { ExtendedValidationError, OneOfProvider } from "../types.js";
|
||||
import { providerRenderers } from "./SubmitStepOverviewRenderers.js";
|
||||
|
||||
const _submitStates = ["reviewing", "running", "submitted"] as const;
|
||||
@ -131,36 +131,39 @@ export class ApplicationWizardSubmitStep extends CustomEmitterElement(Applicatio
|
||||
|
||||
this.state = "running";
|
||||
|
||||
return new CoreApi(DEFAULT_CONFIG)
|
||||
.coreTransactionalApplicationsUpdate({
|
||||
transactionApplicationRequest: request,
|
||||
})
|
||||
.then((_response: TransactionApplicationResponse) => {
|
||||
this.dispatchCustomEvent(EVENT_REFRESH);
|
||||
this.state = "submitted";
|
||||
})
|
||||
return (
|
||||
new CoreApi(DEFAULT_CONFIG)
|
||||
.coreTransactionalApplicationsUpdate({
|
||||
transactionApplicationRequest: request,
|
||||
})
|
||||
.then((_response: TransactionApplicationResponse) => {
|
||||
this.dispatchCustomEvent(EVENT_REFRESH);
|
||||
this.state = "submitted";
|
||||
})
|
||||
|
||||
.catch(async (resolution) => {
|
||||
const errors =
|
||||
await parseAPIResponseError<ApplicationTransactionValidationError>(resolution);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
.catch(async (resolution: any) => {
|
||||
const errors = (await parseAPIError(
|
||||
await resolution,
|
||||
)) as ExtendedValidationError;
|
||||
|
||||
// THIS is a really gross special case; if the user is duplicating the name of an existing provider, the error appears on the `app` (!) error object.
|
||||
// We have to move that to the `provider.name` error field so it shows up in the right place.
|
||||
if (Array.isArray(errors?.app?.provider)) {
|
||||
const providerError = errors.app.provider;
|
||||
errors.provider = errors.provider ?? {};
|
||||
errors.provider.name = providerError;
|
||||
|
||||
delete errors.app.provider;
|
||||
|
||||
if (Object.keys(errors.app).length === 0) {
|
||||
delete errors.app;
|
||||
// THIS is a really gross special case; if the user is duplicating the name of
|
||||
// an existing provider, the error appears on the `app` (!) error object. We
|
||||
// have to move that to the `provider.name` error field so it shows up in the
|
||||
// right place.
|
||||
if (Array.isArray(errors?.app?.provider)) {
|
||||
const providerError = errors.app.provider;
|
||||
errors.provider = errors.provider ?? {};
|
||||
errors.provider.name = providerError;
|
||||
delete errors.app.provider;
|
||||
if (Object.keys(errors.app).length === 0) {
|
||||
delete errors.app;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.handleUpdate({ errors });
|
||||
this.state = "reviewing";
|
||||
});
|
||||
this.handleUpdate({ errors });
|
||||
this.state = "reviewing";
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
override handleButton(button: WizardButton) {
|
||||
@ -229,7 +232,7 @@ export class ApplicationWizardSubmitStep extends CustomEmitterElement(Applicatio
|
||||
const navTo = (step: string) => () => this.dispatchEvent(new WizardNavigationEvent(step));
|
||||
const errors = this.wizard.errors;
|
||||
return html` <hr class="pf-c-divider" />
|
||||
${match(errors as ApplicationTransactionValidationError)
|
||||
${match(errors as ExtendedValidationError)
|
||||
.with(
|
||||
{ app: P.nonNullable },
|
||||
() =>
|
||||
|
@ -9,7 +9,7 @@ import { customElement, state } from "lit/decorators.js";
|
||||
import { OAuth2ProviderRequest, SourcesApi } from "@goauthentik/api";
|
||||
import { type OAuth2Provider, type PaginatedOAuthSourceList } from "@goauthentik/api";
|
||||
|
||||
import { ApplicationTransactionValidationError } from "../../types.js";
|
||||
import { ExtendedValidationError } from "../../types.js";
|
||||
import { ApplicationWizardProviderForm } from "./ApplicationWizardProviderForm.js";
|
||||
|
||||
@customElement("ak-application-wizard-provider-for-oauth")
|
||||
@ -34,7 +34,7 @@ export class ApplicationWizardOauth2ProviderForm extends ApplicationWizardProvid
|
||||
});
|
||||
}
|
||||
|
||||
renderForm(provider: OAuth2Provider, errors: ApplicationTransactionValidationError) {
|
||||
renderForm(provider: OAuth2Provider, errors: ExtendedValidationError) {
|
||||
const showClientSecretCallback = (show: boolean) => {
|
||||
this.showClientSecret = show;
|
||||
};
|
||||
|
@ -1,5 +1,3 @@
|
||||
import { APIError } from "@goauthentik/common/errors/network";
|
||||
|
||||
import {
|
||||
type ApplicationRequest,
|
||||
type LDAPProviderRequest,
|
||||
@ -27,31 +25,16 @@ export type OneOfProvider =
|
||||
|
||||
export type ValidationRecord = { [key: string]: string[] };
|
||||
|
||||
/**
|
||||
* An error that occurs during the creation or modification of an application.
|
||||
*
|
||||
* @todo (Elf) Extend this type to include all possible errors that can occur during the creation or modification of an application.
|
||||
*/
|
||||
export interface ApplicationTransactionValidationError extends ValidationError {
|
||||
// TODO: Elf, extend this type and apply it to every object in the wizard. Then run
|
||||
// the type-checker again.
|
||||
|
||||
export type ExtendedValidationError = ValidationError & {
|
||||
app?: ValidationRecord;
|
||||
provider?: ValidationRecord;
|
||||
bindings?: ValidationRecord;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
detail?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type-guard to determine if an API response is shaped like an {@linkcode ApplicationTransactionValidationError}.
|
||||
*/
|
||||
export function isApplicationTransactionValidationError(
|
||||
error: APIError,
|
||||
): error is ApplicationTransactionValidationError {
|
||||
if ("app" in error) return true;
|
||||
if ("provider" in error) return true;
|
||||
if ("bindings" in error) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// We use the PolicyBinding instead of the PolicyBindingRequest here, because that gives us a slot
|
||||
// in which to preserve the retrieved policy, group, or user object from the SearchSelect used to
|
||||
@ -66,7 +49,7 @@ export interface ApplicationWizardState {
|
||||
proxyMode: ProxyMode;
|
||||
bindings: PolicyBinding[];
|
||||
currentBinding: number;
|
||||
errors: ApplicationTransactionValidationError;
|
||||
errors: ExtendedValidationError;
|
||||
}
|
||||
|
||||
export interface ApplicationWizardStateUpdate {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import "@goauthentik/admin/events/EventVolumeChart";
|
||||
import { EventUser, formatGeoEvent } from "@goauthentik/admin/events/utils";
|
||||
import { EventGeo, EventUser } from "@goauthentik/admin/events/utils";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EventWithContext } from "@goauthentik/common/events";
|
||||
import { actionToLabel } from "@goauthentik/common/labels";
|
||||
@ -80,7 +80,7 @@ export class EventListPage extends TablePage<Event> {
|
||||
html`<div>${getRelativeTime(item.created)}</div>
|
||||
<small>${item.created.toLocaleString()}</small>`,
|
||||
html`<div>${item.clientIp || msg("-")}</div>
|
||||
<small>${formatGeoEvent(item)}</small>`,
|
||||
<small>${EventGeo(item)}</small>`,
|
||||
html`<span>${item.brand?.name || msg("-")}</span>`,
|
||||
html`<a href="#/events/log/${item.pk}">
|
||||
<pf-tooltip position="top" content=${msg("Show details")}>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { EventUser, formatGeoEvent } from "@goauthentik/admin/events/utils";
|
||||
import { EventGeo, EventUser } from "@goauthentik/admin/events/utils";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EventWithContext } from "@goauthentik/common/events";
|
||||
import { actionToLabel } from "@goauthentik/common/labels";
|
||||
@ -118,7 +118,7 @@ export class EventViewPage extends AKElement {
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<div>${this.event.clientIp || msg("-")}</div>
|
||||
<small>${formatGeoEvent(this.event)}</small>
|
||||
<small>${EventGeo(this.event)}</small>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
|
@ -1,31 +1,27 @@
|
||||
import { EventWithContext } from "@goauthentik/common/events";
|
||||
import { truncate } from "@goauthentik/common/utils";
|
||||
import { SlottedTemplateResult } from "@goauthentik/elements/types";
|
||||
import { KeyUnknown } from "@goauthentik/elements/forms/Form";
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { html, nothing } from "lit";
|
||||
import { TemplateResult, html } from "lit";
|
||||
|
||||
/**
|
||||
* Given event with a geographical context, format it into a string for display.
|
||||
*/
|
||||
export function formatGeoEvent(event: EventWithContext): SlottedTemplateResult {
|
||||
if (!event.context.geo) return nothing;
|
||||
|
||||
const { city, country, continent } = event.context.geo;
|
||||
|
||||
const parts = [city, country, continent].filter(Boolean);
|
||||
|
||||
return html`${parts.join(", ")}`;
|
||||
export function EventGeo(event: EventWithContext): TemplateResult {
|
||||
let geo: KeyUnknown | undefined = undefined;
|
||||
if (Object.hasOwn(event.context, "geo")) {
|
||||
geo = event.context.geo as KeyUnknown;
|
||||
const parts = [geo.city, geo.country, geo.continent].filter(
|
||||
(v) => v !== "" && v !== undefined,
|
||||
);
|
||||
return html`${parts.join(", ")}`;
|
||||
}
|
||||
return html``;
|
||||
}
|
||||
|
||||
export function EventUser(
|
||||
event: EventWithContext,
|
||||
truncateUsername?: number,
|
||||
): SlottedTemplateResult {
|
||||
if (!event.user.username) return html`-`;
|
||||
|
||||
let body: SlottedTemplateResult = nothing;
|
||||
|
||||
export function EventUser(event: EventWithContext, truncateUsername?: number): TemplateResult {
|
||||
if (!event.user.username) {
|
||||
return html`-`;
|
||||
}
|
||||
let body = html``;
|
||||
if (event.user.is_anonymous) {
|
||||
body = html`<div>${msg("Anonymous user")}</div>`;
|
||||
} else {
|
||||
@ -37,14 +33,12 @@ export function EventUser(
|
||||
>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
if (event.user.on_behalf_of) {
|
||||
return html`${body}<small>
|
||||
body = html`${body}<small>
|
||||
<a href="#/identity/users/${event.user.on_behalf_of.pk}"
|
||||
>${msg(str`On behalf of ${event.user.on_behalf_of.username}`)}</a
|
||||
>
|
||||
</small>`;
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { SentryIgnoredError } from "@goauthentik/common/sentry";
|
||||
import { SentryIgnoredError } from "@goauthentik/common/errors";
|
||||
import "@goauthentik/components/ak-status-label";
|
||||
import "@goauthentik/elements/events/LogViewer";
|
||||
import { Form } from "@goauthentik/elements/forms/Form";
|
||||
|
@ -21,22 +21,12 @@ export class RelatedApplicationButton extends AKElement {
|
||||
@property({ attribute: false })
|
||||
provider?: Provider;
|
||||
|
||||
@property()
|
||||
mode: "primary" | "backchannel" = "primary";
|
||||
|
||||
render(): TemplateResult {
|
||||
if (this.mode === "primary" && this.provider?.assignedApplicationSlug) {
|
||||
if (this.provider?.assignedApplicationSlug) {
|
||||
return html`<a href="#/core/applications/${this.provider.assignedApplicationSlug}">
|
||||
${this.provider.assignedApplicationName}
|
||||
</a>`;
|
||||
}
|
||||
if (this.mode === "backchannel" && this.provider?.assignedBackchannelApplicationSlug) {
|
||||
return html`<a
|
||||
href="#/core/applications/${this.provider.assignedBackchannelApplicationSlug}"
|
||||
>
|
||||
${this.provider.assignedBackchannelApplicationName}
|
||||
</a>`;
|
||||
}
|
||||
return html`<ak-forms-modal>
|
||||
<span slot="submit"> ${msg("Create")} </span>
|
||||
<span slot="header"> ${msg("Create Application")} </span>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import "@goauthentik/admin/common/ak-flow-search/ak-flow-search-no-default";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { SentryIgnoredError } from "@goauthentik/common/sentry";
|
||||
import { SentryIgnoredError } from "@goauthentik/common/errors";
|
||||
import { Form } from "@goauthentik/elements/forms/Form";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import "@goauthentik/elements/forms/SearchSelect";
|
||||
|
@ -11,7 +11,6 @@ import { html } from "lit";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import {
|
||||
CompatibilityModeEnum,
|
||||
CoreApi,
|
||||
CoreGroupsListRequest,
|
||||
Group,
|
||||
@ -62,35 +61,6 @@ export function renderForm(provider?: Partial<SCIMProvider>, errors: ValidationE
|
||||
)}
|
||||
inputHint="code"
|
||||
></ak-text-input>
|
||||
<ak-radio-input
|
||||
name="compatibilityMode"
|
||||
label=${msg("Compatibility Mode")}
|
||||
.value=${provider?.compatibilityMode}
|
||||
required
|
||||
.options=${[
|
||||
{
|
||||
label: msg("Default"),
|
||||
value: CompatibilityModeEnum.Default,
|
||||
default: true,
|
||||
description: html`${msg("Default behavior.")}`,
|
||||
},
|
||||
{
|
||||
label: msg("AWS"),
|
||||
value: CompatibilityModeEnum.Aws,
|
||||
description: html`${msg(
|
||||
"Altered behavior for usage with Amazon Web Services.",
|
||||
)}`,
|
||||
},
|
||||
{
|
||||
label: msg("Slack"),
|
||||
value: CompatibilityModeEnum.Slack,
|
||||
description: html`${msg("Altered behavior for usage with Slack.")}`,
|
||||
},
|
||||
]}
|
||||
help=${msg(
|
||||
"Alter authentik's behavior for vendor-specific SCIM implementations.",
|
||||
)}
|
||||
></ak-radio-input>
|
||||
<ak-form-element-horizontal name="dryRun">
|
||||
<label class="pf-c-switch">
|
||||
<input
|
||||
|
@ -174,7 +174,6 @@ export class SCIMProviderViewPage extends AKElement {
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<ak-provider-related-application
|
||||
mode="backchannel"
|
||||
.provider=${this.provider}
|
||||
></ak-provider-related-application>
|
||||
</div>
|
||||
|
@ -57,13 +57,10 @@ export class SourceListPage extends TablePage<Source> {
|
||||
}
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
const disabled =
|
||||
this.selectedElements.length < 1 ||
|
||||
this.selectedElements.some((item) => item.component === "");
|
||||
const nonBuiltInSources = this.selectedElements.filter((item) => item.component !== "");
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${msg("Source(s)")}
|
||||
.objects=${nonBuiltInSources}
|
||||
.objects=${this.selectedElements}
|
||||
.usedBy=${(item: Source) => {
|
||||
return new SourcesApi(DEFAULT_CONFIG).sourcesAllUsedByList({
|
||||
slug: item.slug,
|
||||
|
@ -3,7 +3,6 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import "@goauthentik/elements/utils/TimeDeltaHelp";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { TemplateResult, html } from "lit";
|
||||
@ -203,20 +202,19 @@ export class EmailStageForm extends BaseStageForm<EmailStage> {
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Token expiration")}
|
||||
label=${msg("Token expiry")}
|
||||
?required=${true}
|
||||
name="tokenExpiry"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${first(this.instance?.tokenExpiry, "minutes=30")}"
|
||||
type="number"
|
||||
value="${first(this.instance?.tokenExpiry, 30)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Time the token sent is valid.")}
|
||||
${msg("Time in minutes the token sent is valid.")}
|
||||
</p>
|
||||
<ak-utils-time-delta-help></ak-utils-time-delta-help>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Subject")}
|
||||
|
@ -1,9 +1,5 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import {
|
||||
containsNonFieldErrors,
|
||||
parseAPIResponseError,
|
||||
pluckErrorDetail,
|
||||
} from "@goauthentik/common/errors/network";
|
||||
import { parseAPIError } from "@goauthentik/common/errors";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/elements/CodeMirror";
|
||||
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
|
||||
@ -21,7 +17,14 @@ import { map } from "lit/directives/map.js";
|
||||
import PFTitle from "@patternfly/patternfly/components/Title/title.css";
|
||||
import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
|
||||
|
||||
import { Prompt, PromptChallenge, PromptTypeEnum, StagesApi } from "@goauthentik/api";
|
||||
import {
|
||||
Prompt,
|
||||
PromptChallenge,
|
||||
PromptTypeEnum,
|
||||
ResponseError,
|
||||
StagesApi,
|
||||
ValidationError,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
class PreviewStageHost implements StageHost {
|
||||
challenge = undefined;
|
||||
@ -75,22 +78,15 @@ export class PromptForm extends ModelForm<Prompt, string> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return new StagesApi(DEFAULT_CONFIG)
|
||||
.stagesPromptPromptsPreviewCreate({
|
||||
try {
|
||||
this.preview = await new StagesApi(DEFAULT_CONFIG).stagesPromptPromptsPreviewCreate({
|
||||
promptRequest: prompt,
|
||||
})
|
||||
.then((nextPreview) => {
|
||||
this.preview = nextPreview;
|
||||
this.previewError = undefined;
|
||||
})
|
||||
.catch(async (error) => {
|
||||
const parsedError = await parseAPIResponseError(error);
|
||||
|
||||
this.previewError = containsNonFieldErrors(parsedError)
|
||||
? error.nonFieldErrors
|
||||
: [pluckErrorDetail(parsedError, msg("Failed to preview prompt"))];
|
||||
});
|
||||
this.previewError = undefined;
|
||||
} catch (exc) {
|
||||
const errorMessage = parseAPIError(exc as ResponseError);
|
||||
this.previewError = (errorMessage as ValidationError).nonFieldErrors;
|
||||
}
|
||||
}
|
||||
|
||||
getSuccessMessage(): string {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { SentryIgnoredError } from "@goauthentik/common/errors";
|
||||
import { deviceTypeName } from "@goauthentik/common/labels";
|
||||
import { SentryIgnoredError } from "@goauthentik/common/sentry";
|
||||
import { getRelativeTime } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/elements/forms/DeleteBulkForm";
|
||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
|
||||
|
36
web/src/common/errors.ts
Normal file
36
web/src/common/errors.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import {
|
||||
GenericError,
|
||||
GenericErrorFromJSON,
|
||||
ResponseError,
|
||||
ValidationError,
|
||||
ValidationErrorFromJSON,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
export class SentryIgnoredError extends Error {}
|
||||
export class NotFoundError extends Error {}
|
||||
export class RequestError extends Error {}
|
||||
|
||||
export type APIErrorTypes = ValidationError | GenericError;
|
||||
|
||||
export const HTTP_BAD_REQUEST = 400;
|
||||
export const HTTP_INTERNAL_SERVICE_ERROR = 500;
|
||||
|
||||
export async function parseAPIError(error: Error): Promise<APIErrorTypes> {
|
||||
if (!(error instanceof ResponseError)) {
|
||||
return error;
|
||||
}
|
||||
if (
|
||||
error.response.status < HTTP_BAD_REQUEST ||
|
||||
error.response.status >= HTTP_INTERNAL_SERVICE_ERROR
|
||||
) {
|
||||
return error;
|
||||
}
|
||||
const body = await error.response.json();
|
||||
if (error.response.status === 400) {
|
||||
return ValidationErrorFromJSON(body);
|
||||
}
|
||||
if (error.response.status === 403) {
|
||||
return GenericErrorFromJSON(body);
|
||||
}
|
||||
return body;
|
||||
}
|
@ -1,194 +0,0 @@
|
||||
import {
|
||||
GenericError,
|
||||
GenericErrorFromJSON,
|
||||
ResponseError,
|
||||
ValidationError,
|
||||
ValidationErrorFromJSON,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
//#region HTTP
|
||||
|
||||
/**
|
||||
* Common HTTP status names used in the API and their corresponding codes.
|
||||
*/
|
||||
export const HTTPStatusCode = {
|
||||
BadRequest: 400,
|
||||
Forbidden: 403,
|
||||
InternalServiceError: 500,
|
||||
} as const satisfies Record<string, number>;
|
||||
|
||||
export type HTTPStatusCode = (typeof HTTPStatusCode)[keyof typeof HTTPStatusCode];
|
||||
|
||||
export type HTTPErrorJSONTransformer<T = unknown> = (json: T) => APIError;
|
||||
|
||||
export const HTTPStatusCodeTransformer: Record<number, HTTPErrorJSONTransformer> = {
|
||||
[HTTPStatusCode.BadRequest]: ValidationErrorFromJSON,
|
||||
[HTTPStatusCode.Forbidden]: GenericErrorFromJSON,
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Type guard to check if a response contains a JSON body.
|
||||
*
|
||||
* This is useful to guard against parsing errors when attempting to read the response body.
|
||||
*/
|
||||
export function isJSONResponse(response: Response): boolean {
|
||||
return Boolean(response.headers.get("content-type")?.includes("application/json"));
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region API
|
||||
|
||||
/**
|
||||
* An API response error, typically derived from a {@linkcode Response} body.
|
||||
*
|
||||
* @see {@linkcode parseAPIResponseError}
|
||||
*/
|
||||
export type APIError = ValidationError | GenericError;
|
||||
|
||||
/**
|
||||
* Given an error-like object, attempts to normalize it into a {@linkcode GenericError}
|
||||
* suitable for display to the user.
|
||||
*/
|
||||
export function createSyntheticGenericError(detail?: string): GenericError {
|
||||
const syntheticGenericError: GenericError = {
|
||||
detail: detail || ResponseErrorMessages[HTTPStatusCode.InternalServiceError].reason,
|
||||
};
|
||||
|
||||
return syntheticGenericError;
|
||||
}
|
||||
|
||||
/**
|
||||
* An error that contains a native response object.
|
||||
*
|
||||
* @see {@linkcode isResponseErrorLike} to determine if an error contains a response object.
|
||||
*/
|
||||
export type APIErrorWithResponse = Pick<ResponseError, "response" | "message">;
|
||||
|
||||
/**
|
||||
* Type guard to check if an error contains a HTTP {@linkcode Response} object.
|
||||
*
|
||||
* @see {@linkcode parseAPIError} to parse the response body into a {@linkcode APIError}.
|
||||
*/
|
||||
export function isResponseErrorLike(errorLike: unknown): errorLike is APIErrorWithResponse {
|
||||
if (!errorLike || typeof errorLike !== "object") return false;
|
||||
|
||||
return "response" in errorLike && errorLike.response instanceof Response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to check if an error contains non-field errors.
|
||||
*
|
||||
* This is a reasonable heuristic to determine if an error is a {@linkcode ValidationError}.
|
||||
*
|
||||
* @see {@linkcode parseAPIError} to parse the response body into a {@linkcode APIError}.
|
||||
*/
|
||||
export function containsNonFieldErrors(error: APIError): error is ValidationError {
|
||||
return "non_field_errors" in error;
|
||||
}
|
||||
/**
|
||||
* A descriptor to provide a human readable error message for a given HTTP status code.
|
||||
*
|
||||
* @see {@linkcode ResponseErrorMessages} for a list of fallback error messages.
|
||||
*/
|
||||
interface ResponseErrorDescriptor {
|
||||
headline: string;
|
||||
reason: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fallback error messages for HTTP status codes used when a more specific error message is not available in the response.
|
||||
*/
|
||||
export const ResponseErrorMessages: Record<number, ResponseErrorDescriptor> = {
|
||||
[HTTPStatusCode.BadRequest]: {
|
||||
headline: "Bad request",
|
||||
reason: "The server did not understand the request",
|
||||
},
|
||||
[HTTPStatusCode.InternalServiceError]: {
|
||||
headline: "Internal server error",
|
||||
reason: "An unexpected error occurred",
|
||||
},
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Composes a human readable error message from a {@linkcode ResponseErrorDescriptor}.
|
||||
*
|
||||
* Note that this is kept separate from localization to lower the complexity of the error handling code.
|
||||
*/
|
||||
export function composeResponseErrorDescriptor(descriptor: ResponseErrorDescriptor): string {
|
||||
return `${descriptor.headline}: ${descriptor.reason}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to pluck a human readable error message from a {@linkcode ValidationError}.
|
||||
*/
|
||||
export function pluckErrorDetail(validationError: ValidationError, fallback?: string): string;
|
||||
/**
|
||||
* Attempts to pluck a human readable error message from a {@linkcode GenericError}.
|
||||
*/
|
||||
export function pluckErrorDetail(genericError: GenericError, fallback?: string): string;
|
||||
/**
|
||||
* Attempts to pluck a human readable error message from an `Error` object.
|
||||
*/
|
||||
export function pluckErrorDetail(error: Error, fallback?: string): string;
|
||||
/**
|
||||
* Attempts to pluck a human readable error message from an error-like object.
|
||||
*
|
||||
* Prioritizes the `detail` key, then the `message` key.
|
||||
*
|
||||
*/
|
||||
export function pluckErrorDetail(errorLike: unknown, fallback?: string): string;
|
||||
export function pluckErrorDetail(errorLike: unknown, fallback?: string): string {
|
||||
fallback ||= composeResponseErrorDescriptor(
|
||||
ResponseErrorMessages[HTTPStatusCode.InternalServiceError],
|
||||
);
|
||||
|
||||
if (!errorLike || typeof errorLike !== "object") {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
if ("detail" in errorLike && typeof errorLike.detail === "string") {
|
||||
return errorLike.detail;
|
||||
}
|
||||
|
||||
if ("message" in errorLike && typeof errorLike.message === "string") {
|
||||
return errorLike.message;
|
||||
}
|
||||
|
||||
return fallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given API error, parses the response body and transforms it into a {@linkcode APIError}.
|
||||
*/
|
||||
export async function parseAPIResponseError<T extends APIError = APIError>(
|
||||
error: unknown,
|
||||
): Promise<T> {
|
||||
if (!isResponseErrorLike(error)) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
|
||||
return createSyntheticGenericError(message) as T;
|
||||
}
|
||||
|
||||
const { response, message } = error;
|
||||
|
||||
if (!isJSONResponse(response)) {
|
||||
return createSyntheticGenericError(message || response.statusText) as T;
|
||||
}
|
||||
|
||||
return response
|
||||
.json()
|
||||
.then((body) => {
|
||||
const transformer = HTTPStatusCodeTransformer[response.status];
|
||||
const transformedBody = transformer ? transformer(body) : body;
|
||||
|
||||
return transformedBody as unknown as T;
|
||||
})
|
||||
.catch((transformerError) => {
|
||||
console.error("Failed to parse response error body", transformerError);
|
||||
|
||||
return createSyntheticGenericError(message || response.statusText) as T;
|
||||
});
|
||||
}
|
||||
|
||||
//#endregion
|
@ -8,10 +8,13 @@ export interface EventUser {
|
||||
is_anonymous?: boolean;
|
||||
}
|
||||
|
||||
export interface EventGeo {
|
||||
city?: string;
|
||||
country?: string;
|
||||
continent?: string;
|
||||
export interface EventContext {
|
||||
[key: string]: EventContext | EventModel | string | number | string[];
|
||||
}
|
||||
|
||||
export interface EventWithContext extends Event {
|
||||
user: EventUser;
|
||||
context: EventContext;
|
||||
}
|
||||
|
||||
export interface EventModel {
|
||||
@ -25,13 +28,3 @@ export interface EventRequest {
|
||||
path: string;
|
||||
method: string;
|
||||
}
|
||||
|
||||
export interface EventContext {
|
||||
[key: string]: EventContext | EventModel | EventGeo | string | number | string[] | undefined;
|
||||
geo?: EventGeo;
|
||||
}
|
||||
|
||||
export interface EventWithContext extends Event {
|
||||
user: EventUser;
|
||||
context: EventContext;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { VERSION } from "@goauthentik/common/constants";
|
||||
import { SentryIgnoredError } from "@goauthentik/common/sentry";
|
||||
import { SentryIgnoredError } from "@goauthentik/common/errors";
|
||||
|
||||
export interface PlexPinResponse {
|
||||
// Only has the fields we care about
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { config } from "@goauthentik/common/api/config";
|
||||
import { VERSION } from "@goauthentik/common/constants";
|
||||
import { SentryIgnoredError } from "@goauthentik/common/errors";
|
||||
import { me } from "@goauthentik/common/users";
|
||||
import {
|
||||
ErrorEvent,
|
||||
@ -15,85 +16,69 @@ import { CapabilitiesEnum, Config, ResponseError } from "@goauthentik/api";
|
||||
export const TAG_SENTRY_COMPONENT = "authentik.component";
|
||||
export const TAG_SENTRY_CAPABILITIES = "authentik.capabilities";
|
||||
|
||||
/**
|
||||
* A generic error that can be thrown without triggering Sentry's reporting.
|
||||
*/
|
||||
export class SentryIgnoredError extends Error {}
|
||||
|
||||
/**
|
||||
* Configure Sentry with the given configuration.
|
||||
*
|
||||
* @param canSendPII Whether the user can send personally identifiable information.
|
||||
*/
|
||||
export async function configureSentry(canSendPII = false): Promise<Config> {
|
||||
export async function configureSentry(canDoPpi = false): Promise<Config> {
|
||||
const cfg = await config();
|
||||
|
||||
if (!cfg.errorReporting.enabled) return cfg;
|
||||
|
||||
init({
|
||||
dsn: cfg.errorReporting.sentryDsn,
|
||||
ignoreErrors: [
|
||||
/network/gi,
|
||||
/fetch/gi,
|
||||
/module/gi,
|
||||
// Error on edge on ios,
|
||||
// https://stackoverflow.com/questions/69261499/what-is-instantsearchsdkjsbridgeclearhighlight
|
||||
/instantSearchSDKJSBridgeClearHighlight/gi,
|
||||
// Seems to be an issue in Safari and Firefox
|
||||
/MutationObserver.observe/gi,
|
||||
/NS_ERROR_FAILURE/gi,
|
||||
],
|
||||
release: `authentik@${VERSION}`,
|
||||
integrations: [
|
||||
browserTracingIntegration({
|
||||
shouldCreateSpanForRequest: (url: string) => {
|
||||
return url.startsWith(window.location.host);
|
||||
},
|
||||
}),
|
||||
],
|
||||
tracesSampleRate: cfg.errorReporting.tracesSampleRate,
|
||||
environment: cfg.errorReporting.environment,
|
||||
beforeSend: (
|
||||
event: ErrorEvent,
|
||||
hint: EventHint,
|
||||
): ErrorEvent | PromiseLike<ErrorEvent | null> | null => {
|
||||
if (!hint) {
|
||||
if (cfg.errorReporting.enabled) {
|
||||
init({
|
||||
dsn: cfg.errorReporting.sentryDsn,
|
||||
ignoreErrors: [
|
||||
/network/gi,
|
||||
/fetch/gi,
|
||||
/module/gi,
|
||||
// Error on edge on ios,
|
||||
// https://stackoverflow.com/questions/69261499/what-is-instantsearchsdkjsbridgeclearhighlight
|
||||
/instantSearchSDKJSBridgeClearHighlight/gi,
|
||||
// Seems to be an issue in Safari and Firefox
|
||||
/MutationObserver.observe/gi,
|
||||
/NS_ERROR_FAILURE/gi,
|
||||
],
|
||||
release: `authentik@${VERSION}`,
|
||||
integrations: [
|
||||
browserTracingIntegration({
|
||||
shouldCreateSpanForRequest: (url: string) => {
|
||||
return url.startsWith(window.location.host);
|
||||
},
|
||||
}),
|
||||
],
|
||||
tracesSampleRate: cfg.errorReporting.tracesSampleRate,
|
||||
environment: cfg.errorReporting.environment,
|
||||
beforeSend: (
|
||||
event: ErrorEvent,
|
||||
hint: EventHint,
|
||||
): ErrorEvent | PromiseLike<ErrorEvent | null> | null => {
|
||||
if (!hint) {
|
||||
return event;
|
||||
}
|
||||
if (hint.originalException instanceof SentryIgnoredError) {
|
||||
return null;
|
||||
}
|
||||
if (
|
||||
hint.originalException instanceof ResponseError ||
|
||||
hint.originalException instanceof DOMException
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return event;
|
||||
}
|
||||
if (hint.originalException instanceof SentryIgnoredError) {
|
||||
return null;
|
||||
}
|
||||
if (
|
||||
hint.originalException instanceof ResponseError ||
|
||||
hint.originalException instanceof DOMException
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return event;
|
||||
},
|
||||
});
|
||||
|
||||
setTag(TAG_SENTRY_CAPABILITIES, cfg.capabilities.join(","));
|
||||
|
||||
if (window.location.pathname.includes("if/")) {
|
||||
setTag(TAG_SENTRY_COMPONENT, `web/${currentInterface()}`);
|
||||
}
|
||||
|
||||
if (cfg.capabilities.includes(CapabilitiesEnum.CanDebug)) {
|
||||
const Spotlight = await import("@spotlightjs/spotlight");
|
||||
|
||||
Spotlight.init({ injectImmediately: true });
|
||||
}
|
||||
|
||||
if (cfg.errorReporting.sendPii && canSendPII) {
|
||||
await me().then((user) => {
|
||||
setUser({ email: user.user.email });
|
||||
console.debug("authentik/config: Sentry with PII enabled.");
|
||||
},
|
||||
});
|
||||
} else {
|
||||
console.debug("authentik/config: Sentry enabled.");
|
||||
}
|
||||
setTag(TAG_SENTRY_CAPABILITIES, cfg.capabilities.join(","));
|
||||
if (window.location.pathname.includes("if/")) {
|
||||
setTag(TAG_SENTRY_COMPONENT, `web/${currentInterface()}`);
|
||||
}
|
||||
if (cfg.capabilities.includes(CapabilitiesEnum.CanDebug)) {
|
||||
const Spotlight = await import("@spotlightjs/spotlight");
|
||||
|
||||
Spotlight.init({ injectImmediately: true });
|
||||
}
|
||||
if (cfg.errorReporting.sendPii && canDoPpi) {
|
||||
me().then((user) => {
|
||||
setUser({ email: user.user.email });
|
||||
console.debug("authentik/config: Sentry with PII enabled.");
|
||||
});
|
||||
} else {
|
||||
console.debug("authentik/config: Sentry enabled.");
|
||||
}
|
||||
}
|
||||
return cfg;
|
||||
}
|
||||
|
||||
|
@ -56,19 +56,6 @@ html > form > input {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.pf-c-card__title {
|
||||
.pf-icon:first-child,
|
||||
.fa:first-child {
|
||||
margin-inline-end: var(--pf-global--spacer--sm);
|
||||
}
|
||||
}
|
||||
|
||||
a > .fas.fa-external-link-alt {
|
||||
margin-inline-start: var(--pf-global--spacer--xs);
|
||||
font-size: var(--pf-global--FontSize--sm);
|
||||
transform: translateY(-0.1em);
|
||||
}
|
||||
|
||||
.pf-c-form-control {
|
||||
--pf-c-form-control--m-caps-lock--BackgroundUrl: url("data:image/svg+xml;charset=utf8,%3Csvg fill='%23aaabac' viewBox='0 0 56 56' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M 20.7812 37.6211 L 35.2421 37.6211 C 38.5233 37.6211 40.2577 35.6992 40.2577 32.6055 L 40.2577 28.4570 L 49.1404 28.4570 C 51.0859 28.4570 52.6329 27.3086 52.6329 25.5039 C 52.6329 24.4024 52.0703 23.5351 51.0158 22.6211 L 30.9062 4.8789 C 29.9452 4.0351 29.0546 3.4727 27.9999 3.4727 C 26.9687 3.4727 26.0780 4.0351 25.1171 4.8789 L 4.9843 22.6445 C 3.8828 23.6055 3.3671 24.4024 3.3671 25.5039 C 3.3671 27.3086 4.9140 28.4570 6.8828 28.4570 L 15.7421 28.4570 L 15.7421 32.6055 C 15.7421 35.6992 17.4999 37.6211 20.7812 37.6211 Z M 21.1562 34.0820 C 20.2655 34.0820 19.6562 33.4961 19.6562 32.6055 L 19.6562 25.7149 C 19.6562 25.1524 19.4452 24.9180 18.8828 24.9180 L 8.6640 24.9180 C 8.4999 24.9180 8.4296 24.8476 8.4296 24.7305 C 8.4296 24.6367 8.4530 24.5430 8.5702 24.4492 L 27.5077 7.9961 C 27.7187 7.8086 27.8359 7.7383 27.9999 7.7383 C 28.1640 7.7383 28.3046 7.8086 28.4921 7.9961 L 47.4532 24.4492 C 47.5703 24.5430 47.5939 24.6367 47.5939 24.7305 C 47.5939 24.8476 47.4998 24.9180 47.3356 24.9180 L 37.1406 24.9180 C 36.5780 24.9180 36.3671 25.1524 36.3671 25.7149 L 36.3671 32.6055 C 36.3671 33.4727 35.7109 34.0820 34.8671 34.0820 Z M 19.7733 52.5273 L 36.0624 52.5273 C 38.7577 52.5273 40.3046 51.0273 40.3046 48.3086 L 40.3046 44.9336 C 40.3046 42.2148 38.7577 40.6680 36.0624 40.6680 L 19.7733 40.6680 C 17.0546 40.6680 15.5077 42.2383 15.5077 44.9336 L 15.5077 48.3086 C 15.5077 51.0039 17.0546 52.5273 19.7733 52.5273 Z M 20.3124 49.2227 C 19.4921 49.2227 19.0468 48.8008 19.0468 47.9805 L 19.0468 45.2617 C 19.0468 44.4414 19.4921 43.9727 20.3124 43.9727 L 35.5233 43.9727 C 36.3202 43.9727 36.7655 44.4414 36.7655 45.2617 L 36.7655 47.9805 C 36.7655 48.8008 36.3202 49.2227 35.5233 49.2227 Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
@ -4,7 +4,6 @@
|
||||
--pf-c-page__main-section--m-light--BackgroundColor: var(--ak-dark-background-darker);
|
||||
--pf-global--link--Color: var(--ak-dark-foreground-link) !important;
|
||||
--pf-global--BorderColor--100: var(--ak-dark-background-lighter) !important;
|
||||
--pf-c-table--m-striped__tr--BackgroundColor: var(--pf-global--BackgroundColor--dark-300);
|
||||
}
|
||||
body {
|
||||
background-color: var(--ak-dark-background) !important;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { SentryIgnoredError } from "@goauthentik/common/sentry";
|
||||
import { SentryIgnoredError } from "@goauthentik/common/errors";
|
||||
|
||||
import { CSSResult, css } from "lit";
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { EventUser, formatGeoEvent } from "@goauthentik/admin/events/utils";
|
||||
import { EventGeo, EventUser } from "@goauthentik/admin/events/utils";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EventWithContext } from "@goauthentik/common/events";
|
||||
import { actionToLabel } from "@goauthentik/common/labels";
|
||||
@ -76,7 +76,7 @@ export class ObjectChangelog extends Table<Event> {
|
||||
<small>${item.created.toLocaleString()}</small>`,
|
||||
html`<div>${item.clientIp || msg("-")}</div>
|
||||
|
||||
<small>${formatGeoEvent(item)}</small>`,
|
||||
<small>${EventGeo(item)}</small>`,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import { SlottedTemplateResult } from "@goauthentik/elements/types";
|
||||
|
||||
import { CSSResult, TemplateResult, css, html, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
@ -97,30 +96,24 @@ export class AggregateCard extends AKElement implements IAggregateCard {
|
||||
.pf-c-card__footer {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.pf-c-card {
|
||||
--pf-c-card__title--FontSize: var(--pf-global--FontSize--xs);
|
||||
--pf-c-card--child--PaddingLeft: var(--pf-global--spacer--md);
|
||||
--pf-c-card--child--PaddingRight: var(--pf-global--spacer--md);
|
||||
}
|
||||
`,
|
||||
]);
|
||||
}
|
||||
|
||||
renderInner(): SlottedTemplateResult {
|
||||
renderInner(): TemplateResult {
|
||||
return html`<slot></slot>`;
|
||||
}
|
||||
|
||||
renderHeaderLink() {
|
||||
if (!this.headerLink) return nothing;
|
||||
|
||||
return html`<a href="${this.headerLink}">
|
||||
<i class="fa fa-link"></i>
|
||||
</a>`;
|
||||
renderHeaderLink(): TemplateResult {
|
||||
return html`${this.headerLink
|
||||
? html`<a href="${this.headerLink}">
|
||||
<i class="fa fa-link"> </i>
|
||||
</a>`
|
||||
: ""}`;
|
||||
}
|
||||
|
||||
renderHeader(): SlottedTemplateResult {
|
||||
return this.header ? html`${this.header}` : nothing;
|
||||
renderHeader(): TemplateResult {
|
||||
return html`${this.header ? this.header : ""}`;
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
|
@ -1,9 +1,4 @@
|
||||
import { EVENT_REFRESH, EVENT_THEME_CHANGE } from "@goauthentik/common/constants";
|
||||
import {
|
||||
APIError,
|
||||
parseAPIResponseError,
|
||||
pluckErrorDetail,
|
||||
} from "@goauthentik/common/errors/network";
|
||||
import { getRelativeTime } from "@goauthentik/common/utils";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/EmptyState";
|
||||
@ -28,7 +23,7 @@ import { msg } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
import { property, state } from "lit/decorators.js";
|
||||
|
||||
import { UiThemeEnum } from "@goauthentik/api";
|
||||
import { ResponseError, UiThemeEnum } from "@goauthentik/api";
|
||||
|
||||
Chart.register(Legend, Tooltip);
|
||||
Chart.register(LineController, BarController, DoughnutController);
|
||||
@ -72,7 +67,7 @@ export abstract class AKChart<T> extends AKElement {
|
||||
chart?: Chart;
|
||||
|
||||
@state()
|
||||
error?: APIError;
|
||||
error?: ResponseError;
|
||||
|
||||
@property()
|
||||
centerText?: string;
|
||||
@ -84,9 +79,6 @@ export abstract class AKChart<T> extends AKElement {
|
||||
css`
|
||||
.container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
aspect-ratio: 1 / 1;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@ -100,7 +92,6 @@ export abstract class AKChart<T> extends AKElement {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
z-index: 1;
|
||||
cursor: crosshair;
|
||||
}
|
||||
`,
|
||||
];
|
||||
@ -145,24 +136,19 @@ export abstract class AKChart<T> extends AKElement {
|
||||
this.apiRequest()
|
||||
.then((r) => {
|
||||
const canvas = this.shadowRoot?.querySelector<HTMLCanvasElement>("canvas");
|
||||
|
||||
if (!canvas) {
|
||||
console.warn("Failed to get canvas element");
|
||||
return;
|
||||
}
|
||||
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
if (!ctx) {
|
||||
console.warn("failed to get 2d context");
|
||||
return;
|
||||
}
|
||||
|
||||
this.chart = this.configureChart(r, ctx);
|
||||
})
|
||||
.catch(async (error) => {
|
||||
const parsedError = await parseAPIResponseError(error);
|
||||
this.error = parsedError;
|
||||
.catch((exc: ResponseError) => {
|
||||
this.error = exc;
|
||||
});
|
||||
}
|
||||
|
||||
@ -228,7 +214,7 @@ export abstract class AKChart<T> extends AKElement {
|
||||
${this.error
|
||||
? html`
|
||||
<ak-empty-state header="${msg("Failed to fetch data.")}" icon="fa-times">
|
||||
<p slot="body">${pluckErrorDetail(this.error)}</p>
|
||||
<p slot="body">${this.error.response.statusText}</p>
|
||||
</ak-empty-state>
|
||||
`
|
||||
: html`${this.chart
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import { parseAPIResponseError } from "@goauthentik/common/errors/network";
|
||||
import { parseAPIError } from "@goauthentik/common/errors";
|
||||
import { MessageLevel } from "@goauthentik/common/messages";
|
||||
import { camelToSnake, convertToSlug, dateToUTC } from "@goauthentik/common/utils";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
@ -20,7 +20,13 @@ import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-gro
|
||||
import PFSwitch from "@patternfly/patternfly/components/Switch/switch.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
import { instanceOfValidationError } from "@goauthentik/api";
|
||||
import { ResponseError, ValidationError, instanceOfValidationError } from "@goauthentik/api";
|
||||
|
||||
export class APIError extends Error {
|
||||
constructor(public response: ValidationError) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
export interface KeyUnknown {
|
||||
[key: string]: unknown;
|
||||
@ -279,82 +285,73 @@ export abstract class Form<T> extends AKElement {
|
||||
* field-levels errors to the fields, and send the rest of them to the Notifications.
|
||||
*
|
||||
*/
|
||||
async submit(event: Event): Promise<unknown | undefined> {
|
||||
event.preventDefault();
|
||||
|
||||
const data = this.serializeForm();
|
||||
if (!data) return;
|
||||
|
||||
return this.send(data)
|
||||
.then((response) => {
|
||||
showMessage({
|
||||
level: MessageLevel.success,
|
||||
message: this.getSuccessMessage(),
|
||||
});
|
||||
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(EVENT_REFRESH, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
|
||||
return response;
|
||||
})
|
||||
.catch(async (error) => {
|
||||
if (error instanceof PreventFormSubmit && error.element) {
|
||||
error.element.errorMessages = [error.message];
|
||||
error.element.invalid = true;
|
||||
}
|
||||
|
||||
let errorMessage = error.response.statusText;
|
||||
const parsedError = await parseAPIResponseError(error);
|
||||
|
||||
if (instanceOfValidationError(parsedError)) {
|
||||
async submit(ev: Event): Promise<unknown | undefined> {
|
||||
ev.preventDefault();
|
||||
try {
|
||||
const data = this.serializeForm();
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
const response = await this.send(data);
|
||||
showMessage({
|
||||
level: MessageLevel.success,
|
||||
message: this.getSuccessMessage(),
|
||||
});
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(EVENT_REFRESH, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
return response;
|
||||
} catch (ex) {
|
||||
if (ex instanceof ResponseError) {
|
||||
let errorMessage = ex.response.statusText;
|
||||
const error = await parseAPIError(ex);
|
||||
if (instanceOfValidationError(error)) {
|
||||
// assign all input-related errors to their elements
|
||||
const elements =
|
||||
this.shadowRoot?.querySelectorAll<HorizontalFormElement>(
|
||||
"ak-form-element-horizontal",
|
||||
) || [];
|
||||
|
||||
elements.forEach((element) => {
|
||||
element.requestUpdate();
|
||||
|
||||
const elementName = element.name;
|
||||
if (!elementName) return;
|
||||
|
||||
const snakeProperty = camelToSnake(elementName);
|
||||
|
||||
if (snakeProperty in parsedError) {
|
||||
element.errorMessages = parsedError[snakeProperty];
|
||||
if (!elementName) {
|
||||
return;
|
||||
}
|
||||
if (camelToSnake(elementName) in error) {
|
||||
element.errorMessages = (error as ValidationError)[
|
||||
camelToSnake(elementName)
|
||||
];
|
||||
element.invalid = true;
|
||||
} else {
|
||||
element.errorMessages = [];
|
||||
element.invalid = false;
|
||||
}
|
||||
});
|
||||
|
||||
if (parsedError.nonFieldErrors) {
|
||||
this.nonFieldErrors = parsedError.nonFieldErrors;
|
||||
if ((error as ValidationError).nonFieldErrors) {
|
||||
this.nonFieldErrors = (error as ValidationError).nonFieldErrors;
|
||||
}
|
||||
|
||||
errorMessage = msg("Invalid update request.");
|
||||
|
||||
// Only change the message when we have `detail`.
|
||||
// Everything else is handled in the form.
|
||||
if ("detail" in parsedError) {
|
||||
errorMessage = parsedError.detail;
|
||||
if ("detail" in (error as ValidationError)) {
|
||||
errorMessage = (error as ValidationError).detail;
|
||||
}
|
||||
}
|
||||
|
||||
showMessage({
|
||||
message: errorMessage,
|
||||
level: MessageLevel.error,
|
||||
});
|
||||
|
||||
// Rethrow the error so the form doesn't close.
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
if (ex instanceof PreventFormSubmit && ex.element) {
|
||||
ex.element.errorMessages = [ex.message];
|
||||
ex.element.invalid = true;
|
||||
}
|
||||
// rethrow the error so the form doesn't close
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
renderFormWrapper(): TemplateResult {
|
||||
|
@ -1,9 +1,5 @@
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import {
|
||||
APIError,
|
||||
parseAPIResponseError,
|
||||
pluckErrorDetail,
|
||||
} from "@goauthentik/common/errors/network";
|
||||
import { APIErrorTypes, parseAPIError } from "@goauthentik/common/errors";
|
||||
import { groupBy } from "@goauthentik/common/utils";
|
||||
import { AkControlElement } from "@goauthentik/elements/AkControlElement.js";
|
||||
import { PreventFormSubmit } from "@goauthentik/elements/forms/helpers";
|
||||
@ -17,6 +13,8 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
import { ResponseError } from "@goauthentik/api";
|
||||
|
||||
import "./ak-search-select-loading-indicator.js";
|
||||
import "./ak-search-select-view.js";
|
||||
import { SearchSelectView } from "./ak-search-select-view.js";
|
||||
@ -101,7 +99,7 @@ export class SearchSelectBase<T> extends AkControlElement<string> implements ISe
|
||||
isFetchingData = false;
|
||||
|
||||
@state()
|
||||
error?: APIError;
|
||||
error?: APIErrorTypes;
|
||||
|
||||
public toForm(): string {
|
||||
if (!this.objects) {
|
||||
@ -130,26 +128,23 @@ export class SearchSelectBase<T> extends AkControlElement<string> implements ISe
|
||||
}
|
||||
this.isFetchingData = true;
|
||||
this.dispatchEvent(new Event("loading"));
|
||||
|
||||
return this.fetchObjects(this.query)
|
||||
.then((nextObjects) => {
|
||||
nextObjects.forEach((obj) => {
|
||||
if (this.selected && this.selected(obj, nextObjects || [])) {
|
||||
.then((objects) => {
|
||||
objects.forEach((obj) => {
|
||||
if (this.selected && this.selected(obj, objects || [])) {
|
||||
this.selectedObject = obj;
|
||||
this.dispatchChangeEvent(this.selectedObject);
|
||||
}
|
||||
});
|
||||
|
||||
this.objects = nextObjects;
|
||||
this.objects = objects;
|
||||
this.isFetchingData = false;
|
||||
})
|
||||
.catch(async (error) => {
|
||||
.catch((exc: ResponseError) => {
|
||||
this.isFetchingData = false;
|
||||
this.objects = undefined;
|
||||
|
||||
const parsedError = await parseAPIResponseError(error);
|
||||
|
||||
this.error = parsedError;
|
||||
parseAPIError(exc).then((err) => {
|
||||
this.error = err;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -238,9 +233,7 @@ export class SearchSelectBase<T> extends AkControlElement<string> implements ISe
|
||||
|
||||
public override render() {
|
||||
if (this.error) {
|
||||
return html`<em
|
||||
>${msg("Failed to fetch objects: ")} ${pluckErrorDetail(this.error)}</em
|
||||
>`;
|
||||
return html`<em>${msg("Failed to fetch objects: ")} ${this.error.detail}</em>`;
|
||||
}
|
||||
|
||||
// `this.objects` is both a container and a sigil; if it is in the `undefined` state, it's a
|
||||
|
@ -3,7 +3,7 @@ import {
|
||||
EVENT_WS_MESSAGE,
|
||||
WS_MSG_TYPE_MESSAGE,
|
||||
} from "@goauthentik/common/constants";
|
||||
import { SentryIgnoredError } from "@goauthentik/common/sentry";
|
||||
import { SentryIgnoredError } from "@goauthentik/common/errors";
|
||||
import { WSMessage } from "@goauthentik/common/ws";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/messages/Message";
|
||||
|
@ -1,9 +1,5 @@
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import {
|
||||
APIError,
|
||||
parseAPIResponseError,
|
||||
pluckErrorDetail,
|
||||
} from "@goauthentik/common/errors/network";
|
||||
import { APIErrorTypes, parseAPIError } from "@goauthentik/common/errors";
|
||||
import { uiConfig } from "@goauthentik/common/ui/config";
|
||||
import { groupBy } from "@goauthentik/common/utils";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
@ -14,10 +10,9 @@ import "@goauthentik/elements/chips/ChipGroup";
|
||||
import { getURLParam, updateURLParams } from "@goauthentik/elements/router/RouteMatch";
|
||||
import "@goauthentik/elements/table/TablePagination";
|
||||
import "@goauthentik/elements/table/TableSearch";
|
||||
import { SlottedTemplateResult } from "@goauthentik/elements/types";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, css, html, nothing } from "lit";
|
||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
import { property, state } from "lit/decorators.js";
|
||||
import { classMap } from "lit/directives/class-map.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
@ -31,7 +26,7 @@ import PFToolbar from "@patternfly/patternfly/components/Toolbar/toolbar.css";
|
||||
import PFBullseye from "@patternfly/patternfly/layouts/Bullseye/bullseye.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
import { Pagination } from "@goauthentik/api";
|
||||
import { Pagination, ResponseError } from "@goauthentik/api";
|
||||
|
||||
export interface TableLike {
|
||||
order?: string;
|
||||
@ -103,7 +98,7 @@ export interface PaginatedResponse<T> {
|
||||
export abstract class Table<T> extends AKElement implements TableLike {
|
||||
abstract apiEndpoint(): Promise<PaginatedResponse<T>>;
|
||||
abstract columns(): TableColumn[];
|
||||
abstract row(item: T): SlottedTemplateResult[];
|
||||
abstract row(item: T): TemplateResult[];
|
||||
|
||||
private isLoading = false;
|
||||
|
||||
@ -111,12 +106,12 @@ export abstract class Table<T> extends AKElement implements TableLike {
|
||||
return false;
|
||||
}
|
||||
|
||||
renderExpanded(_item: T): SlottedTemplateResult {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
renderExpanded(item: T): TemplateResult {
|
||||
if (this.expandable) {
|
||||
throw new Error("Expandable is enabled but renderExpanded is not overridden!");
|
||||
}
|
||||
|
||||
return nothing;
|
||||
return html``;
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
@ -125,11 +120,10 @@ export abstract class Table<T> extends AKElement implements TableLike {
|
||||
@property({ type: Number })
|
||||
page = getURLParam("tablePage", 1);
|
||||
|
||||
/**
|
||||
* Set if your `selectedElements` use of the selection box is to enable bulk-delete,
|
||||
* so that stale data is cleared out when the API returns a new list minus the deleted entries.
|
||||
/** @prop
|
||||
*
|
||||
* @prop
|
||||
* Set if your `selectedElements` use of the selection box is to enable bulk-delete, so that
|
||||
* stale data is cleared out when the API returns a new list minus the deleted entries.
|
||||
*/
|
||||
@property({ attribute: "clear-on-refresh", type: Boolean, reflect: true })
|
||||
clearOnRefresh = false;
|
||||
@ -168,7 +162,7 @@ export abstract class Table<T> extends AKElement implements TableLike {
|
||||
expandedElements: T[] = [];
|
||||
|
||||
@state()
|
||||
error?: APIError;
|
||||
error?: APIErrorTypes;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
@ -193,12 +187,6 @@ export abstract class Table<T> extends AKElement implements TableLike {
|
||||
.pf-c-toolbar__item .pf-c-input-group {
|
||||
padding: 0 var(--pf-global--spacer--sm);
|
||||
}
|
||||
|
||||
.pf-c-table {
|
||||
--pf-c-table--m-striped__tr--BackgroundColor: var(
|
||||
--pf-global--BackgroundColor--dark-300
|
||||
);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
@ -225,74 +213,64 @@ export abstract class Table<T> extends AKElement implements TableLike {
|
||||
};
|
||||
}
|
||||
|
||||
public groupBy(items: T[]): [SlottedTemplateResult, T[]][] {
|
||||
public groupBy(items: T[]): [string, T[]][] {
|
||||
return groupBy(items, () => {
|
||||
return "";
|
||||
});
|
||||
}
|
||||
|
||||
public async fetch(): Promise<void> {
|
||||
if (this.isLoading) return;
|
||||
|
||||
if (this.isLoading) {
|
||||
return;
|
||||
}
|
||||
this.isLoading = true;
|
||||
|
||||
return this.apiEndpoint()
|
||||
.then((data) => {
|
||||
this.data = data;
|
||||
this.error = undefined;
|
||||
|
||||
this.page = this.data.pagination.current;
|
||||
const newExpanded: T[] = [];
|
||||
|
||||
this.data.results.forEach((res) => {
|
||||
const jsonRes = JSON.stringify(res);
|
||||
// So because we're dealing with complex objects here, we can't use indexOf
|
||||
// since it checks strict equality, and we also can't easily check in findIndex()
|
||||
// Instead we default to comparing the JSON of both objects, which is quite slow
|
||||
// Hence we check if the objects have `pk` attributes set (as most models do)
|
||||
// and compare that instead, which will be much faster.
|
||||
let comp = (item: T) => {
|
||||
return JSON.stringify(item) === jsonRes;
|
||||
try {
|
||||
this.data = await this.apiEndpoint();
|
||||
this.error = undefined;
|
||||
this.page = this.data.pagination.current;
|
||||
const newExpanded: T[] = [];
|
||||
this.data.results.forEach((res) => {
|
||||
const jsonRes = JSON.stringify(res);
|
||||
// So because we're dealing with complex objects here, we can't use indexOf
|
||||
// since it checks strict equality, and we also can't easily check in findIndex()
|
||||
// Instead we default to comparing the JSON of both objects, which is quite slow
|
||||
// Hence we check if the objects have `pk` attributes set (as most models do)
|
||||
// and compare that instead, which will be much faster.
|
||||
let comp = (item: T) => {
|
||||
return JSON.stringify(item) === jsonRes;
|
||||
};
|
||||
if (Object.hasOwn(res as object, "pk")) {
|
||||
comp = (item: T) => {
|
||||
return (
|
||||
(item as unknown as { pk: string | number }).pk ===
|
||||
(res as unknown as { pk: string | number }).pk
|
||||
);
|
||||
};
|
||||
|
||||
if (Object.hasOwn(res as object, "pk")) {
|
||||
comp = (item: T) => {
|
||||
return (
|
||||
(item as unknown as { pk: string | number }).pk ===
|
||||
(res as unknown as { pk: string | number }).pk
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const expandedIndex = this.expandedElements.findIndex(comp);
|
||||
|
||||
if (expandedIndex > -1) {
|
||||
newExpanded.push(res);
|
||||
}
|
||||
});
|
||||
|
||||
this.expandedElements = newExpanded;
|
||||
})
|
||||
.catch(async (error) => {
|
||||
this.error = await parseAPIResponseError(error);
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
this.requestUpdate();
|
||||
}
|
||||
const expandedIndex = this.expandedElements.findIndex(comp);
|
||||
if (expandedIndex > -1) {
|
||||
newExpanded.push(res);
|
||||
}
|
||||
});
|
||||
this.isLoading = false;
|
||||
this.expandedElements = newExpanded;
|
||||
} catch (ex) {
|
||||
this.isLoading = false;
|
||||
this.error = await parseAPIError(ex as Error);
|
||||
}
|
||||
}
|
||||
|
||||
private renderLoading(): TemplateResult {
|
||||
return html`<tr role="row">
|
||||
<td role="cell" colspan="25">
|
||||
<div class="pf-l-bullseye">
|
||||
<ak-empty-state loading header=${msg("Loading")}></ak-empty-state>
|
||||
<ak-empty-state loading header=${msg("Loading")}> </ak-empty-state>
|
||||
</div>
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
|
||||
renderEmpty(inner?: SlottedTemplateResult): TemplateResult {
|
||||
renderEmpty(inner?: TemplateResult): TemplateResult {
|
||||
return html`<tbody role="rowgroup">
|
||||
<tr role="row">
|
||||
<td role="cell" colspan="8">
|
||||
@ -307,16 +285,18 @@ export abstract class Table<T> extends AKElement implements TableLike {
|
||||
</tbody>`;
|
||||
}
|
||||
|
||||
renderObjectCreate(): SlottedTemplateResult {
|
||||
return nothing;
|
||||
renderObjectCreate(): TemplateResult {
|
||||
return html``;
|
||||
}
|
||||
|
||||
renderError(): SlottedTemplateResult {
|
||||
if (!this.error) return nothing;
|
||||
|
||||
return html`<ak-empty-state header="${msg("Failed to fetch objects.")}" icon="fa-ban">
|
||||
<div slot="body">${pluckErrorDetail(this.error)}</div>
|
||||
</ak-empty-state>`;
|
||||
renderError(): TemplateResult {
|
||||
return this.error
|
||||
? html`<ak-empty-state header="${msg("Failed to fetch objects.")}" icon="fa-times">
|
||||
${this.error instanceof ResponseError
|
||||
? html` <div slot="body">${this.error.message}</div> `
|
||||
: html`<div slot="body">${this.error.detail}</div>`}
|
||||
</ak-empty-state>`
|
||||
: html``;
|
||||
}
|
||||
|
||||
private renderRows(): TemplateResult[] | undefined {
|
||||
@ -424,17 +404,15 @@ export abstract class Table<T> extends AKElement implements TableLike {
|
||||
}
|
||||
: itemSelectHandler}
|
||||
>
|
||||
${this.checkbox ? renderCheckbox() : nothing}
|
||||
${this.expandable ? renderExpansion() : nothing}
|
||||
${this.row(item).map((column, columnIndex) => {
|
||||
return html`<td data-column-index="${columnIndex}" role="cell">
|
||||
${column}
|
||||
</td>`;
|
||||
${this.checkbox ? renderCheckbox() : html``}
|
||||
${this.expandable ? renderExpansion() : html``}
|
||||
${this.row(item).map((col) => {
|
||||
return html`<td role="cell">${col}</td>`;
|
||||
})}
|
||||
</tr>
|
||||
<tr class="pf-c-table__expandable-row ${classMap(expandedClass)}" role="row">
|
||||
<td></td>
|
||||
${this.expandedElements.includes(item) ? this.renderExpanded(item) : nothing}
|
||||
${this.expandedElements.includes(item) ? this.renderExpanded(item) : html``}
|
||||
</tr>
|
||||
</tbody>`;
|
||||
});
|
||||
@ -452,12 +430,12 @@ export abstract class Table<T> extends AKElement implements TableLike {
|
||||
>`;
|
||||
}
|
||||
|
||||
renderToolbarSelected(): SlottedTemplateResult {
|
||||
return nothing;
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
return html``;
|
||||
}
|
||||
|
||||
renderToolbarAfter(): SlottedTemplateResult {
|
||||
return nothing;
|
||||
renderToolbarAfter(): TemplateResult {
|
||||
return html``;
|
||||
}
|
||||
|
||||
renderSearch(): TemplateResult {
|
||||
@ -526,9 +504,9 @@ export abstract class Table<T> extends AKElement implements TableLike {
|
||||
* chip-based subtable at the top that shows the list of selected entries. Long text result in
|
||||
* ellipsized chips, which is sub-optimal.
|
||||
*/
|
||||
renderSelectedChip(_item: T): SlottedTemplateResult {
|
||||
renderSelectedChip(_item: T): TemplateResult {
|
||||
// Override this for chip-based displays
|
||||
return nothing;
|
||||
return html``;
|
||||
}
|
||||
|
||||
get needChipGroup() {
|
||||
@ -569,7 +547,7 @@ export abstract class Table<T> extends AKElement implements TableLike {
|
||||
${this.renderToolbarContainer()}
|
||||
<table class="pf-c-table pf-m-compact pf-m-grid-md pf-m-expandable">
|
||||
<thead>
|
||||
<tr role="row" class="pf-c-table__header-row">
|
||||
<tr role="row">
|
||||
${this.checkbox ? this.renderAllOnThisPageCheckbox() : html``}
|
||||
${this.expandable ? html`<td role="cell"></td>` : html``}
|
||||
${this.columns().map((col) => col.render(this))}
|
||||
|
@ -70,57 +70,52 @@ export class AuthenticatorValidateStageWebCode extends BaseDeviceStage<
|
||||
return html`<ak-empty-state loading> </ak-empty-state>`;
|
||||
}
|
||||
return html`<div class="pf-c-login__main-body">
|
||||
<form
|
||||
class="pf-c-form"
|
||||
@submit=${(e: Event) => {
|
||||
this.submitForm(e);
|
||||
}}
|
||||
<form
|
||||
class="pf-c-form"
|
||||
@submit=${(e: Event) => {
|
||||
this.submitForm(e);
|
||||
}}
|
||||
>
|
||||
${this.renderUserInfo()}
|
||||
<div class="icon-description">
|
||||
<i class="fa ${this.deviceIcon()}" aria-hidden="true"></i>
|
||||
<p>${this.deviceMessage()}</p>
|
||||
</div>
|
||||
<ak-form-element
|
||||
label="${this.deviceChallenge?.deviceClass === DeviceClassesEnum.Static
|
||||
? msg("Static token")
|
||||
: msg("Authentication code")}"
|
||||
required
|
||||
class="pf-c-form__group"
|
||||
.errors=${(this.challenge?.responseErrors || {})["code"]}
|
||||
>
|
||||
${this.renderUserInfo()}
|
||||
<div class="icon-description">
|
||||
<i class="fa ${this.deviceIcon()}" aria-hidden="true"></i>
|
||||
<p>${this.deviceMessage()}</p>
|
||||
</div>
|
||||
<ak-form-element
|
||||
label="${this.deviceChallenge?.deviceClass === DeviceClassesEnum.Static
|
||||
? msg("Static token")
|
||||
: msg("Authentication code")}"
|
||||
<!-- @ts-ignore -->
|
||||
<input
|
||||
type="text"
|
||||
name="code"
|
||||
inputmode="${this.deviceChallenge?.deviceClass === DeviceClassesEnum.Static
|
||||
? "text"
|
||||
: "numeric"}"
|
||||
pattern="${this.deviceChallenge?.deviceClass === DeviceClassesEnum.Static
|
||||
? "[0-9a-zA-Z]*"
|
||||
: "[0-9]*"}"
|
||||
placeholder="${msg("Please enter your code")}"
|
||||
autofocus=""
|
||||
autocomplete="one-time-code"
|
||||
class="pf-c-form-control"
|
||||
value="${PasswordManagerPrefill.totp || ""}"
|
||||
required
|
||||
class="pf-c-form__group"
|
||||
.errors=${(this.challenge?.responseErrors || {})["code"]}
|
||||
>
|
||||
<!-- @ts-ignore -->
|
||||
<input
|
||||
type="text"
|
||||
name="code"
|
||||
inputmode="${this.deviceChallenge?.deviceClass ===
|
||||
DeviceClassesEnum.Static
|
||||
? "text"
|
||||
: "numeric"}"
|
||||
pattern="${this.deviceChallenge?.deviceClass ===
|
||||
DeviceClassesEnum.Static
|
||||
? "[0-9a-zA-Z]*"
|
||||
: "[0-9]*"}"
|
||||
placeholder="${msg("Please enter your code")}"
|
||||
autofocus=""
|
||||
autocomplete="one-time-code"
|
||||
class="pf-c-form-control"
|
||||
value="${PasswordManagerPrefill.totp || ""}"
|
||||
required
|
||||
/>
|
||||
</ak-form-element>
|
||||
/>
|
||||
</ak-form-element>
|
||||
|
||||
<div class="pf-c-form__group pf-m-action">
|
||||
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
|
||||
${msg("Continue")}
|
||||
</button>
|
||||
${this.renderReturnToDevicePicker()}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<footer class="pf-c-login__main-footer">
|
||||
<ul class="pf-c-login__main-footer-links"></ul>
|
||||
</footer>`;
|
||||
<div class="pf-c-form__group pf-m-action">
|
||||
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
|
||||
${msg("Continue")}
|
||||
</button>
|
||||
${this.renderReturnToDevicePicker()}
|
||||
</div>
|
||||
</form>
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,21 +165,13 @@ class UserInterfacePresentation extends AKElement {
|
||||
}
|
||||
|
||||
return html`<a
|
||||
class="pf-c-button pf-m-secondary pf-m-small pf-u-display-none pf-u-display-block-on-md"
|
||||
href="${globalAK().api.base}if/admin/"
|
||||
slot="extra"
|
||||
>
|
||||
${msg("Admin interface")}
|
||||
</a>
|
||||
<a
|
||||
class="pf-c-button pf-m-secondary pf-m-small pf-u-display-none-on-md pf-u-display-block"
|
||||
href="${globalAK().api.base}if/admin/"
|
||||
slot="extra"
|
||||
>
|
||||
${msg("Admin")}
|
||||
</a>`;
|
||||
class="pf-c-button pf-m-secondary pf-m-small pf-u-display-none pf-u-display-block-on-md"
|
||||
href="${globalAK().api.base}if/admin/"
|
||||
slot="extra"
|
||||
>
|
||||
${msg("Admin interface")}
|
||||
</a>`;
|
||||
}
|
||||
|
||||
render() {
|
||||
// The `!` in the field definitions above only re-assure typescript and eslint that the
|
||||
// values *should* be available, not that they *are*. Thus this contract check; it asserts
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { SentryIgnoredError } from "@goauthentik/common/sentry";
|
||||
import { SentryIgnoredError } from "@goauthentik/common/errors";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { AndNext, DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { SentryIgnoredError } from "@goauthentik/common/errors";
|
||||
import { globalAK } from "@goauthentik/common/global";
|
||||
import { deviceTypeName } from "@goauthentik/common/labels";
|
||||
import { SentryIgnoredError } from "@goauthentik/common/sentry";
|
||||
import { getRelativeTime } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/elements/buttons/Dropdown";
|
||||
import "@goauthentik/elements/buttons/ModalButton";
|
||||
|
@ -4908,6 +4908,16 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<source>When enabled, global Email connection settings will be used and connection settings below will be ignored.</source>
|
||||
<target>Wenn diese Option aktiviert ist, werden die globalen E-Mail Verbindungseinstellungen benutzt und die unten angegebenen Einstellungen ignoriert</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sb1fe947f9ad27b9d">
|
||||
<source>Token expiry</source>
|
||||
<target>Ablauf des Tokens</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c6ba8d100453392">
|
||||
<source>Time in minutes the token sent is valid.</source>
|
||||
<target>Zeit in Minuten wie lange der verschickte Token gültig ist</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="se47baf2fd16b9d2b">
|
||||
<source>Template</source>
|
||||
@ -9036,33 +9046,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5787e20cab57b383">
|
||||
<source>Unknown error</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa3fc920064fc8380">
|
||||
<source>Time the token sent is valid.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3483d693c72e412e">
|
||||
<source>Compatibility Mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se7195530fa981896">
|
||||
<source>Default behavior.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sfa5ba219a0991116">
|
||||
<source>AWS</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s44e80dc905ab2ecc">
|
||||
<source>Altered behavior for usage with Amazon Web Services.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2f5e2bcd583fbc27">
|
||||
<source>Slack</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7f40ef46b7f213ea">
|
||||
<source>Altered behavior for usage with Slack.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s054cb3c0a84eefe5">
|
||||
<source>Alter authentik's behavior for vendor-specific SCIM implementations.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -4001,6 +4001,14 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<source>When enabled, global Email connection settings will be used and connection settings below will be ignored.</source>
|
||||
<target>When enabled, global Email connection settings will be used and connection settings below will be ignored.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb1fe947f9ad27b9d">
|
||||
<source>Token expiry</source>
|
||||
<target>Token expiry</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c6ba8d100453392">
|
||||
<source>Time in minutes the token sent is valid.</source>
|
||||
<target>Time in minutes the token sent is valid.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="se47baf2fd16b9d2b">
|
||||
<source>Template</source>
|
||||
<target>Template</target>
|
||||
@ -7565,33 +7573,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5787e20cab57b383">
|
||||
<source>Unknown error</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa3fc920064fc8380">
|
||||
<source>Time the token sent is valid.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3483d693c72e412e">
|
||||
<source>Compatibility Mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se7195530fa981896">
|
||||
<source>Default behavior.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sfa5ba219a0991116">
|
||||
<source>AWS</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s44e80dc905ab2ecc">
|
||||
<source>Altered behavior for usage with Amazon Web Services.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2f5e2bcd583fbc27">
|
||||
<source>Slack</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7f40ef46b7f213ea">
|
||||
<source>Altered behavior for usage with Slack.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s054cb3c0a84eefe5">
|
||||
<source>Alter authentik's behavior for vendor-specific SCIM implementations.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -4931,6 +4931,16 @@ no se aprueba cuando una o ambas de las opciones seleccionadas son iguales o sup
|
||||
<source>When enabled, global Email connection settings will be used and connection settings below will be ignored.</source>
|
||||
<target>Cuando se habilita, se utilizará la configuración global de conexión de correo electrónico y se ignorarán las configuraciones de conexión que se indican a continuación</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sb1fe947f9ad27b9d">
|
||||
<source>Token expiry</source>
|
||||
<target>Caducidad del token</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c6ba8d100453392">
|
||||
<source>Time in minutes the token sent is valid.</source>
|
||||
<target>El tiempo en minutos que se envía el token es válido.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="se47baf2fd16b9d2b">
|
||||
<source>Template</source>
|
||||
@ -9128,33 +9138,6 @@ Las vinculaciones a grupos o usuarios se comparan con el usuario del evento.</ta
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5787e20cab57b383">
|
||||
<source>Unknown error</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa3fc920064fc8380">
|
||||
<source>Time the token sent is valid.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3483d693c72e412e">
|
||||
<source>Compatibility Mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se7195530fa981896">
|
||||
<source>Default behavior.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sfa5ba219a0991116">
|
||||
<source>AWS</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s44e80dc905ab2ecc">
|
||||
<source>Altered behavior for usage with Amazon Web Services.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2f5e2bcd583fbc27">
|
||||
<source>Slack</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7f40ef46b7f213ea">
|
||||
<source>Altered behavior for usage with Slack.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s054cb3c0a84eefe5">
|
||||
<source>Alter authentik's behavior for vendor-specific SCIM implementations.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -4999,6 +4999,16 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<source>When enabled, global Email connection settings will be used and connection settings below will be ignored.</source>
|
||||
<target>Si activé, les paramètres globaux de connexion courriel seront utilisés et les paramètres de connexion ci-dessous seront ignorés.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sb1fe947f9ad27b9d">
|
||||
<source>Token expiry</source>
|
||||
<target>Expiration du jeton</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c6ba8d100453392">
|
||||
<source>Time in minutes the token sent is valid.</source>
|
||||
<target>Temps en minutes durant lequel le jeton envoyé est valide.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="se47baf2fd16b9d2b">
|
||||
<source>Template</source>
|
||||
@ -9611,63 +9621,21 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
|
||||
</trans-unit>
|
||||
<trans-unit id="s34661765d81e7340">
|
||||
<source>Successfully cleared application cache</source>
|
||||
<target>Cache d'application vidé avec succès</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9377ae285bc0157c">
|
||||
<source>Failed to delete application cache</source>
|
||||
<target>Impossible de vider le cache d'application</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s754153d9e524ffce">
|
||||
<source>Clear Application cache</source>
|
||||
<target>Vider le cache d'application</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sea5092df2c72b064">
|
||||
<source>Are you sure you want to clear the application cache? This will cause all policies to be re-evaluated on their next usage.</source>
|
||||
<target>Êtes-vous sûr de vouloir vider le cache des applications ? Cela entraînera la réévaluation de toutes les politiques lors de leur prochaine utilisation.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1367dcd6e8749fcd">
|
||||
<source>No name set</source>
|
||||
<target>Aucun nom défini</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
<target>Afficher les utilisateurs inactifs</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5787e20cab57b383">
|
||||
<source>Unknown error</source>
|
||||
<target>Erreur inconnue</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa3fc920064fc8380">
|
||||
<source>Time the token sent is valid.</source>
|
||||
<target>Temps durant lequel le jeton envoyé est valide.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3483d693c72e412e">
|
||||
<source>Compatibility Mode</source>
|
||||
<target>Mode de compatibilité</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="se7195530fa981896">
|
||||
<source>Default behavior.</source>
|
||||
<target>Comportement par défaut.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sfa5ba219a0991116">
|
||||
<source>AWS</source>
|
||||
<target>AWS</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s44e80dc905ab2ecc">
|
||||
<source>Altered behavior for usage with Amazon Web Services.</source>
|
||||
<target>Comportement spécifique pour utilisation avec Amazon Web Services.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2f5e2bcd583fbc27">
|
||||
<source>Slack</source>
|
||||
<target>Slack</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7f40ef46b7f213ea">
|
||||
<source>Altered behavior for usage with Slack.</source>
|
||||
<target>Comportement spécifique pour utilisation avec Slack.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s054cb3c0a84eefe5">
|
||||
<source>Alter authentik's behavior for vendor-specific SCIM implementations.</source>
|
||||
<target>Change le comportement d'authentik en fonction des spécificités d'implémentations des fournisseurs SCIM.</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -4998,6 +4998,16 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<source>When enabled, global Email connection settings will be used and connection settings below will be ignored.</source>
|
||||
<target>Se abilitato, verranno utilizzate le impostazioni di connessione e -mail globali e le impostazioni di connessione di seguito verranno ignorate.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sb1fe947f9ad27b9d">
|
||||
<source>Token expiry</source>
|
||||
<target>Scadenza token</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c6ba8d100453392">
|
||||
<source>Time in minutes the token sent is valid.</source>
|
||||
<target>Il tempo in pochi minuti il token inviato è valido.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="se47baf2fd16b9d2b">
|
||||
<source>Template</source>
|
||||
@ -9479,33 +9489,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5787e20cab57b383">
|
||||
<source>Unknown error</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa3fc920064fc8380">
|
||||
<source>Time the token sent is valid.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3483d693c72e412e">
|
||||
<source>Compatibility Mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se7195530fa981896">
|
||||
<source>Default behavior.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sfa5ba219a0991116">
|
||||
<source>AWS</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s44e80dc905ab2ecc">
|
||||
<source>Altered behavior for usage with Amazon Web Services.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2f5e2bcd583fbc27">
|
||||
<source>Slack</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7f40ef46b7f213ea">
|
||||
<source>Altered behavior for usage with Slack.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s054cb3c0a84eefe5">
|
||||
<source>Alter authentik's behavior for vendor-specific SCIM implementations.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -4945,6 +4945,16 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<source>When enabled, global Email connection settings will be used and connection settings below will be ignored.</source>
|
||||
<target>활성화하면, 전역 이메일 연결 설정이 사용되며 아래의 연결 설정은 무시됩니다.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sb1fe947f9ad27b9d">
|
||||
<source>Token expiry</source>
|
||||
<target>토큰 유효기간</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c6ba8d100453392">
|
||||
<source>Time in minutes the token sent is valid.</source>
|
||||
<target>전송한 토큰이 유효한 시간입니다.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="se47baf2fd16b9d2b">
|
||||
<source>Template</source>
|
||||
@ -9035,33 +9045,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5787e20cab57b383">
|
||||
<source>Unknown error</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa3fc920064fc8380">
|
||||
<source>Time the token sent is valid.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3483d693c72e412e">
|
||||
<source>Compatibility Mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se7195530fa981896">
|
||||
<source>Default behavior.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sfa5ba219a0991116">
|
||||
<source>AWS</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s44e80dc905ab2ecc">
|
||||
<source>Altered behavior for usage with Amazon Web Services.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2f5e2bcd583fbc27">
|
||||
<source>Slack</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7f40ef46b7f213ea">
|
||||
<source>Altered behavior for usage with Slack.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s054cb3c0a84eefe5">
|
||||
<source>Alter authentik's behavior for vendor-specific SCIM implementations.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -4955,6 +4955,16 @@ slaagt niet wanneer een of beide geselecteerde opties gelijk zijn aan of boven d
|
||||
<source>When enabled, global Email connection settings will be used and connection settings below will be ignored.</source>
|
||||
<target>Indien ingeschakeld, worden de wereldwijde e-mail verbindingsinstellingen gebruikt en worden de onderstaande verbindingsinstellingen genegeerd.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sb1fe947f9ad27b9d">
|
||||
<source>Token expiry</source>
|
||||
<target>Token-verval</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c6ba8d100453392">
|
||||
<source>Time in minutes the token sent is valid.</source>
|
||||
<target>Tijd in minuten dat het verzonden token geldig is.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="se47baf2fd16b9d2b">
|
||||
<source>Template</source>
|
||||
@ -8937,33 +8947,6 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5787e20cab57b383">
|
||||
<source>Unknown error</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa3fc920064fc8380">
|
||||
<source>Time the token sent is valid.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3483d693c72e412e">
|
||||
<source>Compatibility Mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se7195530fa981896">
|
||||
<source>Default behavior.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sfa5ba219a0991116">
|
||||
<source>AWS</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s44e80dc905ab2ecc">
|
||||
<source>Altered behavior for usage with Amazon Web Services.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2f5e2bcd583fbc27">
|
||||
<source>Slack</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7f40ef46b7f213ea">
|
||||
<source>Altered behavior for usage with Slack.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s054cb3c0a84eefe5">
|
||||
<source>Alter authentik's behavior for vendor-specific SCIM implementations.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -5001,6 +5001,16 @@ Można tu używać tylko zasad, ponieważ dostęp jest sprawdzany przed uwierzyt
|
||||
<source>When enabled, global Email connection settings will be used and connection settings below will be ignored.</source>
|
||||
<target>Po włączeniu będą używane globalne ustawienia połączenia poczty e-mail, a poniższe ustawienia połączenia będą ignorowane.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sb1fe947f9ad27b9d">
|
||||
<source>Token expiry</source>
|
||||
<target>Token wygasa</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c6ba8d100453392">
|
||||
<source>Time in minutes the token sent is valid.</source>
|
||||
<target>Czas w minutach, w którym wysłany token jest ważny.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="se47baf2fd16b9d2b">
|
||||
<source>Template</source>
|
||||
@ -9365,33 +9375,6 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5787e20cab57b383">
|
||||
<source>Unknown error</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa3fc920064fc8380">
|
||||
<source>Time the token sent is valid.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3483d693c72e412e">
|
||||
<source>Compatibility Mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se7195530fa981896">
|
||||
<source>Default behavior.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sfa5ba219a0991116">
|
||||
<source>AWS</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s44e80dc905ab2ecc">
|
||||
<source>Altered behavior for usage with Amazon Web Services.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2f5e2bcd583fbc27">
|
||||
<source>Slack</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7f40ef46b7f213ea">
|
||||
<source>Altered behavior for usage with Slack.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s054cb3c0a84eefe5">
|
||||
<source>Alter authentik's behavior for vendor-specific SCIM implementations.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -4968,6 +4968,16 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<source>When enabled, global Email connection settings will be used and connection settings below will be ignored.</source>
|
||||
<target>Ŵĥēń ēńàƀĺēď, ĝĺōƀàĺ Ēḿàĩĺ ćōńńēćţĩōń śēţţĩńĝś ŵĩĺĺ ƀē ũśēď àńď ćōńńēćţĩōń śēţţĩńĝś ƀēĺōŵ ŵĩĺĺ ƀē ĩĝńōŕēď.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sb1fe947f9ad27b9d">
|
||||
<source>Token expiry</source>
|
||||
<target>Ţōķēń ēxƥĩŕŷ</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c6ba8d100453392">
|
||||
<source>Time in minutes the token sent is valid.</source>
|
||||
<target>Ţĩḿē ĩń ḿĩńũţēś ţĥē ţōķēń śēńţ ĩś vàĺĩď.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="se47baf2fd16b9d2b">
|
||||
<source>Template</source>
|
||||
@ -9372,31 +9382,4 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5787e20cab57b383">
|
||||
<source>Unknown error</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa3fc920064fc8380">
|
||||
<source>Time the token sent is valid.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3483d693c72e412e">
|
||||
<source>Compatibility Mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se7195530fa981896">
|
||||
<source>Default behavior.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sfa5ba219a0991116">
|
||||
<source>AWS</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s44e80dc905ab2ecc">
|
||||
<source>Altered behavior for usage with Amazon Web Services.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2f5e2bcd583fbc27">
|
||||
<source>Slack</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7f40ef46b7f213ea">
|
||||
<source>Altered behavior for usage with Slack.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s054cb3c0a84eefe5">
|
||||
<source>Alter authentik's behavior for vendor-specific SCIM implementations.</source>
|
||||
</trans-unit>
|
||||
</body></file></xliff>
|
||||
|
@ -5000,6 +5000,16 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<source>When enabled, global Email connection settings will be used and connection settings below will be ignored.</source>
|
||||
<target>Если эта функция включена, будут использоваться глобальные настройки подключения к электронной почте, а настройки подключения, указанные ниже, будут игнорироваться.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sb1fe947f9ad27b9d">
|
||||
<source>Token expiry</source>
|
||||
<target>Срок действия токена</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c6ba8d100453392">
|
||||
<source>Time in minutes the token sent is valid.</source>
|
||||
<target>Время в минутах, в течение которого отправленный токен действителен.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="se47baf2fd16b9d2b">
|
||||
<source>Template</source>
|
||||
@ -9398,33 +9408,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5787e20cab57b383">
|
||||
<source>Unknown error</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa3fc920064fc8380">
|
||||
<source>Time the token sent is valid.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3483d693c72e412e">
|
||||
<source>Compatibility Mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se7195530fa981896">
|
||||
<source>Default behavior.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sfa5ba219a0991116">
|
||||
<source>AWS</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s44e80dc905ab2ecc">
|
||||
<source>Altered behavior for usage with Amazon Web Services.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2f5e2bcd583fbc27">
|
||||
<source>Slack</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7f40ef46b7f213ea">
|
||||
<source>Altered behavior for usage with Slack.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s054cb3c0a84eefe5">
|
||||
<source>Alter authentik's behavior for vendor-specific SCIM implementations.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -4968,6 +4968,16 @@ Belirlenen seçeneklerden biri veya her ikisi de eşiğe eşit veya eşiğin üz
|
||||
<source>When enabled, global Email connection settings will be used and connection settings below will be ignored.</source>
|
||||
<target>Etkinleştirildiğinde, genel E-posta bağlantısı ayarları kullanılır ve aşağıdaki bağlantı ayarları yoksayılır.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sb1fe947f9ad27b9d">
|
||||
<source>Token expiry</source>
|
||||
<target>Belirteç son kullanma tarihi</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c6ba8d100453392">
|
||||
<source>Time in minutes the token sent is valid.</source>
|
||||
<target>Gönderilen belirtecin dakika cinsinden geçerlilik süresi.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="se47baf2fd16b9d2b">
|
||||
<source>Template</source>
|
||||
@ -9428,33 +9438,6 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5787e20cab57b383">
|
||||
<source>Unknown error</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa3fc920064fc8380">
|
||||
<source>Time the token sent is valid.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3483d693c72e412e">
|
||||
<source>Compatibility Mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se7195530fa981896">
|
||||
<source>Default behavior.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sfa5ba219a0991116">
|
||||
<source>AWS</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s44e80dc905ab2ecc">
|
||||
<source>Altered behavior for usage with Amazon Web Services.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2f5e2bcd583fbc27">
|
||||
<source>Slack</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7f40ef46b7f213ea">
|
||||
<source>Altered behavior for usage with Slack.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s054cb3c0a84eefe5">
|
||||
<source>Alter authentik's behavior for vendor-specific SCIM implementations.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -3564,6 +3564,12 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<trans-unit id="sae1e1a59d22609c4">
|
||||
<source>When enabled, global Email connection settings will be used and connection settings below will be ignored.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb1fe947f9ad27b9d">
|
||||
<source>Token expiry</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c6ba8d100453392">
|
||||
<source>Time in minutes the token sent is valid.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se47baf2fd16b9d2b">
|
||||
<source>Template</source>
|
||||
</trans-unit>
|
||||
@ -6170,33 +6176,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5787e20cab57b383">
|
||||
<source>Unknown error</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa3fc920064fc8380">
|
||||
<source>Time the token sent is valid.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3483d693c72e412e">
|
||||
<source>Compatibility Mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se7195530fa981896">
|
||||
<source>Default behavior.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sfa5ba219a0991116">
|
||||
<source>AWS</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s44e80dc905ab2ecc">
|
||||
<source>Altered behavior for usage with Amazon Web Services.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2f5e2bcd583fbc27">
|
||||
<source>Slack</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7f40ef46b7f213ea">
|
||||
<source>Altered behavior for usage with Slack.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s054cb3c0a84eefe5">
|
||||
<source>Alter authentik's behavior for vendor-specific SCIM implementations.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0"?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
|
||||
<?xml version="1.0" ?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
|
||||
<file target-language="zh-Hans" source-language="en" original="lit-localize-inputs" datatype="plaintext">
|
||||
<body>
|
||||
<trans-unit id="s4caed5b7a7e5d89b">
|
||||
@ -596,9 +596,9 @@
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="saa0e2675da69651b">
|
||||
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
|
||||
<target>未找到 URL "
|
||||
<x id="0" equiv-text="${this.url}"/>"。</target>
|
||||
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
|
||||
<target>未找到 URL "
|
||||
<x id="0" equiv-text="${this.url}"/>"。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s58cd9c2fe836d9c6">
|
||||
@ -1715,8 +1715,8 @@
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sa90b7809586c35ce">
|
||||
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
|
||||
<target>输入完整 URL、相对路径,或者使用 'fa://fa-test' 来使用 Font Awesome 图标 "fa-test"。</target>
|
||||
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
|
||||
<target>输入完整 URL、相对路径,或者使用 'fa://fa-test' 来使用 Font Awesome 图标 "fa-test"。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s0410779cb47de312">
|
||||
@ -2864,8 +2864,8 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s76768bebabb7d543">
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
|
||||
<target>包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...'</target>
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
|
||||
<target>包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...'</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s026555347e589f0e">
|
||||
@ -3783,10 +3783,10 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sa95a538bfbb86111">
|
||||
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
|
||||
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
|
||||
<target>您确定要更新
|
||||
<x id="0" equiv-text="${this.objectLabel}"/>"
|
||||
<x id="1" equiv-text="${this.obj?.name}"/>" 吗?</target>
|
||||
<x id="0" equiv-text="${this.objectLabel}"/>"
|
||||
<x id="1" equiv-text="${this.obj?.name}"/>" 吗?</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sc92d7cfb6ee1fec6">
|
||||
@ -4857,7 +4857,7 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sdf1d8edef27236f0">
|
||||
<source>A "roaming" authenticator, like a YubiKey</source>
|
||||
<source>A "roaming" authenticator, like a YubiKey</source>
|
||||
<target>像 YubiKey 这样的“漫游”身份验证器</target>
|
||||
|
||||
</trans-unit>
|
||||
@ -5000,6 +5000,16 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<source>When enabled, global Email connection settings will be used and connection settings below will be ignored.</source>
|
||||
<target>启用后,将使用全局电子邮件连接设置,下面的连接设置将被忽略。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sb1fe947f9ad27b9d">
|
||||
<source>Token expiry</source>
|
||||
<target>令牌过期</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c6ba8d100453392">
|
||||
<source>Time in minutes the token sent is valid.</source>
|
||||
<target>发出令牌的有效时间(单位为分钟)。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="se47baf2fd16b9d2b">
|
||||
<source>Template</source>
|
||||
@ -5216,7 +5226,7 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s1608b2f94fa0dbd4">
|
||||
<source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
|
||||
<source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
|
||||
<target>如果设置时长大于 0,用户可以选择“保持登录”选项,这将使用户的会话延长此处设置的时间。</target>
|
||||
|
||||
</trans-unit>
|
||||
@ -7507,7 +7517,7 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<target>成功创建用户并添加到组 <x id="0" equiv-text="${this.group.name}"/></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s824e0943a7104668">
|
||||
<source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
|
||||
<source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
|
||||
<target>此用户将会被添加到组 &quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&quot;。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s62e7f6ed7d9cb3ca">
|
||||
@ -8801,7 +8811,7 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<target>同步组</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2d5f69929bb7221d">
|
||||
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
|
||||
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
|
||||
<target><x id="0" equiv-text="${p.name}"/>(&quot;<x id="1" equiv-text="${p.fieldKey}"/>&quot;,类型为 <x id="2" equiv-text="${p.type}"/>)</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s25bacc19d98b444e">
|
||||
@ -9049,8 +9059,8 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<target>授权流程成功后有效的重定向 URI。还可以在此处为隐式流程指定任何来源。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4c49d27de60a532b">
|
||||
<source>To allow any redirect URI, set the mode to Regex and the value to ".*". Be aware of the possible security implications this can have.</source>
|
||||
<target>要允许任何重定向 URI,请设置模式为正则表达式,并将此值设置为 ".*"。请注意这可能带来的安全影响。</target>
|
||||
<source>To allow any redirect URI, set the mode to Regex and the value to ".*". Be aware of the possible security implications this can have.</source>
|
||||
<target>要允许任何重定向 URI,请设置模式为正则表达式,并将此值设置为 ".*"。请注意这可能带来的安全影响。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa52bf79fe1ccb13e">
|
||||
<source>Federated OIDC Sources</source>
|
||||
@ -9633,43 +9643,7 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
<target>显示不活跃的用户</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5787e20cab57b383">
|
||||
<source>Unknown error</source>
|
||||
<target>未知错误</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa3fc920064fc8380">
|
||||
<source>Time the token sent is valid.</source>
|
||||
<target>发出令牌的有效时间。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3483d693c72e412e">
|
||||
<source>Compatibility Mode</source>
|
||||
<target>兼容模式</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="se7195530fa981896">
|
||||
<source>Default behavior.</source>
|
||||
<target>默认行为。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sfa5ba219a0991116">
|
||||
<source>AWS</source>
|
||||
<target>AWS</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s44e80dc905ab2ecc">
|
||||
<source>Altered behavior for usage with Amazon Web Services.</source>
|
||||
<target>更改行为以使用 Amazon Web 服务。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2f5e2bcd583fbc27">
|
||||
<source>Slack</source>
|
||||
<target>Slack</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7f40ef46b7f213ea">
|
||||
<source>Altered behavior for usage with Slack.</source>
|
||||
<target>更改行为以使用 Slack。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s054cb3c0a84eefe5">
|
||||
<source>Alter authentik's behavior for vendor-specific SCIM implementations.</source>
|
||||
<target>更改 authentik 的行为,以兼容特定厂商的 SCIM 实现。</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
</xliff>
|
@ -3791,6 +3791,14 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<source>When enabled, global Email connection settings will be used and connection settings below will be ignored.</source>
|
||||
<target>启用后,将使用全局电子邮件连接设置,而下面的连接设置将被忽略。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb1fe947f9ad27b9d">
|
||||
<source>Token expiry</source>
|
||||
<target>令牌到期</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c6ba8d100453392">
|
||||
<source>Time in minutes the token sent is valid.</source>
|
||||
<target>发送的令牌的有效时间(以分钟为单位)。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="se47baf2fd16b9d2b">
|
||||
<source>Template</source>
|
||||
<target>“模板”</target>
|
||||
@ -7265,33 +7273,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5787e20cab57b383">
|
||||
<source>Unknown error</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa3fc920064fc8380">
|
||||
<source>Time the token sent is valid.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3483d693c72e412e">
|
||||
<source>Compatibility Mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se7195530fa981896">
|
||||
<source>Default behavior.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sfa5ba219a0991116">
|
||||
<source>AWS</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s44e80dc905ab2ecc">
|
||||
<source>Altered behavior for usage with Amazon Web Services.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2f5e2bcd583fbc27">
|
||||
<source>Slack</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7f40ef46b7f213ea">
|
||||
<source>Altered behavior for usage with Slack.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s054cb3c0a84eefe5">
|
||||
<source>Alter authentik's behavior for vendor-specific SCIM implementations.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -5000,6 +5000,16 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<source>When enabled, global Email connection settings will be used and connection settings below will be ignored.</source>
|
||||
<target>启用后,将使用全局电子邮件连接设置,下面的连接设置将被忽略。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sb1fe947f9ad27b9d">
|
||||
<source>Token expiry</source>
|
||||
<target>令牌过期</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c6ba8d100453392">
|
||||
<source>Time in minutes the token sent is valid.</source>
|
||||
<target>发出令牌的有效时间(单位为分钟)。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="se47baf2fd16b9d2b">
|
||||
<source>Template</source>
|
||||
@ -9633,42 +9643,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
<target>显示不活跃的用户</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5787e20cab57b383">
|
||||
<source>Unknown error</source>
|
||||
<target>未知错误</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa3fc920064fc8380">
|
||||
<source>Time the token sent is valid.</source>
|
||||
<target>发出令牌的有效时间。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3483d693c72e412e">
|
||||
<source>Compatibility Mode</source>
|
||||
<target>兼容模式</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="se7195530fa981896">
|
||||
<source>Default behavior.</source>
|
||||
<target>默认行为。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sfa5ba219a0991116">
|
||||
<source>AWS</source>
|
||||
<target>AWS</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s44e80dc905ab2ecc">
|
||||
<source>Altered behavior for usage with Amazon Web Services.</source>
|
||||
<target>更改行为以使用 Amazon Web 服务。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2f5e2bcd583fbc27">
|
||||
<source>Slack</source>
|
||||
<target>Slack</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7f40ef46b7f213ea">
|
||||
<source>Altered behavior for usage with Slack.</source>
|
||||
<target>更改行为以使用 Slack。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s054cb3c0a84eefe5">
|
||||
<source>Alter authentik's behavior for vendor-specific SCIM implementations.</source>
|
||||
<target>更改 authentik 的行为,以兼容特定厂商的 SCIM 实现。</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -4936,6 +4936,16 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<source>When enabled, global Email connection settings will be used and connection settings below will be ignored.</source>
|
||||
<target>啟用時,將使用全域電子郵件連線設定,以下的連線設定將被忽略。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sb1fe947f9ad27b9d">
|
||||
<source>Token expiry</source>
|
||||
<target>權杖有效期限</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s1c6ba8d100453392">
|
||||
<source>Time in minutes the token sent is valid.</source>
|
||||
<target>發送權杖的有效期限(分鐘為單位)。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="se47baf2fd16b9d2b">
|
||||
<source>Template</source>
|
||||
@ -9012,33 +9022,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5787e20cab57b383">
|
||||
<source>Unknown error</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa3fc920064fc8380">
|
||||
<source>Time the token sent is valid.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3483d693c72e412e">
|
||||
<source>Compatibility Mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se7195530fa981896">
|
||||
<source>Default behavior.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sfa5ba219a0991116">
|
||||
<source>AWS</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s44e80dc905ab2ecc">
|
||||
<source>Altered behavior for usage with Amazon Web Services.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2f5e2bcd583fbc27">
|
||||
<source>Slack</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7f40ef46b7f213ea">
|
||||
<source>Altered behavior for usage with Slack.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s054cb3c0a84eefe5">
|
||||
<source>Alter authentik's behavior for vendor-specific SCIM implementations.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -6,12 +6,12 @@ If you want to only make changes on the UI, you don't need a backend running fro
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Node.js (any recent version should work; we use 22.x to build)
|
||||
- Node.js (any recent version should work; we use 20.x to build)
|
||||
- Make (again, any recent version should work)
|
||||
- Docker and Docker Compose
|
||||
|
||||
:::info
|
||||
Depending on platform, some native dependencies might be required. On macOS, run `brew install node@22`, and for Docker `brew install --cask docker`
|
||||
Depending on platform, some native dependencies might be required. On macOS, run `brew install node@20`, and for Docker `brew install --cask docker`
|
||||
:::
|
||||
|
||||
### Instructions
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user