Merge branch 'main' into application-wizard-2-with-api-and-tests

* main: (41 commits)
  lifecycle: fix install_id migration not running (#7116)
  core: bump Go from 1.20 to 1.21 (#7117)
  providers/ldap: add windows adsi support (#7098)
  web: bump API Client version (#7113)
  translate: Updates for file web/xliff/en.xlf in zh-Hans on branch main (#7112)
  translate: Updates for file web/xliff/en.xlf in zh_CN on branch main (#7111)
  web: bump the wdio group in /tests/wdio with 4 updates (#7108)
  core/api: add uuid field to core api user http response (#7110)
  core: bump goauthentik.io/api/v3 from 3.2023083.4 to 3.2023083.5 (#7105)
  core: bump golang.org/x/oauth2 from 0.12.0 to 0.13.0 (#7106)
  web: bump the eslint group in /tests/wdio with 1 update (#7107)
  providers/proxy: improve SLO by backchannel logging out sessions (#7099)
  web: bump @rollup/plugin-node-resolve from 15.2.2 to 15.2.3 in /web (#7104)
  web: bump the eslint group in /web with 1 update (#7103)
  web: bump the storybook group in /web with 1 update (#7102)
  web: bump API Client version (#7101)
  providers/saml: add default RelayState value for IDP-initiated requests (#7100)
  lifecycle: improve reliability of system migrations (#7089)
  sources/ldap: fix attribute path resolution (#7090)
  root: Ignore the vendor folder (#7094)
  ...
This commit is contained in:
Ken Sternberg 2023-10-09 12:44:39 -07:00
commit 478258d88f
75 changed files with 1179 additions and 593 deletions

View file

@ -29,7 +29,7 @@ jobs:
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: v1.52.2
version: v1.54.2
args: --timeout 5000s --verbose
skip-cache: true
test-unittest:

3
.gitignore vendored
View file

@ -206,3 +206,6 @@ data/
.netlify
.ruff_cache
source_docs/
### Golang ###
/vendor/

View file

@ -35,7 +35,7 @@ COPY ./gen-ts-api /work/web/node_modules/@goauthentik/api
RUN npm run build
# Stage 3: Build go proxy
FROM docker.io/golang:1.21.1-bookworm AS go-builder
FROM docker.io/golang:1.21.2-bookworm AS go-builder
WORKDIR /go/src/goauthentik.io

View file

@ -81,7 +81,7 @@ dev-drop-db:
dropdb -U ${pg_user} -h ${pg_host} ${pg_name}
# Also remove the test-db if it exists
dropdb -U ${pg_user} -h ${pg_host} test_${pg_name} || true
echo redis-cli -n 0 flushall
redis-cli -n 0 flushall
dev-create-db:
createdb -U ${pg_user} -h ${pg_host} ${pg_name}

View file

@ -190,6 +190,7 @@ class UserSerializer(ModelSerializer):
"uid",
"path",
"type",
"uuid",
]
extra_kwargs = {
"name": {"allow_blank": True},

View file

@ -206,8 +206,8 @@ def prefill_task(func):
task_call_module=func.__module__,
task_call_func=func.__name__,
# We don't have real values for these attributes but they cannot be null
start_timestamp=default_timer(),
finish_timestamp=default_timer(),
start_timestamp=0,
finish_timestamp=0,
finish_time=datetime.now(),
).save(86400)
LOGGER.debug("prefilled task", task_name=func.__name__)

View file

@ -8,6 +8,11 @@ GAUGE_FLOWS_CACHED = Gauge(
"authentik_flows_cached",
"Cached flows",
)
HIST_FLOW_EXECUTION_STAGE_TIME = Histogram(
"authentik_flows_execution_stage_time",
"Duration each stage took to execute.",
["stage_type", "method"],
)
HIST_FLOWS_PLAN_TIME = Histogram(
"authentik_flows_plan_time",
"Duration to build a plan for a flow",

View file

@ -24,6 +24,7 @@ from structlog.stdlib import BoundLogger, get_logger
from authentik.core.models import Application
from authentik.events.models import Event, EventAction, cleanse_dict
from authentik.flows.apps import HIST_FLOW_EXECUTION_STAGE_TIME
from authentik.flows.challenge import (
Challenge,
ChallengeResponse,
@ -266,17 +267,21 @@ class FlowExecutorView(APIView):
)
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
"""Get the next pending challenge from the currently active flow."""
class_path = class_to_path(self.current_stage_view.__class__)
self._logger.debug(
"f(exec): Passing GET",
view_class=class_to_path(self.current_stage_view.__class__),
view_class=class_path,
stage=self.current_stage,
)
try:
with Hub.current.start_span(
op="authentik.flow.executor.stage",
description=class_to_path(self.current_stage_view.__class__),
) as span:
span.set_data("Method", "GET")
description=class_path,
) as span, HIST_FLOW_EXECUTION_STAGE_TIME.labels(
method=request.method.upper(),
stage_type=class_path,
).time():
span.set_data("Method", request.method.upper())
span.set_data("authentik Stage", self.current_stage_view)
span.set_data("authentik Flow", self.flow.slug)
stage_response = self.current_stage_view.dispatch(request)
@ -310,17 +315,21 @@ class FlowExecutorView(APIView):
)
def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
"""Solve the previously retrieved challenge and advanced to the next stage."""
class_path = class_to_path(self.current_stage_view.__class__)
self._logger.debug(
"f(exec): Passing POST",
view_class=class_to_path(self.current_stage_view.__class__),
view_class=class_path,
stage=self.current_stage,
)
try:
with Hub.current.start_span(
op="authentik.flow.executor.stage",
description=class_to_path(self.current_stage_view.__class__),
) as span:
span.set_data("Method", "POST")
description=class_path,
) as span, HIST_FLOW_EXECUTION_STAGE_TIME.labels(
method=request.method.upper(),
stage_type=class_path,
).time():
span.set_data("Method", request.method.upper())
span.set_data("authentik Stage", self.current_stage_view)
span.set_data("authentik Flow", self.flow.slug)
stage_response = self.current_stage_view.dispatch(request)

View file

@ -24,7 +24,7 @@ ENVIRONMENT = os.getenv(f"{ENV_PREFIX}_ENV", "local")
def get_path_from_dict(root: dict, path: str, sep=".", default=None) -> Any:
"""Recursively walk through `root`, checking each part of `path` split by `sep`.
"""Recursively walk through `root`, checking each part of `path` separated by `sep`.
If at any point a dict does not exist, return default"""
for comp in path.split(sep):
if root and comp in root:
@ -34,7 +34,19 @@ def get_path_from_dict(root: dict, path: str, sep=".", default=None) -> Any:
return root
@dataclass
def set_path_in_dict(root: dict, path: str, value: Any, sep="."):
"""Recursively walk through `root`, checking each part of `path` separated by `sep`
and setting the last value to `value`"""
# Walk each component of the path
path_parts = path.split(sep)
for comp in path_parts[:-1]:
if comp not in root:
root[comp] = {}
root = root.get(comp, {})
root[path_parts[-1]] = value
@dataclass(slots=True)
class Attr:
"""Single configuration attribute"""
@ -55,6 +67,10 @@ class Attr:
# to the config file containing this change or the file containing this value
source: Optional[str] = field(default=None)
def __post_init__(self):
if isinstance(self.value, Attr):
raise RuntimeError(f"config Attr with nested Attr for source {self.source}")
class AttrEncoder(JSONEncoder):
"""JSON encoder that can deal with `Attr` classes"""
@ -227,15 +243,7 @@ class ConfigLoader:
def set(self, path: str, value: Any, sep="."):
"""Set value using same syntax as get()"""
# Walk sub_dicts before parsing path
root = self.raw
# Walk each component of the path
path_parts = path.split(sep)
for comp in path_parts[:-1]:
if comp not in root:
root[comp] = {}
root = root.get(comp, {})
root[path_parts[-1]] = Attr(value)
set_path_in_dict(self.raw, path, Attr(value), sep=sep)
CONFIG = ConfigLoader()

View file

@ -27,6 +27,9 @@ class WebsocketMessageInstruction(IntEnum):
# Message sent by us to trigger an Update
TRIGGER_UPDATE = 2
# Provider specific message
PROVIDER_SPECIFIC = 3
@dataclass(slots=True)
class WebsocketMessage:
@ -131,3 +134,14 @@ class OutpostConsumer(AuthJsonConsumer):
self.send_json(
asdict(WebsocketMessage(instruction=WebsocketMessageInstruction.TRIGGER_UPDATE))
)
def event_provider_specific(self, event):
"""Event handler which can be called by provider-specific
implementations to send specific messages to the outpost"""
self.send_json(
asdict(
WebsocketMessage(
instruction=WebsocketMessageInstruction.PROVIDER_SPECIFIC, args=event
)
)
)

View file

@ -5,7 +5,6 @@ from socket import gethostname
from typing import Any, Optional
from urllib.parse import urlparse
import yaml
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from django.core.cache import cache
@ -16,6 +15,7 @@ from docker.constants import DEFAULT_UNIX_SOCKET
from kubernetes.config.incluster_config import SERVICE_TOKEN_FILENAME
from kubernetes.config.kube_config import KUBE_CONFIG_DEFAULT_LOCATION
from structlog.stdlib import get_logger
from yaml import safe_load
from authentik.events.monitored_tasks import (
MonitoredTask,
@ -279,7 +279,7 @@ def outpost_connection_discovery(self: MonitoredTask):
with kubeconfig_path.open("r", encoding="utf8") as _kubeconfig:
KubernetesServiceConnection.objects.create(
name=kubeconfig_local_name,
kubeconfig=yaml.safe_load(_kubeconfig),
kubeconfig=safe_load(_kubeconfig),
)
unix_socket_path = urlparse(DEFAULT_UNIX_SOCKET).path
socket = Path(unix_socket_path)

View file

@ -7,7 +7,11 @@ GAUGE_POLICIES_CACHED = Gauge(
"authentik_policies_cached",
"Cached Policies",
)
HIST_POLICIES_ENGINE_TOTAL_TIME = Histogram(
"authentik_policies_engine_time_total_seconds",
"(Total) Duration the policy engine took to evaluate a result.",
["obj_type", "obj_pk"],
)
HIST_POLICIES_EXECUTION_TIME = Histogram(
"authentik_policies_execution_time",
"Execution times for single policies",
@ -17,6 +21,7 @@ HIST_POLICIES_EXECUTION_TIME = Histogram(
"binding_target_name",
"object_pk",
"object_type",
"mode",
],
)

View file

@ -1,6 +1,7 @@
"""authentik policy engine"""
from multiprocessing import Pipe, current_process
from multiprocessing.connection import Connection
from timeit import default_timer
from typing import Iterator, Optional
from django.core.cache import cache
@ -10,6 +11,8 @@ from sentry_sdk.tracing import Span
from structlog.stdlib import BoundLogger, get_logger
from authentik.core.models import User
from authentik.lib.utils.reflection import class_to_path
from authentik.policies.apps import HIST_POLICIES_ENGINE_TOTAL_TIME, HIST_POLICIES_EXECUTION_TIME
from authentik.policies.exceptions import PolicyEngineException
from authentik.policies.models import Policy, PolicyBinding, PolicyBindingModel, PolicyEngineMode
from authentik.policies.process import PolicyProcess, cache_key
@ -77,6 +80,33 @@ class PolicyEngine:
if binding.policy is not None and binding.policy.__class__ == Policy:
raise PolicyEngineException(f"Policy '{binding.policy}' is root type")
def _check_cache(self, binding: PolicyBinding):
if not self.use_cache:
return False
before = default_timer()
key = cache_key(binding, self.request)
cached_policy = cache.get(key, None)
duration = max(default_timer() - before, 0)
if not cached_policy:
return False
self.logger.debug(
"P_ENG: Taking result from cache",
binding=binding,
cache_key=key,
request=self.request,
)
HIST_POLICIES_EXECUTION_TIME.labels(
binding_order=binding.order,
binding_target_type=binding.target_type,
binding_target_name=binding.target_name,
object_pk=str(self.request.obj.pk),
object_type=class_to_path(self.request.obj.__class__),
mode="cache_retrieve",
).observe(duration)
# It's a bit silly to time this, but
self.__cached_policies.append(cached_policy)
return True
def build(self) -> "PolicyEngine":
"""Build wrapper which monitors performance"""
with (
@ -84,6 +114,10 @@ class PolicyEngine:
op="authentik.policy.engine.build",
description=self.__pbm,
) as span,
HIST_POLICIES_ENGINE_TOTAL_TIME.labels(
obj_type=class_to_path(self.__pbm.__class__),
obj_pk=str(self.__pbm.pk),
).time(),
):
span: Span
span.set_data("pbm", self.__pbm)
@ -92,16 +126,7 @@ class PolicyEngine:
self.__expected_result_count += 1
self._check_policy_type(binding)
key = cache_key(binding, self.request)
cached_policy = cache.get(key, None)
if cached_policy and self.use_cache:
self.logger.debug(
"P_ENG: Taking result from cache",
binding=binding,
cache_key=key,
request=self.request,
)
self.__cached_policies.append(cached_policy)
if self._check_cache(binding):
continue
self.logger.debug("P_ENG: Evaluating policy", binding=binding, request=self.request)
our_end, task_end = Pipe(False)

View file

@ -11,6 +11,7 @@ from structlog.stdlib import get_logger
from authentik.events.models import Event, EventAction
from authentik.lib.config import CONFIG
from authentik.lib.utils.errors import exception_to_string
from authentik.lib.utils.reflection import class_to_path
from authentik.policies.apps import HIST_POLICIES_EXECUTION_TIME
from authentik.policies.exceptions import PolicyException
from authentik.policies.models import PolicyBinding
@ -128,9 +129,8 @@ class PolicyProcess(PROCESS_CLASS):
binding_target_type=self.binding.target_type,
binding_target_name=self.binding.target_name,
object_pk=str(self.request.obj.pk),
object_type=(
f"{self.request.obj._meta.app_label}.{self.request.obj._meta.model_name}"
),
object_type=class_to_path(self.request.obj.__class__),
mode="execute_process",
).time(),
):
span: Span

View file

@ -17,7 +17,7 @@ LOGGER = get_logger()
@receiver(monitoring_set)
def monitoring_set_policies(sender, **kwargs):
"""set policy gauges"""
GAUGE_POLICIES_CACHED.set(len(cache.keys(f"{CACHE_PREFIX}_*") or []))
GAUGE_POLICIES_CACHED.set(len(cache.keys(f"{CACHE_PREFIX}*") or []))
@receiver(post_save, sender=Policy)

View file

@ -9,3 +9,7 @@ class AuthentikProviderProxyConfig(ManagedAppConfig):
label = "authentik_providers_proxy"
verbose_name = "authentik Providers.Proxy"
default = True
def reconcile_load_providers_proxy_signals(self):
"""Load proxy signals"""
self.import_module("authentik.providers.proxy.signals")

View file

@ -0,0 +1,20 @@
"""Proxy provider signals"""
from django.contrib.auth.signals import user_logged_out
from django.db.models.signals import pre_delete
from django.dispatch import receiver
from django.http import HttpRequest
from authentik.core.models import AuthenticatedSession, User
from authentik.providers.proxy.tasks import proxy_on_logout
@receiver(user_logged_out)
def logout_proxy_revoke_direct(sender: type[User], request: HttpRequest, **_):
"""Catch logout by direct logout and forward to proxy providers"""
proxy_on_logout.delay(request.session.session_key)
@receiver(pre_delete, sender=AuthenticatedSession)
def logout_proxy_revoke(sender: type[AuthenticatedSession], instance: AuthenticatedSession, **_):
"""Catch logout by expiring sessions being deleted"""
proxy_on_logout.delay(instance.session_key)

View file

@ -1,6 +1,9 @@
"""proxy provider tasks"""
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from django.db import DatabaseError, InternalError, ProgrammingError
from authentik.outposts.models import Outpost, OutpostState, OutpostType
from authentik.providers.proxy.models import ProxyProvider
from authentik.root.celery import CELERY_APP
@ -13,3 +16,20 @@ def proxy_set_defaults():
for provider in ProxyProvider.objects.all():
provider.set_oauth_defaults()
provider.save()
@CELERY_APP.task()
def proxy_on_logout(session_id: str):
"""Update outpost instances connected to a single outpost"""
layer = get_channel_layer()
for outpost in Outpost.objects.filter(type=OutpostType.PROXY):
for state in OutpostState.for_outpost(outpost):
for channel in state.channel_ids:
async_to_sync(layer.send)(
channel,
{
"type": "event.provider.specific",
"sub_type": "logout",
"session_id": session_id,
},
)

View file

@ -146,6 +146,7 @@ class SAMLProviderSerializer(ProviderSerializer):
"signing_kp",
"verification_kp",
"sp_binding",
"default_relay_state",
"url_download_metadata",
"url_sso_post",
"url_sso_redirect",

View file

@ -0,0 +1,21 @@
# Generated by Django 4.2.6 on 2023-10-08 20:29
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_providers_saml", "0012_managed"),
]
operations = [
migrations.AddField(
model_name="samlprovider",
name="default_relay_state",
field=models.TextField(
blank=True,
default="",
help_text="Default relay_state value for IDP-initiated logins",
),
),
]

View file

@ -138,6 +138,10 @@ class SAMLProvider(Provider):
verbose_name=_("Signing Keypair"),
)
default_relay_state = models.TextField(
default="", blank=True, help_text=_("Default relay_state value for IDP-initiated logins")
)
@property
def launch_url(self) -> Optional[str]:
"""Use IDP-Initiated SAML flow as launch URL"""

View file

@ -175,4 +175,7 @@ class AuthNRequestParser:
def idp_initiated(self) -> AuthNRequest:
"""Create IdP Initiated AuthNRequest"""
return AuthNRequest()
relay_state = None
if self.provider.default_relay_state != "":
relay_state = self.provider.default_relay_state
return AuthNRequest(relay_state=relay_state)

View file

@ -8,6 +8,7 @@ from authentik.blueprints.tests import apply_blueprint
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
from authentik.crypto.models import CertificateKeyPair
from authentik.events.models import Event, EventAction
from authentik.lib.generators import generate_id
from authentik.lib.tests.utils import get_request
from authentik.providers.saml.models import SAMLPropertyMapping, SAMLProvider
from authentik.providers.saml.processors.assertion import AssertionProcessor
@ -264,3 +265,10 @@ class TestAuthNRequest(TestCase):
events.first().context["message"],
"Failed to evaluate property-mapping: 'test'",
)
def test_idp_initiated(self):
"""Test IDP-initiated login"""
self.provider.default_relay_state = generate_id()
request = AuthNRequestParser(self.provider).idp_initiated()
self.assertEqual(request.id, None)
self.assertEqual(request.relay_state, self.provider.default_relay_state)

View file

@ -1,8 +1,10 @@
"""Integrate ./manage.py test with pytest"""
import os
from argparse import ArgumentParser
from unittest import TestCase
from django.conf import settings
from django.test.runner import DiscoverRunner
from authentik.lib.config import CONFIG
from authentik.lib.sentry import sentry_init
@ -12,13 +14,11 @@ from tests.e2e.utils import get_docker_tag
TestCase.maxDiff = None
class PytestTestRunner: # pragma: no cover
class PytestTestRunner(DiscoverRunner): # pragma: no cover
"""Runs pytest to discover and run tests."""
def __init__(self, verbosity=1, failfast=False, keepdb=False, **kwargs):
self.verbosity = verbosity
self.failfast = failfast
self.keepdb = keepdb
super().__init__(verbosity, failfast, keepdb, **kwargs)
self.args = []
if self.failfast:
@ -47,16 +47,61 @@ class PytestTestRunner: # pragma: no cover
@classmethod
def add_arguments(cls, parser: ArgumentParser):
"""Add more pytest-specific arguments"""
parser.add_argument("--randomly-seed", type=int)
parser.add_argument("--keepdb", action="store_true")
parser.add_argument(
"--randomly-seed",
type=int,
help="Set the seed that pytest-randomly uses (int), or pass the special value 'last'"
"to reuse the seed from the previous run."
"Default behaviour: use random.Random().getrandbits(32), so the seed is"
"different on each run.",
)
parser.add_argument(
"--keepdb", action="store_true", help="Preserves the test DB between runs."
)
def run_tests(self, test_labels):
def run_tests(self, test_labels, extra_tests=None, **kwargs):
"""Run pytest and return the exitcode.
It translates some of Django's test command option to pytest's.
It is supported to only run specific classes and methods using
a dotted module name i.e. foo.bar[.Class[.method]]
The extra_tests argument has been deprecated since Django 5.x
It is kept for compatibility with PyCharm's Django test runner.
"""
for label in test_labels:
valid_label_found = False
label_as_path = os.path.abspath(label)
# File path has been specified
if os.path.exists(label_as_path):
self.args.append(label_as_path)
valid_label_found = True
else:
# Already correctly formatted test found (file_path::class::method)
if "::" in label:
self.args.append(label)
valid_label_found = True
# Convert dotted module path to file_path::class::method
else:
path_pieces = label.split(".")
# Check whether only class or class and method are specified
for i in range(-1, -3, -1):
path = os.path.join(*path_pieces[:i]) + ".py"
label_as_path = os.path.abspath(path)
if os.path.exists(label_as_path):
path_method = label_as_path + "::" + "::".join(path_pieces[i:])
self.args.append(path_method)
valid_label_found = True
break
if not valid_label_found:
raise RuntimeError(
f"One of the test labels: {label!r}, "
f"is not supported. Use a dotted module name or "
f"path instead."
)
import pytest
self.args.extend(test_labels)
return pytest.main(self.args)

View file

@ -9,7 +9,7 @@ from structlog.stdlib import BoundLogger, get_logger
from authentik.core.exceptions import PropertyMappingExpressionException
from authentik.events.models import Event, EventAction
from authentik.lib.config import CONFIG
from authentik.lib.config import CONFIG, set_path_in_dict
from authentik.lib.merge import MERGE_LIST_UNIQUE
from authentik.sources.ldap.auth import LDAP_DISTINGUISHED_NAME
from authentik.sources.ldap.models import LDAPPropertyMapping, LDAPSource
@ -164,7 +164,7 @@ class BaseLDAPSynchronizer:
if object_field.startswith("attributes."):
# Because returning a list might desired, we can't
# rely on self._flatten here. Instead, just save the result as-is
properties["attributes"][object_field.replace("attributes.", "")] = value
set_path_in_dict(properties, object_field, value)
else:
properties[object_field] = self._flatten(value)
except PropertyMappingExpressionException as exc:

View file

@ -14,6 +14,7 @@ from authentik.core.sources.stage import PLAN_CONTEXT_SOURCES_CONNECTION
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
from authentik.flows.stage import StageView
from authentik.flows.views.executor import FlowExecutorView
from authentik.lib.config import set_path_in_dict
from authentik.stages.password import BACKEND_INBUILT
from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
@ -44,12 +45,7 @@ class UserWriteStageView(StageView):
# this is just a sanity check to ensure that is removed
if parts[0] == "attributes":
parts = parts[1:]
attrs = user.attributes
for comp in parts[:-1]:
if comp not in attrs:
attrs[comp] = {}
attrs = attrs.get(comp)
attrs[parts[-1]] = value
set_path_in_dict(user.attributes, ".".join(parts), value)
def ensure_user(self) -> tuple[Optional[User], bool]:
"""Ensure a user exists"""

View file

@ -4826,6 +4826,11 @@
],
"title": "Service Provider Binding",
"description": "This determines how authentik sends the response back to the Service Provider."
},
"default_relay_state": {
"type": "string",
"title": "Default relay state",
"description": "Default relay_state value for IDP-initiated logins"
}
},
"required": []
@ -7427,146 +7432,32 @@
"model_authentik_stages_invitation.invitation": {
"type": "object",
"properties": {
"name": {
"type": "string",
"maxLength": 50,
"minLength": 1,
"pattern": "^[-a-zA-Z0-9_]+$",
"title": "Name"
},
"expires": {
"type": "string",
"format": "date-time",
"title": "Expires"
},
"user": {
"fixed_data": {
"type": "object",
"properties": {
"username": {
"type": "string",
"maxLength": 150,
"minLength": 1,
"title": "Username"
},
"name": {
"type": "string",
"title": "Name",
"description": "User's display name."
},
"is_active": {
"type": "boolean",
"title": "Active",
"description": "Designates whether this user should be treated as active. Unselect this instead of deleting accounts."
},
"last_login": {
"type": [
"string",
"null"
],
"format": "date-time",
"title": "Last login"
},
"groups": {
"type": "array",
"items": {
"type": "integer"
},
"title": "Groups"
},
"email": {
"type": "string",
"format": "email",
"maxLength": 254,
"title": "Email address"
},
"attributes": {
"type": "object",
"additionalProperties": true,
"title": "Attributes"
},
"path": {
"type": "string",
"minLength": 1,
"title": "Path"
},
"type": {
"type": "string",
"enum": [
"internal",
"external",
"service_account",
"internal_service_account"
],
"title": "Type"
}
},
"required": [
"username",
"name"
],
"title": "User"
"additionalProperties": true,
"title": "Fixed data"
},
"application": {
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"title": "Name",
"description": "Application's display Name."
},
"slug": {
"type": "string",
"maxLength": 50,
"minLength": 1,
"pattern": "^[-a-zA-Z0-9_]+$",
"title": "Slug",
"description": "Internal application name, used in URLs."
},
"provider": {
"type": "integer",
"title": "Provider"
},
"backchannel_providers": {
"type": "array",
"items": {
"type": "integer"
},
"title": "Backchannel providers"
},
"open_in_new_tab": {
"type": "boolean",
"title": "Open in new tab",
"description": "Open launch URL in a new browser tab or window."
},
"meta_launch_url": {
"type": "string",
"title": "Meta launch url"
},
"meta_description": {
"type": "string",
"title": "Meta description"
},
"meta_publisher": {
"type": "string",
"title": "Meta publisher"
},
"policy_engine_mode": {
"type": "string",
"enum": [
"all",
"any"
],
"title": "Policy engine mode"
},
"group": {
"type": "string",
"title": "Group"
}
},
"required": [
"name",
"slug"
],
"title": "Application"
"single_use": {
"type": "boolean",
"title": "Single use",
"description": "When enabled, the invitation will be deleted after usage."
},
"permissions": {
"type": "string",
"minLength": 1,
"title": "Permissions"
"flow": {
"type": "integer",
"title": "Flow",
"description": "When set, only the configured flow can use this invitation."
}
},
"required": []

View file

@ -15,6 +15,7 @@ entries:
# This mapping is used by the authentik proxy. It passes extra user attributes,
# which are used for example for the HTTP-Basic Authentication mapping.
return {
"sid": request.http_request.session.session_key,
"ak_proxy": {
"user_attributes": request.user.group_attributes(request),
"is_superuser": request.user.is_superuser,

18
go.mod
View file

@ -1,12 +1,12 @@
module goauthentik.io
go 1.20
go 1.21
require (
beryju.io/ldap v0.1.0
github.com/Netflix/go-env v0.0.0-20210215222557-e437a7e7f9fb
github.com/coreos/go-oidc v2.2.1+incompatible
github.com/getsentry/sentry-go v0.24.1
github.com/getsentry/sentry-go v0.25.0
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
github.com/go-ldap/ldap/v3 v3.4.6
github.com/go-openapi/runtime v0.26.0
@ -19,6 +19,7 @@ require (
github.com/gorilla/sessions v1.2.1
github.com/gorilla/websocket v1.5.0
github.com/jellydator/ttlcache/v3 v3.1.0
github.com/mitchellh/mapstructure v1.5.0
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
github.com/pires/go-proxyproto v0.7.0
github.com/prometheus/client_golang v1.17.0
@ -26,10 +27,10 @@ require (
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.7.0
github.com/stretchr/testify v1.8.4
goauthentik.io/api/v3 v3.2023083.4
goauthentik.io/api/v3 v3.2023083.5
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
golang.org/x/oauth2 v0.12.0
golang.org/x/sync v0.3.0
golang.org/x/oauth2 v0.13.0
golang.org/x/sync v0.4.0
gopkg.in/yaml.v2 v2.4.0
layeh.com/radius v0.0.0-20210819152912-ad72663a72ab
)
@ -60,7 +61,6 @@ require (
github.com/josharian/intern v1.0.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
@ -72,9 +72,9 @@ require (
go.mongodb.org/mongo-driver v1.11.3 // indirect
go.opentelemetry.io/otel v1.14.0 // indirect
go.opentelemetry.io/otel/trace v1.14.0 // indirect
golang.org/x/crypto v0.13.0 // indirect
golang.org/x/net v0.15.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/net v0.16.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.31.0 // indirect

36
go.sum
View file

@ -49,7 +49,9 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:W
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@ -73,11 +75,12 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/getsentry/sentry-go v0.24.1 h1:W6/0GyTy8J6ge6lVCc94WB6Gx2ZuLrgopnn9w8Hiwuk=
github.com/getsentry/sentry-go v0.24.1/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
github.com/getsentry/sentry-go v0.25.0 h1:q6Eo+hS+yoJlTO3uu/azhQadsD8V+jQn2D8VvX1eOyI=
github.com/getsentry/sentry-go v0.25.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA=
github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@ -196,6 +199,7 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@ -242,6 +246,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@ -269,6 +274,7 @@ github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -294,6 +300,7 @@ github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
@ -343,11 +350,13 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM=
go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU=
go.opentelemetry.io/otel/sdk v1.14.0 h1:PDCppFRDq8A1jL9v6KMI6dYesaq+DFcDZvjsoGvxGzY=
go.opentelemetry.io/otel/sdk v1.14.0/go.mod h1:bwIC5TjrNG6QDCHNWvW4HLHtUQ4I+VQDsnjhvyZCALM=
go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M=
go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
goauthentik.io/api/v3 v3.2023083.4 h1:WIi2+LFfBTvhxcbH/WqvhY/4EsX8bAN6mrPODq02B/w=
goauthentik.io/api/v3 v3.2023083.4/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
goauthentik.io/api/v3 v3.2023083.5 h1:U9/+QWIVpsfZCZivMAsG6E6dq3/4wT5qt/k7uUC9rZc=
goauthentik.io/api/v3 v3.2023083.5/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
@ -359,8 +368,9 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
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=
@ -427,16 +437,16 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos=
golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
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=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4=
golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4=
golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY=
golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -449,8 +459,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -490,8 +500,9 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@ -645,6 +656,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w=
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=

View file

@ -22,6 +22,8 @@ import (
log "github.com/sirupsen/logrus"
)
type WSHandler func(ctx context.Context, args map[string]interface{})
const ConfigLogLevel = "log_level"
// APIController main controller which connects to the authentik api via http and ws
@ -42,6 +44,7 @@ type APIController struct {
lastWsReconnect time.Time
wsIsReconnecting bool
wsBackoffMultiplier int
wsHandlers []WSHandler
refreshHandlers []func()
instanceUUID uuid.UUID
@ -106,6 +109,7 @@ func NewAPIController(akURL url.URL, token string) *APIController {
reloadOffset: time.Duration(rand.Intn(10)) * time.Second,
instanceUUID: uuid.New(),
Outpost: outpost,
wsHandlers: []WSHandler{},
wsBackoffMultiplier: 1,
refreshHandlers: make([]func(), 0),
}
@ -156,6 +160,10 @@ func (a *APIController) AddRefreshHandler(handler func()) {
a.refreshHandlers = append(a.refreshHandlers, handler)
}
func (a *APIController) AddWSHandler(handler WSHandler) {
a.wsHandlers = append(a.wsHandlers, handler)
}
func (a *APIController) OnRefresh() error {
// Because we don't know the outpost UUID, we simply do a list and pick the first
// The service account this token belongs to should only have access to a single outpost

View file

@ -1,6 +1,7 @@
package ak
import (
"context"
"crypto/tls"
"fmt"
"net/http"
@ -145,6 +146,10 @@ func (ac *APIController) startWSHandler() {
"build": constants.BUILD("tagged"),
}).SetToCurrentTime()
}
} else if wsMsg.Instruction == WebsocketInstructionProviderSpecific {
for _, h := range ac.wsHandlers {
h(context.Background(), wsMsg.Args)
}
}
}
}

View file

@ -9,6 +9,8 @@ const (
WebsocketInstructionHello websocketInstruction = 1
// WebsocketInstructionTriggerUpdate Code received to trigger a config update
WebsocketInstructionTriggerUpdate websocketInstruction = 2
// WebsocketInstructionProviderSpecific Code received to trigger some provider specific function
WebsocketInstructionProviderSpecific websocketInstruction = 3
)
type websocketMessage struct {

View file

@ -25,6 +25,7 @@ const (
)
const (
OCPerson = "person"
OCUser = "user"
OCOrgPerson = "organizationalPerson"
OCInetOrgPerson = "inetOrgPerson"
@ -54,6 +55,8 @@ func GetContainerOCs() map[string]bool {
func GetUserOCs() map[string]bool {
return map[string]bool{
OCTop: true,
OCPerson: true,
OCUser: true,
OCOrgPerson: true,
OCInetOrgPerson: true,

View file

@ -31,8 +31,8 @@ func (pi *ProviderInstance) UserEntry(u api.User) *ldap.Entry {
u.Email = api.PtrString("")
}
attrs = utils.EnsureAttributes(attrs, map[string][]string{
"ak-active": {strconv.FormatBool(*u.IsActive)},
"ak-superuser": {strconv.FormatBool(u.IsSuperuser)},
"ak-active": {strings.ToUpper(strconv.FormatBool(*u.IsActive))},
"ak-superuser": {strings.ToUpper(strconv.FormatBool(u.IsSuperuser))},
"memberOf": pi.GroupsForUser(u),
"cn": {u.Username},
"sAMAccountName": {u.Username},
@ -41,11 +41,13 @@ func (pi *ProviderInstance) UserEntry(u api.User) *ldap.Entry {
"displayName": {u.Name},
"mail": {*u.Email},
"objectClass": {
constants.OCUser,
constants.OCTop,
constants.OCPerson,
constants.OCOrgPerson,
constants.OCInetOrgPerson,
constants.OCAKUser,
constants.OCUser,
constants.OCPosixAccount,
constants.OCAKUser,
},
"uidNumber": {pi.GetUidNumber(u)},
"gidNumber": {pi.GetUidNumber(u)},

View file

@ -33,6 +33,29 @@ func (ds *DirectSearcher) SearchBase(req *search.Request) (ldap.ServerSearchResu
Name: "supportedLDAPVersion",
Values: []string{"3"},
},
{
Name: "supportedCapabilities",
Values: []string{
"1.2.840.113556.1.4.800", //LDAP_CAP_ACTIVE_DIRECTORY_OID
"1.2.840.113556.1.4.1791", //LDAP_CAP_ACTIVE_DIRECTORY_LDAP_INTEG_OID
"1.2.840.113556.1.4.1670", //LDAP_CAP_ACTIVE_DIRECTORY_V51_OID
"1.2.840.113556.1.4.1880", //LDAP_CAP_ACTIVE_DIRECTORY_ADAM_DIGEST_OID
"1.2.840.113556.1.4.1851", //LDAP_CAP_ACTIVE_DIRECTORY_ADAM_OID
"1.2.840.113556.1.4.1920", //LDAP_CAP_ACTIVE_DIRECTORY_PARTIAL_SECRETS_OID
"1.2.840.113556.1.4.1935", //LDAP_CAP_ACTIVE_DIRECTORY_V60_OID
"1.2.840.113556.1.4.2080", //LDAP_CAP_ACTIVE_DIRECTORY_V61_R2_OID
"1.2.840.113556.1.4.2237", //LDAP_CAP_ACTIVE_DIRECTORY_W8_OID
},
},
{
Name: "supportedControl",
Values: []string{
"2.16.840.1.113730.3.4.9", //VLV Request LDAPv3 Control
"2.16.840.1.113730.3.4.10", //VLV Response LDAPv3 Control
"1.2.840.113556.1.4.474", //Sort result
"1.2.840.113556.1.4.319", //Paged Result Control
},
},
{
Name: "subschemaSubentry",
Values: []string{"cn=subschema"},

View file

@ -29,62 +29,80 @@ func (ds *DirectSearcher) SearchSubschema(req *search.Request) (ldap.ServerSearc
},
},
{
Name: "objectClasses",
Name: "dITContentRules",
Values: []string{
"( 2.5.6.0 NAME 'top' ABSTRACT MUST ( objectClass ) MAY (cn $ description $ displayName $ memberOf $ name ) )",
"( 2.5.6.6 NAME 'person' SUP top STRUCTURAL MUST ( cn ) MAY (sn $ telephoneNumber ) )",
"( 2.5.6.7 NAME 'organizationalPerson' SUP person STRUCTURAL MAY (c $ l $ o $ ou $ title $ givenName $ co $ department $ company $ division $ mail $ mobile $ telephoneNumber ) )",
"( 2.5.6.9 NAME 'groupOfNames' SUP top STRUCTURAL MUST (cn $ member ) MAY (o $ ou ) )",
"( 1.2.840.113556.1.5.9 NAME 'user' SUP organizationalPerson STRUCTURAL MAY ( name $ displayName $ uid $ mail ) )",
"( 1.3.6.1.1.1.2.0 NAME 'posixAccount' SUP top AUXILIARY MAY (cn $ description $ homeDirectory $ uid $ uidNumber $ gidNumber ) )",
"( 2.16.840.1.113730.3.2.2 NAME 'inetOrgPerson' AUX ( posixAccount ) MUST ( sAMAccountName ) MAY ( uidNumber $ gidNumber ))",
// Custom attributes
// Temporarily use 1.3.6.1.4.1.26027.1.1 as a base
// https://docs.oracle.com/cd/E19450-01/820-6169/working-with-object-identifiers.html#obtaining-a-base-oid
"( 1.3.6.1.4.1.26027.1.1.1 NAME 'goauthentik.io/ldap/user' SUP organizationalPerson STRUCTURAL MAY ( ak-active $ sAMAccountName $ goauthentikio-user-sources $ goauthentik.io/user/sources $ goauthentik.io/ldap/active $ goauthentik.io/ldap/superuser ) )",
"( 2.5.6.0 NAME 'top' )",
"( 2.5.6.6 NAME 'person' )",
"( 2.5.6.7 NAME 'organizationalPerson' )",
"( 2.5.6.9 NAME 'groupOfNames' )",
"( 1.2.840.113556.1.5.9 NAME 'user' )",
"( 1.3.6.1.1.1.2.0 NAME 'posixAccount' )",
"( 2.16.840.1.113730.3.2.2 NAME 'inetOrgPerson' )",
"( 1.3.6.1.4.1.26027.1.1.1 NAME 'goauthentik.io/ldap/user' )",
},
},
{
Name: "attributeTypes",
Values: []string{
"( 2.5.4.0 NAME 'objectClass' DESC 'RFC4512: object classes of the entity' EQUALITY objectIdentifierMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )",
"( 1.3.6.1.4.1.1466.101.120.5 NAME 'namingContexts' DESC 'RFC4512: naming contexts' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 USAGE dSAOperation )",
"( 2.5.18.10 NAME 'subschemaSubentry' DESC 'RFC4512: name of controlling subschema entry' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
"( 1.3.6.1.4.1.1466.101.120.15 NAME 'supportedLDAPVersion' DESC 'RFC4512: supported LDAP versions' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 USAGE dSAOperation )",
"( 1.3.6.1.1.20 NAME 'entryDN' DESC 'DN of the entry' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
"( 1.3.6.1.1.4 NAME 'vendorName' DESC 'RFC3045: name of implementation vendor' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE NO-USER-MODIFICATION USAGE dSAOperation )",
"( 1.3.6.1.1.5 NAME 'vendorVersion' DESC 'RFC3045: version of implementation' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE NO-USER-MODIFICATION USAGE dSAOperation )",
"( 0.9.2342.19200300.100.1.1 NAME 'uid' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 0.9.2342.19200300.100.1.3 NAME 'mail' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 0.9.2342.19200300.100.1.41 NAME 'mobile' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.2.840.113556.1.2.102 NAME 'memberOf' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' NO-USER-MODIFICATION )",
"( 1.2.840.113556.1.2.13 NAME 'displayName' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.2.840.113556.1.4.1 NAME 'name' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE NO-USER-MODIFICATION )",
"( 1.2.840.113556.1.2.131 NAME 'co' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.2.840.113556.1.2.141 NAME 'department' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.2.840.113556.1.2.146 NAME 'company' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.2.840.113556.1.4.44 NAME 'homeDirectory' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.2.840.113556.1.4.221 NAME 'sAMAccountName' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.2.840.113556.1.4.261 NAME 'division' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.3.6.1.1.1.1.0 NAME 'uidNumber' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )",
"( 1.3.6.1.1.1.1.1 NAME 'gidNumber' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )",
"( 2.5.4.0 NAME 'objectClass' SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' NO-USER-MODIFICATION )",
"( 2.5.4.4 NAME 'sn' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 2.5.4.3 NAME 'cn' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 2.5.4.6 NAME 'c' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 2.5.4.7 NAME 'l' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 2.5.4.10 NAME 'o' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )",
"( 2.5.4.11 NAME 'ou' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )",
"( 2.5.4.20 NAME 'telephoneNumber' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 2.5.4.42 NAME 'givenName' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 2.5.4.0 NAME 'objectClass' SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' NO-USER-MODIFICATION )",
"( 2.5.4.3 NAME 'cn' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 2.5.4.4 NAME 'sn' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 2.5.4.12 NAME 'title' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 2.5.4.13 NAME 'description' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )",
"( 2.5.4.20 NAME 'telephoneNumber' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 2.5.4.31 NAME 'member' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )",
"( 2.5.4.42 NAME 'givenName' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 2.5.21.2 NAME 'dITContentRules' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' NO-USER-MODIFICATION )",
"( 2.5.21.5 NAME 'attributeTypes' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' NO-USER-MODIFICATION )",
"( 2.5.21.6 NAME 'objectClasses' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' NO-USER-MODIFICATION )",
"( 0.9.2342.19200300.100.1.1 NAME 'uid' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 0.9.2342.19200300.100.1.3 NAME 'mail' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 0.9.2342.19200300.100.1.41 NAME 'mobile' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.2.840.113556.1.2.13 NAME 'displayName' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.2.840.113556.1.2.146 NAME 'company' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.2.840.113556.1.2.102 NAME 'memberOf' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' NO-USER-MODIFICATION )",
"( 1.2.840.113556.1.2.131 NAME 'co' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.2.840.113556.1.2.141 NAME 'department' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.2.840.113556.1.4.1 NAME 'name' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE NO-USER-MODIFICATION )",
"( 1.2.840.113556.1.4.44 NAME 'homeDirectory' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.2.840.113556.1.4.221 NAME 'sAMAccountName' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.2.840.113556.1.4.261 NAME 'division' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.2.840.113556.1.4.750 NAME 'groupType' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )",
"( 1.2.840.113556.1.4.782 NAME 'objectCategory' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' SINGLE-VALUE )",
"( 1.3.6.1.1.1.1.0 NAME 'uidNumber' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )",
"( 1.3.6.1.1.1.1.1 NAME 'gidNumber' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )",
"( 1.3.6.1.1.1.1.12 NAME 'memberUid' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )",
// Custom attributes
// Temporarily use 1.3.6.1.4.1.26027.1.1 as a base
// https://docs.oracle.com/cd/E19450-01/820-6169/working-with-object-identifiers.html#obtaining-a-base-oid
"( 1.3.6.1.4.1.26027.1.1.2 NAME ( 'goauthentik.io/ldap/superuser' 'ak-superuser' ) SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' SINGLE-VALUE )",
"( 1.3.6.1.4.1.26027.1.1.3 NAME ( 'goauthentik.io/ldap/active' 'ak-active' ) SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' SINGLE-VALUE )",
"( 1.3.6.1.4.1.26027.1.1.4 NAME ( 'goauthentik.io/ldap/sources' 'goauthentikio-user-sources' ) SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
},
},
{
Name: "objectClasses",
Values: []string{
"( 2.5.6.0 NAME 'top' ABSTRACT MUST ( objectClass ) MAY ( objectCategory $ cn $ description $ displayName $ memberOf $ name ) )",
"( 2.5.6.6 NAME 'person' SUP top STRUCTURAL MUST ( cn ) MAY ( sn $ telephoneNumber ) )",
"( 2.5.6.7 NAME 'organizationalPerson' SUP person STRUCTURAL MAY ( c $ l $ o $ ou $ title $ givenName $ co $ department $ company $ division $ mail $ mobile $ telephoneNumber ) )",
"( 2.5.6.9 NAME 'groupOfNames' SUP top STRUCTURAL MUST ( cn $ member ) MAY ( o $ ou ) )",
"( 1.2.840.113556.1.5.9 NAME 'user' SUP organizationalPerson STRUCTURAL MAY ( name $ displayName $ uid $ mail ) )",
"( 1.3.6.1.1.1.2.0 NAME 'posixAccount' SUP top AUXILIARY MAY ( cn $ description $ homeDirectory $ uid $ uidNumber $ gidNumber ) )",
"( 2.16.840.1.113730.3.2.2 NAME 'inetOrgPerson' SUP user STRUCTURAL MAY ( uidNumber $ gidNumber $ displayName $ homeDirectory ) )",
"( 2.5.6.5 NAME 'organizationalUnit' SUP top STRUCTURAL MUST ( ou ) MAY ( c $ l ) )",
"( 1.2.840.113556.1.5.8 NAME 'group' SUP top AUXILIARY MAY ( cn $ groupType $ member ) )",
"( 1.3.6.1.1.1.2.2 NAME 'posixGroup' SUP top AUXILIARY MAY ( cn $ description $ gidNumber $ memberUid ) )",
"( 2.5.20.1 NAME 'subSchema' SUP top STRUCTURAL MAY ( dITContentRules $ attributeTypes $ objectClasses ) )",
// Custom attributes
// Temporarily use 1.3.6.1.4.1.26027.1.1 as a base
// https://docs.oracle.com/cd/E19450-01/820-6169/working-with-object-identifiers.html#obtaining-a-base-oid
"( 1.3.6.1.4.1.26027.1.1.1 NAME 'goauthentik.io/ldap/user' SUP organizationalPerson STRUCTURAL MAY ( ak-superuser $ ak-active $ sAMAccountName $ goauthentikio-user-sources $ goauthentik.io/user/sources $ goauthentik.io/ldap/active $ goauthentik.io/ldap/superuser ) )",
},
},
},

View file

@ -280,7 +280,9 @@ func (a *Application) handleSignOut(rw http.ResponseWriter, r *http.Request) {
"id_token_hint": []string{cc.RawToken},
}
redirect += "?" + uv.Encode()
err = a.Logout(r.Context(), cc.Sub)
err = a.Logout(r.Context(), func(c Claims) bool {
return c.Sub == cc.Sub
})
if err != nil {
a.log.WithError(err).Warning("failed to logout of other sessions")
}

View file

@ -11,10 +11,11 @@ type Claims struct {
Exp int `json:"exp"`
Email string `json:"email"`
Verified bool `json:"email_verified"`
Proxy *ProxyClaims `json:"ak_proxy"`
Name string `json:"name"`
PreferredUsername string `json:"preferred_username"`
Groups []string `json:"groups"`
Sid string `json:"sid"`
Proxy *ProxyClaims `json:"ak_proxy"`
RawToken string
}

View file

@ -88,7 +88,7 @@ func (a *Application) getAllCodecs() []securecookie.Codec {
return cs
}
func (a *Application) Logout(ctx context.Context, sub string) error {
func (a *Application) Logout(ctx context.Context, filter func(c Claims) bool) error {
if _, ok := a.sessions.(*sessions.FilesystemStore); ok {
files, err := os.ReadDir(os.TempDir())
if err != nil {
@ -118,7 +118,7 @@ func (a *Application) Logout(ctx context.Context, sub string) error {
continue
}
claims := s.Values[constants.SessionClaims].(Claims)
if claims.Sub == sub {
if filter(claims) {
a.log.WithField("path", fullPath).Trace("deleting session")
err := os.Remove(fullPath)
if err != nil {
@ -153,7 +153,7 @@ func (a *Application) Logout(ctx context.Context, sub string) error {
continue
}
claims := c.(Claims)
if claims.Sub == sub {
if filter(claims) {
a.log.WithField("key", key).Trace("deleting session")
_, err := client.Del(ctx, key).Result()
if err != nil {

View file

@ -65,6 +65,7 @@ func NewProxyServer(ac *ak.APIController) *ProxyServer {
globalMux.PathPrefix("/outpost.goauthentik.io/static").HandlerFunc(s.HandleStatic)
globalMux.Path("/outpost.goauthentik.io/ping").HandlerFunc(sentryutils.SentryNoSample(s.HandlePing))
rootMux.PathPrefix("/").HandlerFunc(s.Handle)
ac.AddWSHandler(s.handleWSMessage)
return s
}

View file

@ -0,0 +1,49 @@
package proxyv2
import (
"context"
"github.com/mitchellh/mapstructure"
"goauthentik.io/internal/outpost/proxyv2/application"
)
type WSProviderSubType string
const (
WSProviderSubTypeLogout WSProviderSubType = "logout"
)
type WSProviderMsg struct {
SubType WSProviderSubType `mapstructure:"sub_type"`
SessionID string `mapstructure:"session_id"`
}
func ParseWSProvider(args map[string]interface{}) (*WSProviderMsg, error) {
msg := &WSProviderMsg{}
err := mapstructure.Decode(args, &msg)
if err != nil {
return nil, err
}
return msg, nil
}
func (ps *ProxyServer) handleWSMessage(ctx context.Context, args map[string]interface{}) {
msg, err := ParseWSProvider(args)
if err != nil {
ps.log.WithError(err).Warning("invalid provider-specific ws message")
return
}
switch msg.SubType {
case WSProviderSubTypeLogout:
for _, p := range ps.apps {
err := p.Logout(ctx, func(c application.Claims) bool {
return c.Sid == msg.SessionID
})
if err != nil {
ps.log.WithField("provider", p.Host).WithError(err).Warning("failed to logout")
}
}
default:
ps.log.WithField("sub_type", msg.SubType).Warning("invalid sub_type")
}
}

View file

@ -1,5 +1,5 @@
# Stage 1: Build
FROM docker.io/golang:1.21.1-bookworm AS builder
FROM docker.io/golang:1.21.2-bookworm AS builder
WORKDIR /go/src/goauthentik.io

View file

@ -1,12 +1,12 @@
#!/usr/bin/env python
"""System Migration handler"""
import os
from importlib.util import module_from_spec, spec_from_file_location
from inspect import getmembers, isclass
from os import environ, system
from pathlib import Path
from typing import Any
from psycopg import connect
from psycopg import Connection, Cursor, connect
from structlog.stdlib import get_logger
from authentik.lib.config import CONFIG
@ -16,16 +16,33 @@ ADV_LOCK_UID = 1000
LOCKED = False
class CommandError(Exception):
"""Error raised when a system_crit command fails"""
class BaseMigration:
"""Base System Migration"""
cur: Any
con: Any
cur: Cursor
con: Connection
def __init__(self, cur: Any, con: Any):
self.cur = cur
self.con = con
def system_crit(self, command: str):
"""Run system command"""
LOGGER.debug("Running system_crit command", command=command)
retval = system(command) # nosec
if retval != 0:
raise CommandError("Migration error")
def fake_migration(self, *app_migration: tuple[str, str]):
"""Fake apply a list of migrations, arguments are
expected to be tuples of (app_label, migration_name)"""
for app, _migration in app_migration:
self.system_crit(f"./manage.py migrate {app} {_migration} --fake")
def needs_migration(self) -> bool:
"""Return true if Migration needs to be run"""
return False
@ -82,7 +99,7 @@ if __name__ == "__main__":
LOGGER.info("Migration finished applying", migration=sub)
release_lock()
LOGGER.info("applying django migrations")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "authentik.root.settings")
environ.setdefault("DJANGO_SETTINGS_MODULE", "authentik.root.settings")
wait_for_lock()
try:
from django.core.management import execute_from_command_line

View file

@ -20,18 +20,17 @@ class Migration(BaseMigration):
def upgrade(self, migrate=False):
self.cur.execute(SQL_STATEMENT)
self.con.commit()
if migrate:
# If we already have migrations in the database, assume we're upgrading an existing install
# and set the install id to the secret key
self.cur.execute(
"INSERT INTO authentik_install_id (id) VALUES (%s)", (CONFIG.get("secret_key"),)
)
else:
# Otherwise assume a new install, generate an install ID based on a UUID
install_id = str(uuid4())
self.cur.execute("INSERT INTO authentik_install_id (id) VALUES (%s)", (install_id,))
self.con.commit()
with self.con.transaction():
if migrate:
# If we already have migrations in the database, assume we're upgrading an existing install
# and set the install id to the secret key
self.cur.execute(
"INSERT INTO authentik_install_id (id) VALUES (%s)", (CONFIG.get("secret_key"),)
)
else:
# Otherwise assume a new install, generate an install ID based on a UUID
install_id = str(uuid4())
self.cur.execute("INSERT INTO authentik_install_id (id) VALUES (%s)", (install_id,))
def run(self):
self.cur.execute(

View file

@ -1,10 +1,7 @@
# flake8: noqa
from os import system
from lifecycle.migrate import BaseMigration
SQL_STATEMENT = """
BEGIN TRANSACTION;
DELETE FROM django_migrations WHERE app = 'otp_static';
DELETE FROM django_migrations WHERE app = 'otp_totp';
-- Rename tables (static)
@ -15,7 +12,7 @@ ALTER SEQUENCE otp_static_staticdevice_id_seq RENAME TO authentik_stages_authent
-- Rename tables (totp)
ALTER TABLE otp_totp_totpdevice RENAME TO authentik_stages_authenticator_totp_totpdevice;
ALTER SEQUENCE otp_totp_totpdevice_id_seq RENAME TO authentik_stages_authenticator_totp_totpdevice_id_seq;
COMMIT;"""
"""
class Migration(BaseMigration):
@ -25,23 +22,24 @@ class Migration(BaseMigration):
)
return bool(self.cur.rowcount)
def system_crit(self, command):
retval = system(command) # nosec
if retval != 0:
raise Exception("Migration error")
def run(self):
self.cur.execute(SQL_STATEMENT)
self.con.commit()
self.system_crit(
"./manage.py migrate authentik_stages_authenticator_static 0008_initial --fake"
)
self.system_crit(
"./manage.py migrate authentik_stages_authenticator_static 0009_throttling --fake"
)
self.system_crit(
"./manage.py migrate authentik_stages_authenticator_totp 0008_initial --fake"
)
self.system_crit(
"./manage.py migrate authentik_stages_authenticator_totp 0009_auto_20190420_0723 --fake"
)
with self.con.transaction():
self.cur.execute(SQL_STATEMENT)
self.fake_migration(
(
"authentik_stages_authenticator_static",
"0008_initial",
),
(
"authentik_stages_authenticator_static",
"0009_throttling",
),
(
"authentik_stages_authenticator_totp",
"0008_initial",
),
(
"authentik_stages_authenticator_totp",
"0009_auto_20190420_0723",
),
)

View file

@ -1,10 +1,7 @@
# flake8: noqa
from os import system
from lifecycle.migrate import BaseMigration
SQL_STATEMENT = """
BEGIN TRANSACTION;
DELETE FROM django_migrations WHERE app = 'passbook_stages_prompt';
DROP TABLE passbook_stages_prompt_prompt cascade;
DROP TABLE passbook_stages_prompt_promptstage cascade;
@ -25,7 +22,7 @@ DELETE FROM django_migrations WHERE app = 'passbook_flows' AND name = '0008_defa
DELETE FROM django_migrations WHERE app = 'passbook_flows' AND name = '0009_source_flows';
DELETE FROM django_migrations WHERE app = 'passbook_flows' AND name = '0010_provider_flows';
DELETE FROM django_migrations WHERE app = 'passbook_stages_password' AND name = '0002_passwordstage_change_flow';
COMMIT;"""
"""
class Migration(BaseMigration):
@ -35,17 +32,14 @@ class Migration(BaseMigration):
)
return bool(self.cur.rowcount)
def system_crit(self, command):
retval = system(command) # nosec
if retval != 0:
raise Exception("Migration error")
def run(self):
self.cur.execute(SQL_STATEMENT)
self.con.commit()
self.system_crit("./manage.py migrate passbook_stages_prompt")
self.system_crit("./manage.py migrate passbook_flows 0008_default_flows --fake")
self.system_crit("./manage.py migrate passbook_flows 0009_source_flows --fake")
self.system_crit("./manage.py migrate passbook_flows 0010_provider_flows --fake")
self.system_crit("./manage.py migrate passbook_flows")
self.system_crit("./manage.py migrate passbook_stages_password --fake")
with self.con.transaction():
self.cur.execute(SQL_STATEMENT)
self.system_crit("./manage.py migrate passbook_stages_prompt")
self.fake_migration(
("passbook_flows", "0008_default_flows"),
("passbook_flows", "0009_source_flows"),
("passbook_flows", "0010_provider_flows"),
)
self.system_crit("./manage.py migrate passbook_flows")
self.fake_migration(("passbook_stages_password", ""))

View file

@ -4,7 +4,7 @@ from redis import Redis
from authentik.lib.config import CONFIG
from lifecycle.migrate import BaseMigration
SQL_STATEMENT = """BEGIN TRANSACTION;
SQL_STATEMENT = """
ALTER TABLE passbook_audit_event RENAME TO authentik_audit_event;
ALTER TABLE passbook_core_application RENAME TO authentik_core_application;
ALTER TABLE passbook_core_group RENAME TO authentik_core_group;
@ -92,8 +92,7 @@ ALTER SEQUENCE passbook_stages_prompt_promptstage_validation_policies_id_seq REN
UPDATE django_migrations SET app = replace(app, 'passbook', 'authentik');
UPDATE django_content_type SET app_label = replace(app_label, 'passbook', 'authentik');
END TRANSACTION;"""
"""
class Migration(BaseMigration):
@ -104,18 +103,18 @@ class Migration(BaseMigration):
return bool(self.cur.rowcount)
def run(self):
self.cur.execute(SQL_STATEMENT)
self.con.commit()
# We also need to clean the cache to make sure no pickeled objects still exist
for db in [
CONFIG.get("redis.message_queue_db"),
CONFIG.get("redis.cache_db"),
CONFIG.get("redis.ws_db"),
]:
redis = Redis(
host=CONFIG.get("redis.host"),
port=6379,
db=db,
password=CONFIG.get("redis.password"),
)
redis.flushall()
with self.con.transaction():
self.cur.execute(SQL_STATEMENT)
# We also need to clean the cache to make sure no pickeled objects still exist
for db in [
CONFIG.get("redis.message_queue_db"),
CONFIG.get("redis.cache_db"),
CONFIG.get("redis.ws_db"),
]:
redis = Redis(
host=CONFIG.get("redis.host"),
port=6379,
db=db,
password=CONFIG.get("redis.password"),
)
redis.flushall()

View file

@ -1,12 +1,9 @@
# flake8: noqa
from lifecycle.migrate import BaseMigration
SQL_STATEMENT = """BEGIN TRANSACTION;
ALTER TABLE authentik_audit_event RENAME TO authentik_events_event;
SQL_STATEMENT = """ALTER TABLE authentik_audit_event RENAME TO authentik_events_event;
UPDATE django_migrations SET app = replace(app, 'authentik_audit', 'authentik_events');
UPDATE django_content_type SET app_label = replace(app_label, 'authentik_audit', 'authentik_events');
END TRANSACTION;"""
UPDATE django_content_type SET app_label = replace(app_label, 'authentik_audit', 'authentik_events');"""
class Migration(BaseMigration):
@ -17,5 +14,5 @@ class Migration(BaseMigration):
return bool(self.cur.rowcount)
def run(self):
self.cur.execute(SQL_STATEMENT)
self.con.commit()
with self.con.transaction():
self.cur.execute(SQL_STATEMENT)

View file

@ -1,7 +1,7 @@
# flake8: noqa
from lifecycle.migrate import BaseMigration
SQL_STATEMENT = """BEGIN TRANSACTION;
SQL_STATEMENT = """
ALTER TABLE authentik_stages_otp_static_otpstaticstage RENAME TO authentik_stages_authenticator_static_otpstaticstage;
UPDATE django_migrations SET app = replace(app, 'authentik_stages_otp_static', 'authentik_stages_authenticator_static');
UPDATE django_content_type SET app_label = replace(app_label, 'authentik_stages_otp_static', 'authentik_stages_authenticator_static');
@ -13,8 +13,7 @@ UPDATE django_content_type SET app_label = replace(app_label, 'authentik_stages_
ALTER TABLE authentik_stages_otp_validate_otpvalidatestage RENAME TO authentik_stages_authenticator_validate_otpvalidatestage;
UPDATE django_migrations SET app = replace(app, 'authentik_stages_otp_validate', 'authentik_stages_authenticator_validate');
UPDATE django_content_type SET app_label = replace(app_label, 'authentik_stages_otp_validate', 'authentik_stages_authenticator_validate');
END TRANSACTION;"""
"""
class Migration(BaseMigration):
@ -26,5 +25,5 @@ class Migration(BaseMigration):
return bool(self.cur.rowcount)
def run(self):
self.cur.execute(SQL_STATEMENT)
self.con.commit()
with self.con.transaction():
self.cur.execute(SQL_STATEMENT)

View file

@ -1,10 +1,8 @@
# flake8: noqa
from lifecycle.migrate import BaseMigration
SQL_STATEMENT = """BEGIN TRANSACTION;
DROP TABLE "authentik_policies_hibp_haveibeenpwendpolicy";
DELETE FROM django_migrations WHERE app = 'authentik_policies_hibp';
END TRANSACTION;"""
SQL_STATEMENT = """DROP TABLE "authentik_policies_hibp_haveibeenpwendpolicy";
DELETE FROM django_migrations WHERE app = 'authentik_policies_hibp';"""
class Migration(BaseMigration):
@ -16,5 +14,5 @@ class Migration(BaseMigration):
return bool(self.cur.rowcount)
def run(self):
self.cur.execute(SQL_STATEMENT)
self.con.commit()
with self.con.transaction():
self.cur.execute(SQL_STATEMENT)

7
poetry.lock generated
View file

@ -3723,20 +3723,19 @@ wsproto = ">=0.14"
[[package]]
name = "twilio"
version = "8.9.0"
version = "8.9.1"
description = "Twilio API client and TwiML generator"
optional = false
python-versions = ">=3.7.0"
files = [
{file = "twilio-8.9.0-py2.py3-none-any.whl", hash = "sha256:d2f71060575432f73ed73f7cfc33259c53c96b92aa969d2910dacc8da1d1a5d9"},
{file = "twilio-8.9.0.tar.gz", hash = "sha256:e711b5ea89694cb58a55a6b69f6190ea4c8499a1d6d68eb6a03c9840bb78fbb3"},
{file = "twilio-8.9.1-py2.py3-none-any.whl", hash = "sha256:3edc0bcde7320b5ae5f516484af9092bc4df2f5a3b1d4d94a66c29310adb924c"},
{file = "twilio-8.9.1.tar.gz", hash = "sha256:7bca5dc476d4d15e89e41d3074f8a265bd61445d1de9e8a697ca96cd8399eda6"},
]
[package.dependencies]
aiohttp = ">=3.8.4"
aiohttp-retry = ">=2.8.3"
PyJWT = ">=2.0.0,<3.0.0"
pytz = "*"
requests = ">=2.0.0"
[[package]]

View file

@ -15,7 +15,7 @@ COPY web .
RUN npm run build-proxy
# Stage 2: Build
FROM docker.io/golang:1.21.1-bookworm AS builder
FROM docker.io/golang:1.21.2-bookworm AS builder
WORKDIR /go/src/goauthentik.io

View file

@ -1,5 +1,5 @@
# Stage 1: Build
FROM docker.io/golang:1.21.1-bookworm AS builder
FROM docker.io/golang:1.21.2-bookworm AS builder
WORKDIR /go/src/goauthentik.io

View file

@ -16292,6 +16292,10 @@ paths:
schema:
type: string
format: uuid
- in: query
name: default_relay_state
schema:
type: string
- in: query
name: digest_algorithm
schema:
@ -36303,6 +36307,9 @@ components:
* `redirect` - Redirect
* `post` - Post
default_relay_state:
type: string
description: Default relay_state value for IDP-initiated logins
PatchedSAMLSourceRequest:
type: object
description: SAMLSource Serializer
@ -38480,6 +38487,9 @@ components:
* `redirect` - Redirect
* `post` - Post
default_relay_state:
type: string
description: Default relay_state value for IDP-initiated logins
url_download_metadata:
type: string
description: Get metadata download URL
@ -38624,6 +38634,9 @@ components:
* `redirect` - Redirect
* `post` - Post
default_relay_state:
type: string
description: Default relay_state value for IDP-initiated logins
required:
- acs_url
- authorization_flow
@ -40142,6 +40155,10 @@ components:
type: string
type:
$ref: '#/components/schemas/UserTypeEnum'
uuid:
type: string
format: uuid
readOnly: true
required:
- avatar
- groups_obj
@ -40150,6 +40167,7 @@ components:
- pk
- uid
- username
- uuid
UserAccountRequest:
type: object
description: Account adding/removing operations

View file

@ -231,6 +231,7 @@ class TestProviderLDAP(SeleniumTestCase):
for obj in response:
del obj["raw_attributes"]
del obj["raw_dn"]
obj["attributes"] = dict(obj["attributes"])
o_user = outpost.user
expected = [
{
@ -244,11 +245,13 @@ class TestProviderLDAP(SeleniumTestCase):
"sn": o_user.name,
"mail": "",
"objectClass": [
"user",
"top",
"person",
"organizationalPerson",
"inetOrgPerson",
"goauthentik.io/ldap/user",
"user",
"posixAccount",
"goauthentik.io/ldap/user",
],
"uidNumber": 2000 + o_user.pk,
"gidNumber": 2000 + o_user.pk,
@ -270,11 +273,13 @@ class TestProviderLDAP(SeleniumTestCase):
"sn": embedded_account.name,
"mail": "",
"objectClass": [
"user",
"top",
"person",
"organizationalPerson",
"inetOrgPerson",
"goauthentik.io/ldap/user",
"user",
"posixAccount",
"goauthentik.io/ldap/user",
],
"uidNumber": 2000 + embedded_account.pk,
"gidNumber": 2000 + embedded_account.pk,
@ -296,11 +301,13 @@ class TestProviderLDAP(SeleniumTestCase):
"sn": self.user.name,
"mail": self.user.email,
"objectClass": [
"user",
"top",
"person",
"organizationalPerson",
"inetOrgPerson",
"goauthentik.io/ldap/user",
"user",
"posixAccount",
"goauthentik.io/ldap/user",
],
"uidNumber": 2000 + self.user.pk,
"gidNumber": 2000 + self.user.pk,

View file

@ -9,11 +9,11 @@
"@trivago/prettier-plugin-sort-imports": "^4.2.0",
"@typescript-eslint/eslint-plugin": "^6.7.4",
"@typescript-eslint/parser": "^6.7.4",
"@wdio/cli": "^8.16.19",
"@wdio/local-runner": "^8.16.19",
"@wdio/mocha-framework": "^8.16.17",
"@wdio/spec-reporter": "^8.16.17",
"eslint": "^8.49.0",
"@wdio/cli": "^8.16.22",
"@wdio/local-runner": "^8.16.22",
"@wdio/mocha-framework": "^8.16.22",
"@wdio/spec-reporter": "^8.16.22",
"eslint": "^8.51.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-sonarjs": "^0.21.0",
"npm-run-all": "^4.1.5",
@ -340,9 +340,9 @@
}
},
"node_modules/@eslint/js": {
"version": "8.50.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.50.0.tgz",
"integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==",
"version": "8.51.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz",
"integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@ -1067,18 +1067,18 @@
}
},
"node_modules/@wdio/cli": {
"version": "8.16.19",
"resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.16.19.tgz",
"integrity": "sha512-MGpRrb56kp0n+r/Z0KMe0o4O1dFgBhhjt/O4HC5l0WcPuf5Ew19w1Nr8PoSZ5XqXklGJi0woFh68YZA1MziInA==",
"version": "8.16.22",
"resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.16.22.tgz",
"integrity": "sha512-/cv/fQ3qZoTJEnjmxWwPC2ohPfg5GrtBxi1MXNMJK85l1RHVYLkuNwUq18gSNcLol2vJ7GGoFowadlBjoCj56Q==",
"dev": true,
"dependencies": {
"@types/node": "^20.1.1",
"@wdio/config": "8.16.17",
"@wdio/globals": "8.16.19",
"@wdio/config": "8.16.22",
"@wdio/globals": "8.16.22",
"@wdio/logger": "8.16.17",
"@wdio/protocols": "8.16.5",
"@wdio/types": "8.16.12",
"@wdio/utils": "8.16.17",
"@wdio/types": "8.16.22",
"@wdio/utils": "8.16.22",
"async-exit-hook": "^2.0.1",
"chalk": "^5.2.0",
"chokidar": "^3.5.3",
@ -1093,7 +1093,7 @@
"lodash.union": "^4.6.0",
"read-pkg-up": "10.1.0",
"recursive-readdir": "^2.2.3",
"webdriverio": "8.16.19",
"webdriverio": "8.16.22",
"yargs": "^17.7.2",
"yarn-install": "^1.0.0"
},
@ -1117,14 +1117,14 @@
}
},
"node_modules/@wdio/config": {
"version": "8.16.17",
"resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.16.17.tgz",
"integrity": "sha512-9+AY73Dp6N/CHzUYe4KbYV8wcKh3mpzBsMKieNlwXi1bQ3AAirTjOXzQ2BoQn6fg/Yd1GxmT3F0YsVS+bF1PmQ==",
"version": "8.16.22",
"resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.16.22.tgz",
"integrity": "sha512-sxqiVUEq++GFDKeR+HfzlgNhEbYuJZlrkU09p2rZzCRpPM3ty4azzdHB+XEdHHJJlV4UguvqUkp7n2126d9SAQ==",
"dev": true,
"dependencies": {
"@wdio/logger": "8.16.17",
"@wdio/types": "8.16.12",
"@wdio/utils": "8.16.17",
"@wdio/types": "8.16.22",
"@wdio/utils": "8.16.22",
"decamelize": "^6.0.0",
"deepmerge-ts": "^5.0.0",
"glob": "^10.2.2",
@ -1136,29 +1136,29 @@
}
},
"node_modules/@wdio/globals": {
"version": "8.16.19",
"resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.16.19.tgz",
"integrity": "sha512-KziZCYLcEvcsESJm2STkCEUKq2rhIbAP+1lyksULUdMsLoKhqW3yPrb8g6z3qj8G7yAYF8xWpKID0yQejl3UXA==",
"version": "8.16.22",
"resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.16.22.tgz",
"integrity": "sha512-YGmGboSDTnFk+Bp/FJX2oPf548YILOr6M2T+wLnZtfgEPV5X8LbhT+XMqOYOiIdnI5MfgWGn8+XIgdjtNumHwQ==",
"dev": true,
"engines": {
"node": "^16.13 || >=18"
},
"optionalDependencies": {
"expect-webdriverio": "^4.2.5",
"webdriverio": "8.16.19"
"webdriverio": "8.16.22"
}
},
"node_modules/@wdio/local-runner": {
"version": "8.16.19",
"resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.16.19.tgz",
"integrity": "sha512-YUGF+7JCWoziFRW9/L+JSxuGKLgRiRXkRJ39iKaW97qS3MckBxLtuB4IY7gt3WJ80iDYo6IWLllRZfWtpvAT/A==",
"version": "8.16.22",
"resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.16.22.tgz",
"integrity": "sha512-ZhP8lDgjYzBuopIROALMcAmjvo7KGYjk6W+eJAR2p1EgdQy8IFMIuYuK1lWhAGCN3GRMRC1CTVtEVxc79/EJMg==",
"dev": true,
"dependencies": {
"@types/node": "^20.1.0",
"@wdio/logger": "8.16.17",
"@wdio/repl": "8.10.1",
"@wdio/runner": "8.16.19",
"@wdio/types": "8.16.12",
"@wdio/runner": "8.16.22",
"@wdio/types": "8.16.22",
"async-exit-hook": "^2.0.1",
"split2": "^4.1.0",
"stream-buffers": "^3.0.2"
@ -1195,16 +1195,16 @@
}
},
"node_modules/@wdio/mocha-framework": {
"version": "8.16.17",
"resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.16.17.tgz",
"integrity": "sha512-aJ3CMzSBPOCb1i7hPyAsGYwccxPkD96qqdme/YUGL4U4SB+kEgDgNvouTJbyqvAB4VEuCcs+KqNWIMtM+rPi0Q==",
"version": "8.16.22",
"resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.16.22.tgz",
"integrity": "sha512-/TgHCr4QoNRChGAXfBNZZHGEs+hMO2A8aU6mNEyrokAcHBoCL3NhnAP2SiQV0uT5wzoAOfv4RXqXOJ3bJa30Rw==",
"dev": true,
"dependencies": {
"@types/mocha": "^10.0.0",
"@types/node": "^20.1.0",
"@wdio/logger": "8.16.17",
"@wdio/types": "8.16.12",
"@wdio/utils": "8.16.17",
"@wdio/types": "8.16.22",
"@wdio/utils": "8.16.22",
"mocha": "^10.0.0"
},
"engines": {
@ -1230,14 +1230,14 @@
}
},
"node_modules/@wdio/reporter": {
"version": "8.16.17",
"resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-8.16.17.tgz",
"integrity": "sha512-c7B4dnOhCM9qCn/0vlV0IjCTL/Dv++MNOMtZFTQlEEo5qXSX+LNkpsZi0STnkPqnv6ZP7liwz4bA01MFksGaww==",
"version": "8.16.22",
"resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-8.16.22.tgz",
"integrity": "sha512-5f4H2bAaq+mxl51j+4pyDuhgvE5MIJOhF3G75AGCjEnXgDEHYJ+yzpvRwmLxPB98BkxZ/ldxrQth/I/l3+j1fQ==",
"dev": true,
"dependencies": {
"@types/node": "^20.1.0",
"@wdio/logger": "8.16.17",
"@wdio/types": "8.16.12",
"@wdio/types": "8.16.22",
"diff": "^5.0.0",
"object-inspect": "^1.12.0"
},
@ -1246,35 +1246,35 @@
}
},
"node_modules/@wdio/runner": {
"version": "8.16.19",
"resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.16.19.tgz",
"integrity": "sha512-CzLxlxcRfIVzTKGeo+TO5rUmmWHjUUdRaM6/6UGNFhFCcYW5rLvfHd1ojpU7ZKxBIc/bz2RGw2/cQ8KgQbFR3g==",
"version": "8.16.22",
"resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.16.22.tgz",
"integrity": "sha512-SgWW1GPlZ7kS/7VZuuCYmCKIF6/WlOccGS2kHAg2rM45MjUId3KegNQ+INi2S3CkgU19M0rH2nNEUozp68dddw==",
"dev": true,
"dependencies": {
"@types/node": "^20.1.0",
"@wdio/config": "8.16.17",
"@wdio/globals": "8.16.19",
"@wdio/config": "8.16.22",
"@wdio/globals": "8.16.22",
"@wdio/logger": "8.16.17",
"@wdio/types": "8.16.12",
"@wdio/utils": "8.16.17",
"@wdio/types": "8.16.22",
"@wdio/utils": "8.16.22",
"deepmerge-ts": "^5.0.0",
"expect-webdriverio": "^4.2.5",
"gaze": "^1.1.2",
"webdriver": "8.16.17",
"webdriverio": "8.16.19"
"webdriver": "8.16.22",
"webdriverio": "8.16.22"
},
"engines": {
"node": "^16.13 || >=18"
}
},
"node_modules/@wdio/spec-reporter": {
"version": "8.16.17",
"resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-8.16.17.tgz",
"integrity": "sha512-CBpZhTJASDWpxJBUK5TLBZKBWbZxsVctpqXjpjG9fl9+IXBG00P5oFecDa90aUa00Dq+eIE1UUsVJa7evd36Tg==",
"version": "8.16.22",
"resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-8.16.22.tgz",
"integrity": "sha512-bOELqVNDGRf4hxAtnYKjQiejMSKr/KNKgIwvyk6Ww2WHavzbZ/3oPRKNiyu5qgQR1lU+IN46V848EQV/RwtG+Q==",
"dev": true,
"dependencies": {
"@wdio/reporter": "8.16.17",
"@wdio/types": "8.16.12",
"@wdio/reporter": "8.16.22",
"@wdio/types": "8.16.22",
"chalk": "^5.1.2",
"easy-table": "^1.2.0",
"pretty-ms": "^7.0.0"
@ -1296,9 +1296,9 @@
}
},
"node_modules/@wdio/types": {
"version": "8.16.12",
"resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.16.12.tgz",
"integrity": "sha512-TjCZJ3P9ual21G0dRv0lC9QgHGd3Igv+guEINevBKf/oD4/N84PvQ2eZG1nSbZ3xh8X/dvi+O64A6VEv43gx2w==",
"version": "8.16.22",
"resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.16.22.tgz",
"integrity": "sha512-bg30seCgYu5JXukJ7M0qWKZLNATpKROvnl5/lRSOu4oopjm28UUan/+gHfHfyJ3MJ2uFNhaIVotPAvUziVJAdg==",
"dev": true,
"dependencies": {
"@types/node": "^20.1.0"
@ -1308,14 +1308,14 @@
}
},
"node_modules/@wdio/utils": {
"version": "8.16.17",
"resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.16.17.tgz",
"integrity": "sha512-jDyOrxbQRDJO0OPt9UBgnwpUIKqtRn4+R0gR5VSDrIG/in5ZZg28yer8urrIVY4yY9ut5r/22VaMHZI9LEXF5w==",
"version": "8.16.22",
"resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.16.22.tgz",
"integrity": "sha512-1hQjm7Jweiz+ABakS33TyWXYoxEg7LxL12RqbEYqtGB6ZTJhik+Cwyj/jcJbETSjiYJflmHxDvhFwuOkLR8ljg==",
"dev": true,
"dependencies": {
"@puppeteer/browsers": "^1.6.0",
"@wdio/logger": "8.16.17",
"@wdio/types": "8.16.12",
"@wdio/types": "8.16.22",
"decamelize": "^6.0.0",
"deepmerge-ts": "^5.1.0",
"edgedriver": "^5.3.5",
@ -2595,11 +2595,10 @@
}
},
"node_modules/devtools-protocol": {
"version": "0.0.1188743",
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1188743.tgz",
"integrity": "sha512-FZDQC58vLiGR2mjSgsMzU8aEJieovMonIyxf38b775eYdIfAYgSzyAWnDf0Eq6ouF/L9qcbqR8jcQeIC34jp/w==",
"dev": true,
"peer": true
"version": "0.0.1203626",
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1203626.tgz",
"integrity": "sha512-nEzHZteIUZfGCZtTiS1fRpC8UZmsfD1SiyPvaUNvS13dvKf666OAm8YTi0+Ca3n1nLEyu49Cy4+dPWpaHFJk9g==",
"dev": true
},
"node_modules/diff": {
"version": "5.1.0",
@ -2946,15 +2945,15 @@
}
},
"node_modules/eslint": {
"version": "8.50.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.50.0.tgz",
"integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==",
"version": "8.51.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz",
"integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
"@eslint/eslintrc": "^2.1.2",
"@eslint/js": "8.50.0",
"@eslint/js": "8.51.0",
"@humanwhocodes/config-array": "^0.11.11",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
@ -8694,18 +8693,18 @@
}
},
"node_modules/webdriver": {
"version": "8.16.17",
"resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.16.17.tgz",
"integrity": "sha512-pG5aEqK6odI9Tr9pr0+1mN6iGqUu5uc5HTVbqbEM6CSX2g035JRVQ/tavFTegCF1HI6yIquHiwAqsfPgLciAnQ==",
"version": "8.16.22",
"resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.16.22.tgz",
"integrity": "sha512-7W3LwQ5Np/qQG/EHD02aujv4QBuiE3/PbDd576s4QRDVa5RHLTvuAg3sZpj5kJ4wZEK2MbPsj1Nb8xqSyCUUqw==",
"dev": true,
"dependencies": {
"@types/node": "^20.1.0",
"@types/ws": "^8.5.3",
"@wdio/config": "8.16.17",
"@wdio/config": "8.16.22",
"@wdio/logger": "8.16.17",
"@wdio/protocols": "8.16.5",
"@wdio/types": "8.16.12",
"@wdio/utils": "8.16.17",
"@wdio/types": "8.16.22",
"@wdio/utils": "8.16.22",
"deepmerge-ts": "^5.1.0",
"got": "^ 12.6.1",
"ky": "^0.33.0",
@ -8753,18 +8752,18 @@
}
},
"node_modules/webdriverio": {
"version": "8.16.19",
"resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.16.19.tgz",
"integrity": "sha512-b63vRWWuLq7OKYTLMdCn+uvTW48sMFepEyrv8MKFJproaSOCcokw7sqJ/EcQFmqIgrZxKL/mDch+QKjxlW0ORw==",
"version": "8.16.22",
"resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.16.22.tgz",
"integrity": "sha512-fzZtONvimqYc+C7DnnntkOz883+VP50uIvofLkDdH5yXU6duyclclChwWNZWllEHZvkLfpmLgrpgbS7R1wNGQg==",
"dev": true,
"dependencies": {
"@types/node": "^20.1.0",
"@wdio/config": "8.16.17",
"@wdio/config": "8.16.22",
"@wdio/logger": "8.16.17",
"@wdio/protocols": "8.16.5",
"@wdio/repl": "8.10.1",
"@wdio/types": "8.16.12",
"@wdio/utils": "8.16.17",
"@wdio/types": "8.16.22",
"@wdio/utils": "8.16.22",
"archiver": "^6.0.0",
"aria-query": "^5.0.0",
"css-shorthand-properties": "^1.1.1",
@ -8781,7 +8780,7 @@
"resq": "^1.9.1",
"rgb2hex": "0.2.5",
"serialize-error": "^11.0.1",
"webdriver": "8.16.17"
"webdriver": "8.16.22"
},
"engines": {
"node": "^16.13 || >=18"
@ -8804,12 +8803,6 @@
"balanced-match": "^1.0.0"
}
},
"node_modules/webdriverio/node_modules/devtools-protocol": {
"version": "0.0.1203626",
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1203626.tgz",
"integrity": "sha512-nEzHZteIUZfGCZtTiS1fRpC8UZmsfD1SiyPvaUNvS13dvKf666OAm8YTi0+Ca3n1nLEyu49Cy4+dPWpaHFJk9g==",
"dev": true
},
"node_modules/webdriverio/node_modules/minimatch": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",

View file

@ -6,11 +6,11 @@
"@trivago/prettier-plugin-sort-imports": "^4.2.0",
"@typescript-eslint/eslint-plugin": "^6.7.4",
"@typescript-eslint/parser": "^6.7.4",
"@wdio/cli": "^8.16.19",
"@wdio/local-runner": "^8.16.19",
"@wdio/mocha-framework": "^8.16.17",
"@wdio/spec-reporter": "^8.16.17",
"eslint": "^8.49.0",
"@wdio/cli": "^8.16.22",
"@wdio/local-runner": "^8.16.22",
"@wdio/mocha-framework": "^8.16.22",
"@wdio/spec-reporter": "^8.16.22",
"eslint": "^8.51.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-sonarjs": "^0.21.0",
"npm-run-all": "^4.1.5",

311
web/package-lock.json generated
View file

@ -17,7 +17,7 @@
"@codemirror/theme-one-dark": "^6.1.2",
"@formatjs/intl-listformat": "^7.4.2",
"@fortawesome/fontawesome-free": "^6.4.2",
"@goauthentik/api": "^2023.8.3-1696335052",
"@goauthentik/api": "^2023.8.3-1696847703",
"@lit-labs/context": "^0.4.1",
"@lit-labs/task": "^3.0.2",
"@lit/localize": "^0.11.4",
@ -55,12 +55,12 @@
"@jackfranklin/rollup-plugin-markdown": "^0.4.0",
"@jeysal/storybook-addon-css-user-preferences": "^0.2.0",
"@lit/localize-tools": "^0.6.10",
"@rollup/plugin-babel": "^6.0.3",
"@rollup/plugin-commonjs": "^25.0.4",
"@rollup/plugin-node-resolve": "^15.2.1",
"@rollup/plugin-replace": "^5.0.2",
"@rollup/plugin-terser": "^0.4.3",
"@rollup/plugin-typescript": "^11.1.4",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-commonjs": "^25.0.5",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-replace": "^5.0.3",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^11.1.5",
"@storybook/addon-essentials": "^7.4.6",
"@storybook/addon-links": "^7.4.6",
"@storybook/blocks": "^7.1.1",
@ -75,19 +75,19 @@
"babel-plugin-macros": "^3.1.0",
"babel-plugin-tsconfig-paths": "^1.0.3",
"cross-env": "^7.0.3",
"eslint": "^8.50.0",
"eslint": "^8.51.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-custom-elements": "0.0.8",
"eslint-plugin-lit": "^1.9.1",
"eslint-plugin-sonarjs": "^0.21.0",
"eslint-plugin-storybook": "^0.6.14",
"eslint-plugin-storybook": "^0.6.15",
"lit-analyzer": "^1.2.1",
"npm-run-all": "^4.1.5",
"prettier": "^3.0.3",
"pyright": "^1.1.330",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"rollup": "^3.29.4",
"rollup": "^4.0.2",
"rollup-plugin-copy": "^3.5.0",
"rollup-plugin-cssimport": "^1.0.3",
"rollup-plugin-postcss-lit": "^2.1.0",
@ -2796,9 +2796,9 @@
}
},
"node_modules/@eslint/js": {
"version": "8.50.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.50.0.tgz",
"integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==",
"version": "8.51.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz",
"integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@ -2882,9 +2882,9 @@
}
},
"node_modules/@goauthentik/api": {
"version": "2023.8.3-1696335052",
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.8.3-1696335052.tgz",
"integrity": "sha512-MFgMdkk8NVvJfgU9RfZlP8ypUjH5xhBtnannnFWDJLuvsYxyyaD6Rbj2cLE2KSaIGHEpbsmzeW3eUy7ZRjpKOw=="
"version": "2023.8.3-1696847703",
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.8.3-1696847703.tgz",
"integrity": "sha512-RsOANX4L6RHaGXvMhJNq9g+E0ZLW3cwgl/t5CyQxLYvWgmVvZU4t78hxlOF7vFREoO5nhZUZnOOlD2+n5gOqLg=="
},
"node_modules/@hcaptcha/types": {
"version": "1.0.3",
@ -4345,9 +4345,9 @@
}
},
"node_modules/@rollup/plugin-babel": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-6.0.3.tgz",
"integrity": "sha512-fKImZKppa1A/gX73eg4JGo+8kQr/q1HBQaCGKECZ0v4YBBv3lFqi14+7xyApECzvkLTHCifx+7ntcrvtBIRcpg==",
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-6.0.4.tgz",
"integrity": "sha512-YF7Y52kFdFT/xVSuVdjkV5ZdX/3YtmX0QulG+x0taQOtJdHYzVU61aSSkAgVJ7NOv6qPkIYiJSgSWWN/DM5sGw==",
"dev": true,
"dependencies": {
"@babel/helper-module-imports": "^7.18.6",
@ -4359,7 +4359,7 @@
"peerDependencies": {
"@babel/core": "^7.0.0",
"@types/babel__core": "^7.1.9",
"rollup": "^1.20.0||^2.0.0||^3.0.0"
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"@types/babel__core": {
@ -4371,9 +4371,9 @@
}
},
"node_modules/@rollup/plugin-commonjs": {
"version": "25.0.4",
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.4.tgz",
"integrity": "sha512-L92Vz9WUZXDnlQQl3EwbypJR4+DM2EbsO+/KOcEkP4Mc6Ct453EeDB2uH9lgRwj4w5yflgNpq9pHOiY8aoUXBQ==",
"version": "25.0.5",
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.5.tgz",
"integrity": "sha512-xY8r/A9oisSeSuLCTfhssyDjo9Vp/eDiRLXkg1MXCcEEgEjPmLU+ZyDB20OOD0NlyDa/8SGbK5uIggF5XTx77w==",
"dev": true,
"dependencies": {
"@rollup/pluginutils": "^5.0.1",
@ -4387,7 +4387,7 @@
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^2.68.0||^3.0.0"
"rollup": "^2.68.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
@ -4396,9 +4396,9 @@
}
},
"node_modules/@rollup/plugin-node-resolve": {
"version": "15.2.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.1.tgz",
"integrity": "sha512-nsbUg588+GDSu8/NS8T4UAshO6xeaOfINNuXeVHcKV02LJtoRaM1SiOacClw4kws1SFiNhdLGxlbMY9ga/zs/w==",
"version": "15.2.3",
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz",
"integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==",
"dev": true,
"dependencies": {
"@rollup/pluginutils": "^5.0.1",
@ -4412,7 +4412,7 @@
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^2.78.0||^3.0.0"
"rollup": "^2.78.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
@ -4421,9 +4421,9 @@
}
},
"node_modules/@rollup/plugin-replace": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-5.0.2.tgz",
"integrity": "sha512-M9YXNekv/C/iHHK+cvORzfRYfPbq0RDD8r0G+bMiTXjNGKulPnCT9O3Ss46WfhI6ZOCgApOP7xAdmCQJ+U2LAA==",
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-5.0.3.tgz",
"integrity": "sha512-je7fu05B800IrMlWjb2wzJcdXzHYW46iTipfChnBDbIbDXhASZs27W1B58T2Yf45jZtJUONegpbce+9Ut2Ti/Q==",
"dev": true,
"dependencies": {
"@rollup/pluginutils": "^5.0.1",
@ -4433,7 +4433,7 @@
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0||^3.0.0"
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
@ -4442,9 +4442,9 @@
}
},
"node_modules/@rollup/plugin-terser": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.3.tgz",
"integrity": "sha512-EF0oejTMtkyhrkwCdg0HJ0IpkcaVg1MMSf2olHb2Jp+1mnLM04OhjpJWGma4HobiDTF0WCyViWuvadyE9ch2XA==",
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz",
"integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==",
"dev": true,
"dependencies": {
"serialize-javascript": "^6.0.1",
@ -4455,7 +4455,7 @@
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^2.x || ^3.x"
"rollup": "^2.0.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
@ -4464,9 +4464,9 @@
}
},
"node_modules/@rollup/plugin-typescript": {
"version": "11.1.4",
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.1.4.tgz",
"integrity": "sha512-WZRh5LBVLQXdKFICUId5J3eIpmjGURaBqntfg3GSZACgeOAFS+lOSMGTwfzDkELTaZVp/lWdMVNU3UkwCUBg/Q==",
"version": "11.1.5",
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.1.5.tgz",
"integrity": "sha512-rnMHrGBB0IUEv69Q8/JGRD/n4/n6b3nfpufUu26axhUcboUzv/twfZU8fIBbTOphRAe0v8EyxzeDpKXqGHfyDA==",
"dev": true,
"dependencies": {
"@rollup/pluginutils": "^5.0.1",
@ -4476,7 +4476,7 @@
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^2.14.0||^3.0.0",
"rollup": "^2.14.0||^3.0.0||^4.0.0",
"tslib": "*",
"typescript": ">=3.7.0"
},
@ -4490,9 +4490,9 @@
}
},
"node_modules/@rollup/pluginutils": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.4.tgz",
"integrity": "sha512-0KJnIoRI8A+a1dqOYLxH8vBf8bphDmty5QvIm2hqm7oFCFYKCAZWWd2hXgMibaPsNDhI0AtpYfQZJG47pt/k4g==",
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.5.tgz",
"integrity": "sha512-6aEYR910NyP73oHiJglti74iRyOwgFU4x3meH/H8OJx6Ry0j6cOVZ5X/wTvub7G7Ao6qaHBEaNsV3GLJkSsF+Q==",
"dev": true,
"dependencies": {
"@types/estree": "^1.0.0",
@ -4503,7 +4503,7 @@
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0||^3.0.0"
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
@ -4511,6 +4511,162 @@
}
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.0.2.tgz",
"integrity": "sha512-xDvk1pT4vaPU2BOLy0MqHMdYZyntqpaBf8RhBiezlqG9OjY8F50TyctHo8znigYKd+QCFhCmlmXHOL/LoaOl3w==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.0.2.tgz",
"integrity": "sha512-lqCglytY3E6raze27DD9VQJWohbwCxzqs9aSHcj5X/8hJpzZfNdbsr4Ja9Hqp6iPyF53+5PtPx0pKRlkSvlHZg==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.0.2.tgz",
"integrity": "sha512-nkBKItS6E6CCzvRwgiKad+j+1ibmL7SIInj7oqMWmdkCjiSX6VeVZw2mLlRKIUL+JjsBgpATTfo7BiAXc1v0jA==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.0.2.tgz",
"integrity": "sha512-vX2C8xvWPIbpEgQht95+dY6BReKAvtDgPDGi0XN0kWJKkm4WdNmq5dnwscv/zxvi+n6jUTBhs6GtpkkWT4q8Gg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.0.2.tgz",
"integrity": "sha512-DVFIfcHOjgmeHOAqji4xNz2wczt1Bmzy9MwBZKBa83SjBVO/i38VHDR+9ixo8QpBOiEagmNw12DucG+v55tCrg==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.0.2.tgz",
"integrity": "sha512-GCK/a9ItUxPI0V5hQEJjH4JtOJO90GF2Hja7TO+EZ8rmkGvEi8/ZDMhXmcuDpQT7/PWrTT9RvnG8snMd5SrhBQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.0.2.tgz",
"integrity": "sha512-cLuBp7rOjIB1R2j/VazjCmHC7liWUur2e9mFflLJBAWCkrZ+X0+QwHLvOQakIwDymungzAKv6W9kHZnTp/Mqrg==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.0.2.tgz",
"integrity": "sha512-Zqw4iVnJr2naoyQus0yLy7sLtisCQcpdMKUCeXPBjkJtpiflRime/TMojbnl8O3oxUAj92mxr+t7im/RbgA20w==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.0.2.tgz",
"integrity": "sha512-jJRU9TyUD/iMqjf8aLAp7XiN3pIj5v6Qcu+cdzBfVTKDD0Fvua4oUoK8eVJ9ZuKBEQKt3WdlcwJXFkpmMLk6kg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.0.2.tgz",
"integrity": "sha512-ZkS2NixCxHKC4zbOnw64ztEGGDVIYP6nKkGBfOAxEPW71Sji9v8z3yaHNuae/JHPwXA+14oDefnOuVfxl59SmQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.0.2.tgz",
"integrity": "sha512-3SKjj+tvnZ0oZq2BKB+fI+DqYI83VrRzk7eed8tJkxeZ4zxJZcLSE8YDQLYGq1tZAnAX+H076RHHB4gTZXsQzw==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.0.2.tgz",
"integrity": "sha512-MBdJIOxRauKkry7t2q+rTHa3aWjVez2eioWg+etRVS3dE4tChhmt5oqZYr48R6bPmcwEhxQr96gVRfeQrLbqng==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
]
},
"node_modules/@sentry-internal/tracing": {
"version": "7.73.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.73.0.tgz",
@ -7500,6 +7656,22 @@
"node": ">=12"
}
},
"node_modules/@storybook/builder-vite/node_modules/rollup": {
"version": "3.29.4",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz",
"integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==",
"dev": true,
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=14.18.0",
"npm": ">=8.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/@storybook/channels": {
"version": "7.4.1",
"resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.4.1.tgz",
@ -13606,15 +13778,15 @@
}
},
"node_modules/eslint": {
"version": "8.50.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.50.0.tgz",
"integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==",
"version": "8.51.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz",
"integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
"@eslint/eslintrc": "^2.1.2",
"@eslint/js": "8.50.0",
"@eslint/js": "8.51.0",
"@humanwhocodes/config-array": "^0.11.11",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
@ -13710,9 +13882,9 @@
}
},
"node_modules/eslint-plugin-storybook": {
"version": "0.6.14",
"resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.6.14.tgz",
"integrity": "sha512-IeYigPur/MvESNDo43Z+Z5UvlcEVnt0dDZmnw1odi9X2Th1R3bpGyOZsHXb9bp1pFecOpRUuoMG5xdID2TwwOg==",
"version": "0.6.15",
"resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.6.15.tgz",
"integrity": "sha512-lAGqVAJGob47Griu29KXYowI4G7KwMoJDOkEip8ujikuDLxU+oWJ1l0WL6F2oDO4QiyUFXvtDkEkISMOPzo+7w==",
"dev": true,
"dependencies": {
"@storybook/csf": "^0.0.1",
@ -20233,18 +20405,30 @@
"integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="
},
"node_modules/rollup": {
"version": "3.29.4",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz",
"integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==",
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.0.2.tgz",
"integrity": "sha512-MCScu4usMPCeVFaiLcgMDaBQeYi1z6vpWxz0r0hq0Hv77Y2YuOTZldkuNJ54BdYBH3e+nkrk6j0Rre/NLDBYzg==",
"dev": true,
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=14.18.0",
"node": ">=18.0.0",
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.0.2",
"@rollup/rollup-android-arm64": "4.0.2",
"@rollup/rollup-darwin-arm64": "4.0.2",
"@rollup/rollup-darwin-x64": "4.0.2",
"@rollup/rollup-linux-arm-gnueabihf": "4.0.2",
"@rollup/rollup-linux-arm64-gnu": "4.0.2",
"@rollup/rollup-linux-arm64-musl": "4.0.2",
"@rollup/rollup-linux-x64-gnu": "4.0.2",
"@rollup/rollup-linux-x64-musl": "4.0.2",
"@rollup/rollup-win32-arm64-msvc": "4.0.2",
"@rollup/rollup-win32-ia32-msvc": "4.0.2",
"@rollup/rollup-win32-x64-msvc": "4.0.2",
"fsevents": "~2.3.2"
}
},
@ -23000,6 +23184,23 @@
}
}
},
"node_modules/vite/node_modules/rollup": {
"version": "3.29.4",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz",
"integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==",
"dev": true,
"peer": true,
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=14.18.0",
"npm": ">=8.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/vscode-css-languageservice": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-4.3.0.tgz",

View file

@ -35,7 +35,7 @@
"@codemirror/theme-one-dark": "^6.1.2",
"@formatjs/intl-listformat": "^7.4.2",
"@fortawesome/fontawesome-free": "^6.4.2",
"@goauthentik/api": "^2023.8.3-1696335052",
"@goauthentik/api": "^2023.8.3-1696847703",
"@lit-labs/context": "^0.4.1",
"@lit-labs/task": "^3.0.2",
"@lit/localize": "^0.11.4",
@ -73,12 +73,12 @@
"@jackfranklin/rollup-plugin-markdown": "^0.4.0",
"@jeysal/storybook-addon-css-user-preferences": "^0.2.0",
"@lit/localize-tools": "^0.6.10",
"@rollup/plugin-babel": "^6.0.3",
"@rollup/plugin-commonjs": "^25.0.4",
"@rollup/plugin-node-resolve": "^15.2.1",
"@rollup/plugin-replace": "^5.0.2",
"@rollup/plugin-terser": "^0.4.3",
"@rollup/plugin-typescript": "^11.1.4",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-commonjs": "^25.0.5",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-replace": "^5.0.3",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^11.1.5",
"@storybook/addon-essentials": "^7.4.6",
"@storybook/addon-links": "^7.4.6",
"@storybook/blocks": "^7.1.1",
@ -93,19 +93,19 @@
"babel-plugin-macros": "^3.1.0",
"babel-plugin-tsconfig-paths": "^1.0.3",
"cross-env": "^7.0.3",
"eslint": "^8.50.0",
"eslint": "^8.51.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-custom-elements": "0.0.8",
"eslint-plugin-lit": "^1.9.1",
"eslint-plugin-sonarjs": "^0.21.0",
"eslint-plugin-storybook": "^0.6.14",
"eslint-plugin-storybook": "^0.6.15",
"lit-analyzer": "^1.2.1",
"npm-run-all": "^4.1.5",
"prettier": "^3.0.3",
"pyright": "^1.1.330",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"rollup": "^3.29.4",
"rollup": "^4.0.2",
"rollup-plugin-copy": "^3.5.0",
"rollup-plugin-cssimport": "^1.0.3",
"rollup-plugin-postcss-lit": "^2.1.0",

View file

@ -318,6 +318,24 @@ export class SAMLProviderFormPage extends ModelForm<SAMLProvider, number> {
</p>
<ak-utils-time-delta-help></ak-utils-time-delta-help>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Default relay state")}
?required=${true}
name="defaultRelayState"
>
<input
type="text"
value="${this.instance?.defaultRelayState || ""}"
class="pf-c-form-control"
required
/>
<p class="pf-c-form__helper-text">
${msg(
"When using IDP-initiated logins, the relay state will be set to this value.",
)}
</p>
<ak-utils-time-delta-help></ak-utils-time-delta-help>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Digest algorithm")}

View file

@ -994,10 +994,6 @@
<source>Validate SSL Certificates of upstream servers.</source>
<target>SSL-Zertifikate der Upstream-Server prüfen.</target>
</trans-unit>
<trans-unit id="s9c73dced379c37a2">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a manged outpost, this is done for you).</source>
<target>Verwenden Sie diesen Provider mit auth_request von Nginx oder forwardAuth von Traefik. Jede Anwendung/Domäne benötigt ihren eigenen Provider. Zusätzlich muss auf jeder Domain /outpost.goauthentik.io an den Außenposten weitergeleitet werden (wenn Sie einen gemanagten Außenposten verwenden, wird dies für Sie erledigt).</target>
</trans-unit>
<trans-unit id="s44c90273f08fb718">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Only a single provider is required per root domain. You can't do per-application authorization, but you don't have to create a provider for each application.</source>
<target>Verwenden Sie diesen Anbieter mit auth_request von nginx oder forwardAuth von traefik. Pro Root-Domain wird nur ein einziger Anbieter benötigt. Sie können keine Autorisierung pro Anwendung vornehmen, aber Sie müssen nicht für jede Anwendung einen Anbieter erstellen.</target>
@ -5925,6 +5921,15 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="se9e9e1d6799b86a5">
<source>WebAuthn not supported by browser.</source>
</trans-unit>
<trans-unit id="sff0ac1ace2d90709">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a managed outpost, this is done for you).</source>
</trans-unit>
<trans-unit id="scb58b8a60cad8762">
<source>Default relay state</source>
</trans-unit>
<trans-unit id="s6827a456c9dfc6ee">
<source>When using IDP-initiated logins, the relay state will be set to this value.</source>
</trans-unit>
</body>
</file>

View file

@ -1041,10 +1041,6 @@
<source>Validate SSL Certificates of upstream servers.</source>
<target>Validate SSL Certificates of upstream servers.</target>
</trans-unit>
<trans-unit id="s9c73dced379c37a2">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a manged outpost, this is done for you).</source>
<target>Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a manged outpost, this is done for you).</target>
</trans-unit>
<trans-unit id="s44c90273f08fb718">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Only a single provider is required per root domain. You can't do per-application authorization, but you don't have to create a provider for each application.</source>
<target>Use this provider with nginx's auth_request or traefik's forwardAuth. Only a single provider is required per root domain. You can't do per-application authorization, but you don't have to create a provider for each application.</target>
@ -6239,6 +6235,15 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="se9e9e1d6799b86a5">
<source>WebAuthn not supported by browser.</source>
</trans-unit>
<trans-unit id="sff0ac1ace2d90709">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a managed outpost, this is done for you).</source>
</trans-unit>
<trans-unit id="scb58b8a60cad8762">
<source>Default relay state</source>
</trans-unit>
<trans-unit id="s6827a456c9dfc6ee">
<source>When using IDP-initiated logins, the relay state will be set to this value.</source>
</trans-unit>
</body>
</file>

View file

@ -976,10 +976,6 @@
<source>Validate SSL Certificates of upstream servers.</source>
<target>Validar los certificados SSL de los servidores ascendentes.</target>
</trans-unit>
<trans-unit id="s9c73dced379c37a2">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a manged outpost, this is done for you).</source>
<target>Use este proveedor con auth_request de nginx o ForwardAuth de traefik. Cada aplicación/dominio necesita su propio proveedor. Además, en cada dominio, /outpost.goauthentik.io debe enrutarse al puesto avanzado (cuando se usa un puesto avanzado administrado, esto se hace por usted).</target>
</trans-unit>
<trans-unit id="s44c90273f08fb718">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Only a single provider is required per root domain. You can't do per-application authorization, but you don't have to create a provider for each application.</source>
<target>Use este proveedor con auth_request de nginx o ForwardAuth de traefik. Solo se requiere un único proveedor por dominio raíz. No puede realizar la autorización por solicitud, pero no tiene que crear un proveedor para cada solicitud.</target>
@ -5833,6 +5829,15 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="se9e9e1d6799b86a5">
<source>WebAuthn not supported by browser.</source>
</trans-unit>
<trans-unit id="sff0ac1ace2d90709">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a managed outpost, this is done for you).</source>
</trans-unit>
<trans-unit id="scb58b8a60cad8762">
<source>Default relay state</source>
</trans-unit>
<trans-unit id="s6827a456c9dfc6ee">
<source>When using IDP-initiated logins, the relay state will be set to this value.</source>
</trans-unit>
</body>
</file>

View file

@ -1295,11 +1295,6 @@ Il y a <x id="0" equiv-text="${ago}"/> jour(s)</target>
<source>Validate SSL Certificates of upstream servers.</source>
<target>Valider les certificats SSL des serveurs amonts.</target>
</trans-unit>
<trans-unit id="s9c73dced379c37a2">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a manged outpost, this is done for you).</source>
<target>Utilisez ce fournisseur avec l'option "auth_request" de Nginx ou "forwardAuth" de Traefik. Chaque application/domaine a besoin de son propre fournisseur. De plus, sur chaque domaine, "/outpost.goauthentik.io" doit être routé vers le poste avancé (lorsque vous utilisez un poste avancé géré, cela est fait pour vous).</target>
</trans-unit>
<trans-unit id="s44c90273f08fb718">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Only a single provider is required per root domain. You can't do per-application authorization, but you don't have to create a provider for each application.</source>
@ -7816,6 +7811,15 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
<trans-unit id="se9e9e1d6799b86a5">
<source>WebAuthn not supported by browser.</source>
<target>WebAuthn n'est pas supporté pas ce navigateur.</target>
</trans-unit>
<trans-unit id="sff0ac1ace2d90709">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a managed outpost, this is done for you).</source>
</trans-unit>
<trans-unit id="scb58b8a60cad8762">
<source>Default relay state</source>
</trans-unit>
<trans-unit id="s6827a456c9dfc6ee">
<source>When using IDP-initiated logins, the relay state will be set to this value.</source>
</trans-unit>
</body>
</file>

View file

@ -1002,10 +1002,6 @@
<source>Validate SSL Certificates of upstream servers.</source>
<target>Sprawdź poprawność certyfikatów SSL serwerów nadrzędnych.</target>
</trans-unit>
<trans-unit id="s9c73dced379c37a2">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a manged outpost, this is done for you).</source>
<target>Użyj tego dostawcy z auth_request nginx lub forwardAuth traefik. Każda aplikacja/domena potrzebuje własnego dostawcy. Dodatkowo w każdej domenie /outpost.goauthentik.io musi być przekierowany do placówki (w przypadku korzystania z zarządzanej placówki jest to zrobione za Ciebie).</target>
</trans-unit>
<trans-unit id="s44c90273f08fb718">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Only a single provider is required per root domain. You can't do per-application authorization, but you don't have to create a provider for each application.</source>
<target>Użyj tego dostawcy z auth_request nginx lub forwardAuth traefik. Tylko jeden dostawca jest wymagany na domenę główną. Nie możesz wykonać autoryzacji dla aplikacji, ale nie musisz tworzyć dostawcy dla każdej aplikacji.</target>
@ -6072,6 +6068,15 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="se9e9e1d6799b86a5">
<source>WebAuthn not supported by browser.</source>
</trans-unit>
<trans-unit id="sff0ac1ace2d90709">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a managed outpost, this is done for you).</source>
</trans-unit>
<trans-unit id="scb58b8a60cad8762">
<source>Default relay state</source>
</trans-unit>
<trans-unit id="s6827a456c9dfc6ee">
<source>When using IDP-initiated logins, the relay state will be set to this value.</source>
</trans-unit>
</body>
</file>

View file

@ -1025,10 +1025,6 @@
<trans-unit id="s4a26798e1c3c37dd">
<source>Validate SSL Certificates of upstream servers.</source>
</trans-unit>
<trans-unit id="s9c73dced379c37a2">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a manged outpost, this is done for you).</source>
</trans-unit>
<trans-unit id="s44c90273f08fb718">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Only a single provider is required per root domain. You can't do per-application authorization, but you don't have to create a provider for each application.</source>
@ -6174,6 +6170,15 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="se9e9e1d6799b86a5">
<source>WebAuthn not supported by browser.</source>
</trans-unit>
<trans-unit id="sff0ac1ace2d90709">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a managed outpost, this is done for you).</source>
</trans-unit>
<trans-unit id="scb58b8a60cad8762">
<source>Default relay state</source>
</trans-unit>
<trans-unit id="s6827a456c9dfc6ee">
<source>When using IDP-initiated logins, the relay state will be set to this value.</source>
</trans-unit>
</body>
</file>

View file

@ -975,10 +975,6 @@
<source>Validate SSL Certificates of upstream servers.</source>
<target>Yayın yukarı akış sunucularının SSL Sertifikalarını doğrulayın.</target>
</trans-unit>
<trans-unit id="s9c73dced379c37a2">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a manged outpost, this is done for you).</source>
<target>Bu sağlayıcıyı nginx'in auth_request veya traefik's forwardAuth ile kullanın. Her uygulama/etki alanının kendi sağlayıcısına ihtiyacı vardır. Ayrıca, her etki alanında /outpost.goauthentik.io üsse yönlendirilmelidir (manged bir üs kullanırken, bu sizin için yapılır).</target>
</trans-unit>
<trans-unit id="s44c90273f08fb718">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Only a single provider is required per root domain. You can't do per-application authorization, but you don't have to create a provider for each application.</source>
<target>Bu sağlayıcıyı nginx'in auth_request veya traefik'in forwardAuth ile kullanın. Kök etki alanı başına yalnızca tek bir sağlayıcı gereklidir. Uygulama başına yetkilendirme yapamazsınız, ancak her uygulama için bir sağlayıcı oluşturmanız gerekmez.</target>
@ -5826,6 +5822,15 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="se9e9e1d6799b86a5">
<source>WebAuthn not supported by browser.</source>
</trans-unit>
<trans-unit id="sff0ac1ace2d90709">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a managed outpost, this is done for you).</source>
</trans-unit>
<trans-unit id="scb58b8a60cad8762">
<source>Default relay state</source>
</trans-unit>
<trans-unit id="s6827a456c9dfc6ee">
<source>When using IDP-initiated logins, the relay state will be set to this value.</source>
</trans-unit>
</body>
</file>

View file

@ -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">
@ -613,9 +613,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 &quot;<x id="0" equiv-text="${this.url}"/>&quot; was not found.</source>
<target>未找到 URL &quot;
<x id="0" equiv-text="${this.url}"/>&quot;。</target>
</trans-unit>
<trans-unit id="s58cd9c2fe836d9c6">
@ -1067,8 +1067,8 @@
</trans-unit>
<trans-unit id="sa8384c9c26731f83">
<source>To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have.</source>
<target>要允许任何重定向 URI请将此值设置为 ".*"。请注意这可能带来的安全影响。</target>
<source>To allow any redirect URI, set this value to &quot;.*&quot;. Be aware of the possible security implications this can have.</source>
<target>要允许任何重定向 URI请将此值设置为 &quot;.*&quot;。请注意这可能带来的安全影响。</target>
</trans-unit>
<trans-unit id="s55787f4dfcdce52b">
@ -1295,11 +1295,6 @@
<source>Validate SSL Certificates of upstream servers.</source>
<target>验证上游服务器的 SSL 证书。</target>
</trans-unit>
<trans-unit id="s9c73dced379c37a2">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a manged outpost, this is done for you).</source>
<target>与 nginx 的 auth_request 或 traefik 的 ForwardAuth 一起使用此提供程序。每个应用程序/域名都需要自己的提供程序。此外,在每个域名上,/outpost.goauthentik.io 必须路由到前哨(在使用托管的 Outpost 时,这已经为您处理好了)。</target>
</trans-unit>
<trans-unit id="s44c90273f08fb718">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Only a single provider is required per root domain. You can't do per-application authorization, but you don't have to create a provider for each application.</source>
@ -1814,8 +1809,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 &quot;fa-test&quot;.</source>
<target>输入完整 URL、相对路径或者使用 'fa://fa-test' 来使用 Font Awesome 图标 &quot;fa-test&quot;。</target>
</trans-unit>
<trans-unit id="s0410779cb47de312">
@ -3238,8 +3233,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 &quot;memberUid&quot; 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>包含组成员的字段。请注意,如果使用 &quot;memberUid&quot; 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...'</target>
</trans-unit>
<trans-unit id="s026555347e589f0e">
@ -4031,8 +4026,8 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s7b1fba26d245cb1c">
<source>When using an external logging solution for archiving, this can be set to "minutes=5".</source>
<target>使用外部日志记录解决方案进行存档时,可以将其设置为 "minutes=5"。</target>
<source>When using an external logging solution for archiving, this can be set to &quot;minutes=5&quot;.</source>
<target>使用外部日志记录解决方案进行存档时,可以将其设置为 &quot;minutes=5&quot;。</target>
</trans-unit>
<trans-unit id="s44536d20bb5c8257">
@ -4041,8 +4036,8 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s3bb51cabb02b997e">
<source>Format: "weeks=3;days=2;hours=3,seconds=2".</source>
<target>格式:"weeks=3;days=2;hours=3,seconds=2"。</target>
<source>Format: &quot;weeks=3;days=2;hours=3,seconds=2&quot;.</source>
<target>格式:&quot;weeks=3;days=2;hours=3,seconds=2&quot;。</target>
</trans-unit>
<trans-unit id="s04bfd02201db5ab8">
@ -4238,10 +4233,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}"/> &quot;<x id="1" equiv-text="${this.obj?.name}"/>&quot;?</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}"/>&quot;
<x id="1" equiv-text="${this.obj?.name}"/>&quot; 吗?</target>
</trans-unit>
<trans-unit id="sc92d7cfb6ee1fec6">
@ -5342,7 +5337,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 &quot;roaming&quot; authenticator, like a YubiKey</source>
<target>像 YubiKey 这样的“漫游”身份验证器</target>
</trans-unit>
@ -5677,10 +5672,10 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<source><x id="0" equiv-text="${prompt.name}"/> (&quot;<x id="1" equiv-text="${prompt.fieldKey}"/>&quot;, of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<target>
<x id="0" equiv-text="${prompt.name}"/>"
<x id="1" equiv-text="${prompt.fieldKey}"/>",类型为
<x id="0" equiv-text="${prompt.name}"/>&quot;
<x id="1" equiv-text="${prompt.fieldKey}"/>&quot;,类型为
<x id="2" equiv-text="${prompt.type}"/></target>
</trans-unit>
@ -5729,7 +5724,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 &quot;stay signed in&quot;, which will extend their session by the time specified here.</source>
<target>如果设置时长大于 0用户可以选择“保持登录”选项这将使用户的会话延长此处设置的时间。</target>
</trans-unit>
@ -7818,7 +7813,19 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="se9e9e1d6799b86a5">
<source>WebAuthn not supported by browser.</source>
<target>浏览器不支持 WebAuthn。</target>
</trans-unit>
<trans-unit id="sff0ac1ace2d90709">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a managed outpost, this is done for you).</source>
<target>与 nginx 的 auth_request 或 traefik 的 ForwardAuth 一起使用此提供程序。每个应用程序/域名都需要自己的提供程序。此外,在每个域名上,/outpost.goauthentik.io 必须路由到前哨(在使用托管的 Outpost 时,这已经为您处理好了)。</target>
</trans-unit>
<trans-unit id="scb58b8a60cad8762">
<source>Default relay state</source>
<target>默认中继状态</target>
</trans-unit>
<trans-unit id="s6827a456c9dfc6ee">
<source>When using IDP-initiated logins, the relay state will be set to this value.</source>
<target>当使用 IDP 发起的登录时,中继状态会被设置为此值。</target>
</trans-unit>
</body>
</file>
</xliff>
</xliff>

View file

@ -983,10 +983,6 @@
<source>Validate SSL Certificates of upstream servers.</source>
<target>验证上游服务器的 SSL 证书。</target>
</trans-unit>
<trans-unit id="s9c73dced379c37a2">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a manged outpost, this is done for you).</source>
<target>将此提供程序与 nginx 的 auth_request 或 traefik 的 ForwardAuth 一起使用。每个应用程序/域都需要自己的提供商。此外,在每个域上,/outpost.goauthentik.io必须路由到 Outpost使用托管的 Outpost 时,这是为您完成的)。</target>
</trans-unit>
<trans-unit id="s44c90273f08fb718">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Only a single provider is required per root domain. You can't do per-application authorization, but you don't have to create a provider for each application.</source>
<target>将此提供程序与 nginx 的 auth_request 或 traefik 的 ForwardAuth 一起使用。每个根域只需要一个提供程序。您无法执行每个应用程序的授权,但不必为每个应用程序创建提供程序。</target>
@ -5878,6 +5874,15 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="se9e9e1d6799b86a5">
<source>WebAuthn not supported by browser.</source>
</trans-unit>
<trans-unit id="sff0ac1ace2d90709">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a managed outpost, this is done for you).</source>
</trans-unit>
<trans-unit id="scb58b8a60cad8762">
<source>Default relay state</source>
</trans-unit>
<trans-unit id="s6827a456c9dfc6ee">
<source>When using IDP-initiated logins, the relay state will be set to this value.</source>
</trans-unit>
</body>
</file>

View file

@ -1295,11 +1295,6 @@
<source>Validate SSL Certificates of upstream servers.</source>
<target>验证上游服务器的 SSL 证书。</target>
</trans-unit>
<trans-unit id="s9c73dced379c37a2">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a manged outpost, this is done for you).</source>
<target>与 nginx 的 auth_request 或 traefik 的 ForwardAuth 一起使用此提供程序。每个应用程序/域名都需要自己的提供程序。此外,在每个域名上,/outpost.goauthentik.io 必须路由到前哨(在使用托管的 Outpost 时,这已经为您处理好了)。</target>
</trans-unit>
<trans-unit id="s44c90273f08fb718">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Only a single provider is required per root domain. You can't do per-application authorization, but you don't have to create a provider for each application.</source>
@ -7818,6 +7813,18 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="se9e9e1d6799b86a5">
<source>WebAuthn not supported by browser.</source>
<target>浏览器不支持 WebAuthn。</target>
</trans-unit>
<trans-unit id="sff0ac1ace2d90709">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a managed outpost, this is done for you).</source>
<target>与 nginx 的 auth_request 或 traefik 的 ForwardAuth 一起使用此提供程序。每个应用程序/域名都需要自己的提供程序。此外,在每个域名上,/outpost.goauthentik.io 必须路由到前哨(在使用托管的 Outpost 时,这已经为您处理好了)。</target>
</trans-unit>
<trans-unit id="scb58b8a60cad8762">
<source>Default relay state</source>
<target>默认中继状态</target>
</trans-unit>
<trans-unit id="s6827a456c9dfc6ee">
<source>When using IDP-initiated logins, the relay state will be set to this value.</source>
<target>当使用 IDP 发起的登录时,中继状态会被设置为此值。</target>
</trans-unit>
</body>
</file>

View file

@ -983,10 +983,6 @@
<source>Validate SSL Certificates of upstream servers.</source>
<target>验证上游服务器的 SSL 证书。</target>
</trans-unit>
<trans-unit id="s9c73dced379c37a2">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a manged outpost, this is done for you).</source>
<target>将此提供程序与 nginx 的 auth_request 或 traefik 的 ForwardAuth 一起使用。每个应用程序/域都需要自己的提供商。此外,在每个域上,/outpost.goauthentik.io必须路由到 Outpost使用托管的 Outpost 时,这是为您完成的)。</target>
</trans-unit>
<trans-unit id="s44c90273f08fb718">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Only a single provider is required per root domain. You can't do per-application authorization, but you don't have to create a provider for each application.</source>
<target>将此提供程序与 nginx 的 auth_request 或 traefik 的 ForwardAuth 一起使用。每个根域只需要一个提供程序。您无法执行每个应用程序的授权,但不必为每个应用程序创建提供程序。</target>
@ -5877,6 +5873,15 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="se9e9e1d6799b86a5">
<source>WebAuthn not supported by browser.</source>
</trans-unit>
<trans-unit id="sff0ac1ace2d90709">
<source>Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a managed outpost, this is done for you).</source>
</trans-unit>
<trans-unit id="scb58b8a60cad8762">
<source>Default relay state</source>
</trans-unit>
<trans-unit id="s6827a456c9dfc6ee">
<source>When using IDP-initiated logins, the relay state will be set to this value.</source>
</trans-unit>
</body>
</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

View file

@ -0,0 +1,81 @@
---
title: "We need to talk about SCIM: More deviation than standard"
description: "SCIMs many deviations, undocumented edge cases, and lack of official test coverage make it an especially complex protocol to implement."
slug: 2023-10-05-SCIMs-many-deviations
authors:
- name: Jens Langhammer
title: CTO at Authentik Security Inc
url: https://github.com/BeryJu
image_url: https://github.com/BeryJu.png
tags:
- SCIM
- SSO
- open source
- community
- identity provider
- security
- authentication
hide_table_of_contents: false
image: ./image1.png
---
> **_authentik is an open source Identity Provider that unifies your identity needs into a single platform, replacing Okta, Active Directory, and auth0. Authentik Security is a [public benefit company](https://github.com/OpenCoreVentures/ocv-public-benefit-company/blob/main/ocv-public-benefit-company-charter.md) building on top of the open source project._**
---
As a young security company, weve been working on our implementation of SCIM (System for Cross-domain Identity Management), which Ill share more about below. SCIM is in many ways a great improvement on LDAP, but weve run into challenges in implementation and some things just seem to be harder than they need to be. Is it just us?
!["authentik admin interface"](./image1.png)
<!--truncate-->
# Improvements on LDAP
From a security standpoint, its wise not to expose LDAP (Lightweight Directory Access Protocol) to the internet if youre using Active Directory, OpenLDAP, FreeIPA or anything similar as your source of truth for authentication. SCIM fills a need for directory synchronization in a cloud-native world in which many companies arent hosting the software they use on their own servers.
SCIM, being an HTTP API specification, is much simpler and (in theory) gives you less to worry about than LDAP (being its own specific protocol). SCIM also offers time- and cost-saving advantages over Just in Time provisioning, especially for scaling companies. SCIM can save hours of company time for IT admins who no longer have to manually create individual accounts across multiple applications for new team members. Offboarding is also streamlined as departing team members can be deprovisioned automatically, preventing unauthorized access.
Most modern SaaS applications support SCIM, making it essential for security vendors to support the protocol, but it does come with its drawbacks.
# Growing pains
authentik currently supports SCIM going outwards; which means is that authentik is your source of truth/central directory, and you can use authentik together with a tool like [Sentry](https://sentry.io) that supports SCIM. In this case all your users or employees in authentik automatically get created in Sentry, with their correct group assignment, and they can just log in.
Most of the information and commentary I see about SCIM focuses on the advantages described above, but I dont see a lot of talk about the pitfalls of SCIM. Im sharing our experiences here and am curious if others have found the same or can tell me how theyre avoiding these (I would love to hear that were doing this wrong actually!).
## Deviation from standards isnt well documented
Implementing a protocol based on reading the RFCs and then writing the code is in itself not fun (to be fair, this is true for implementing any protocol based on a standard). Having implemented SCIM in line with the specification though, once we actually started testing with different solutions that can receive SCIM, we discovered a lot of quirks along the lines of x solution doesnt do y (which the documentation says they should) or they do it slightly differently, and so on.
This leads to a lot of workarounds which shouldnt be necessary or things that simply dont work without a clear cause. For example, when we started testing SCIM with Sentry, we ran into a lot of deviations (to their credit these were mostly listed in their [documentation](https://docs.sentry.io/product/accounts/sso/#scim-provisioning)). One of the issues I ran into when testing locally was when we created a user with SCIM, it just returned an error saying, “Please enter a valid email address” even though we _had_ sent it a valid email address. At least Sentry has the advantage of being open source, so we can just go and look at the code and see whats happening, but this is still no small effort and you dont have that option with closed source solutions.
You can see other examples of confusing/unexpected behavior from SCIM [here](https://github.com/goauthentik/authentik/issues/5396) and [here](https://github.com/goauthentik/authentik/issues/6695).
## Testing isnt built out
Some protocols make a big effort to uphold the adherence to the standard. OpenID Connect is another standard thats well defined by multiple RFCs, but also has a lot of room for vendor-specific quirks. However, with OpenID we have the reassurance that the [OpenID Foundation](https://openid.net/foundation/) is behind it.
The OpenID Foundation is a non-profit standards body of which Authentik Security is a member, but anyone can join to contribute to working groups that support implementation. OpenID Connect offers an [entire test suite](https://openid.net/certification/about-conformance-suite/) made up of hundreds of tests that you can run against your implementation, testing for edge cases and all the behaviors that they define. If you pass all the required tests you can send them the test results and get a [certification](https://openid.net/certification/) (which we are also working on) that your software adheres to the standards.
Instead of working in the dark and trying to make sure youve interpreted the specs correctly (while testing with vendors who might have their own interpretations), you have some reassurance that youre doing the right things when developing with OpenID Connect.
To my knowledge there isnt an official equivalent for SCIM—there are some smaller community projects that try to do something similar, but again, then you have to rely on someones interpretation of the standard. Even the [SCIM websites overview page](https://scim.cloud/) says, “Information on this overview page is not normative.”
## Updating a user is unnecessarily complex
As mentioned above, authentik currently supports SCIM in one direction, but we are [working on making it so that another application can send SCIM to authentik](https://github.com/goauthentik/authentik/pull/3051), to create users in it. In this process weve discovered that updating a user is surprisingly annoying to implement. With SCIM [you have two options to update a user](https://datatracker.ietf.org/doc/html/rfc7644#autoid-22):
- You can either send a request to replace the user (for which you have to send _all_ the users data), or
- You can send a patch request
A lot of vendors use the patch request option to update group membership: they send a patch request for a user and just say, for example, “Add that group,” or “Remove that group.” This approach makes more sense in the case of an advanced user with tons of groups, as youre not replacing everything, just making adjustments to their membership. However, this patch request is done with a custom filtering expression language which is extremely and needlessly complex.
My first thought when I encountered this was, “Okay, can I just parse this with RegEx?” but its not possible. The correct way to parse it is with [ANTLR](https://www.antlr.org/), a parser generator for different kinds of grammars. The thing about ANTLR is that its a type of tool usually used to build a compiler: it allows you to define a grammar for which it generates a parser that can then parse things in said grammar. Its not typically used for filtering language for directories and there are a lot of existing syntaxes that could have been used for this purpose. While luckily some people have written a full grammar for this, I was hoping that there would at least be an official definition for an ANTLR grammar.
# Immaturity bites
LDAP, being the more mature protocol (introduced in the 90s), has the advantage that deviations have been well documented and kinks ironed out. There are a handful of “standard” implementations like Active Directory, FreeIPA and some others. Similar to SAML support—theres just been a lot more time to document edge cases and workarounds.
SCIM, despite being around since 2015, is still subject to a lot of different interpretations of the standard, which leads to varying implementations and quirks with how vendors do SCIM. Theres a maturity challenge at work here in both senses—from the vendors but also from ourselves. Since weve added SCIM to our product a lot later than LDAP, theres still a lot of room for us to catch up and make our implementation better.
_Have you worked on SCIM implementation? Got advice for us? Wed love to hear from you in the comments._

View file

@ -51,7 +51,7 @@ Applications can either match users on a unique ID sent by authentik called `ext
#### OAuth/OIDC
The default provider configuration for the _Subject mode_ option of _Based on the User's hashed ID_ matches the `externalId` that's generated by default. If any other _Subjet mode_ is selected, the `externalId` attribute can be customized via SCIM mappings.
The default provider configuration for the _Subject mode_ option of _Based on the User's hashed ID_ matches the `externalId` that's generated by default. If any other _Subject mode_ is selected, the `externalId` attribute can be customized via SCIM mappings.
#### SAML