From 3455bf3d27dd35456994a27423da679d2480eb1b Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Tue, 7 Jun 2022 22:26:01 +0200 Subject: [PATCH 01/23] policies: consolidate log user and application Signed-off-by: Jens Langhammer --- authentik/policies/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/authentik/policies/views.py b/authentik/policies/views.py index 96b785afb..ae1c8abfd 100644 --- a/authentik/policies/views.py +++ b/authentik/policies/views.py @@ -117,8 +117,8 @@ class PolicyAccessView(AccessMixin, View): result = policy_engine.result LOGGER.debug( "PolicyAccessView user_has_access", - user=user, - app=self.application, + user=user.username, + app=self.application.slug, result=result, ) if not result.passing: From 6559fdee154b6585fc6fc36b48a94ad1873809fc Mon Sep 17 00:00:00 2001 From: Jens L Date: Wed, 8 Jun 2022 20:50:48 +0200 Subject: [PATCH 02/23] stages/authenticator_validate: add webauthn tests (#3069) Signed-off-by: Jens Langhammer --- authentik/root/test_runner.py | 1 + .../authenticator_validate/challenge.py | 2 +- .../tests/test_webauthn.py | 203 +++++++++++++++++- .../stages/authenticator_webauthn/stage.py | 2 +- .../stages/authenticator_webauthn/tests.py | 87 +++++++- 5 files changed, 282 insertions(+), 13 deletions(-) diff --git a/authentik/root/test_runner.py b/authentik/root/test_runner.py index 806c3c0b6..9cbbf7bb3 100644 --- a/authentik/root/test_runner.py +++ b/authentik/root/test_runner.py @@ -43,6 +43,7 @@ class PytestTestRunner: # pragma: no cover 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") def run_tests(self, test_labels): """Run pytest and return the exitcode. diff --git a/authentik/stages/authenticator_validate/challenge.py b/authentik/stages/authenticator_validate/challenge.py index c747be7a4..2ee28d96e 100644 --- a/authentik/stages/authenticator_validate/challenge.py +++ b/authentik/stages/authenticator_validate/challenge.py @@ -120,7 +120,7 @@ def validate_challenge_webauthn(data: dict, stage_view: StageView, user: User) - device = WebAuthnDevice.objects.filter(credential_id=credential_id).first() if not device: - raise Http404() + raise ValidationError("Invalid device") try: authentication_verification = verify_authentication_response( diff --git a/authentik/stages/authenticator_validate/tests/test_webauthn.py b/authentik/stages/authenticator_validate/tests/test_webauthn.py index d13c04035..ad1ac7fce 100644 --- a/authentik/stages/authenticator_validate/tests/test_webauthn.py +++ b/authentik/stages/authenticator_validate/tests/test_webauthn.py @@ -1,12 +1,12 @@ """Test validator stage""" from time import sleep -from django.http import Http404 from django.test.client import RequestFactory from django.urls.base import reverse -from webauthn.helpers import bytes_to_base64url +from rest_framework.serializers import ValidationError +from webauthn.helpers import base64url_to_bytes, bytes_to_base64url -from authentik.core.tests.utils import create_test_admin_user +from authentik.core.tests.utils import create_test_admin_user, create_test_flow from authentik.flows.models import Flow, FlowStageBinding, NotConfiguredAction from authentik.flows.stage import StageView from authentik.flows.tests import FlowTestCase @@ -15,10 +15,13 @@ from authentik.lib.generators import generate_id from authentik.lib.tests.utils import get_request from authentik.stages.authenticator_validate.challenge import ( get_challenge_for_device, + get_webauthn_challenge_without_user, validate_challenge_webauthn, ) from authentik.stages.authenticator_validate.models import AuthenticatorValidateStage, DeviceClasses +from authentik.stages.authenticator_validate.stage import AuthenticatorValidateStageView from authentik.stages.authenticator_webauthn.models import WebAuthnDevice +from authentik.stages.authenticator_webauthn.stage import SESSION_KEY_WEBAUTHN_CHALLENGE from authentik.stages.identification.models import IdentificationStage, UserFields @@ -104,7 +107,199 @@ class AuthenticatorValidateStageWebAuthnTests(FlowTestCase): }, ) - with self.assertRaises(Http404): + with self.assertRaises(ValidationError): validate_challenge_webauthn( {}, StageView(FlowExecutorView(current_stage=stage), request=request), self.user ) + + def test_get_challenge(self): + """Test webauthn""" + request = get_request("/") + request.user = self.user + + webauthn_device = WebAuthnDevice.objects.create( + user=self.user, + public_key=( + "pQECAyYgASFYIGsBLkklToCQkT7qJT_bJYN1sEc1oJdbnmoOc43i0J" + "H6IlggLTXytuhzFVYYAK4PQNj8_coGrbbzSfUxdiPAcZTQCyU" + ), + credential_id="QKZ97ASJAOIDyipAs6mKUxDUZgDrWrbAsUb5leL7-oU", + sign_count=0, + rp_id=generate_id(), + ) + challenge = get_challenge_for_device(request, webauthn_device) + webauthn_challenge = request.session[SESSION_KEY_WEBAUTHN_CHALLENGE] + self.assertEqual( + challenge, + { + "allowCredentials": [ + { + "id": "QKZ97ASJAOIDyipAs6mKUxDUZgDrWrbAsUb5leL7-oU", + "type": "public-key", + } + ], + "challenge": bytes_to_base64url(webauthn_challenge), + "rpId": "testserver", + "timeout": 60000, + "userVerification": "preferred", + }, + ) + + def test_get_challenge_userless(self): + """Test webauthn (userless)""" + request = get_request("/") + + WebAuthnDevice.objects.create( + user=self.user, + public_key=( + "pQECAyYgASFYIGsBLkklToCQkT7qJT_bJYN1sEc1oJdbnmoOc43i0J" + "H6IlggLTXytuhzFVYYAK4PQNj8_coGrbbzSfUxdiPAcZTQCyU" + ), + credential_id="QKZ97ASJAOIDyipAs6mKUxDUZgDrWrbAsUb5leL7-oU", + sign_count=0, + rp_id=generate_id(), + ) + challenge = get_webauthn_challenge_without_user(request) + webauthn_challenge = request.session[SESSION_KEY_WEBAUTHN_CHALLENGE] + self.assertEqual( + challenge, + { + "allowCredentials": [], + "challenge": bytes_to_base64url(webauthn_challenge), + "rpId": "testserver", + "timeout": 60000, + "userVerification": "preferred", + }, + ) + + def test_validate_challenge(self): + """Test webauthn""" + request = get_request("/") + request.user = self.user + + WebAuthnDevice.objects.create( + user=self.user, + public_key=( + "pQECAyYgASFYIGsBLkklToCQkT7qJT_bJYN1sEc1oJdbnmoOc43i0J" + "H6IlggLTXytuhzFVYYAK4PQNj8_coGrbbzSfUxdiPAcZTQCyU" + ), + credential_id="QKZ97ASJAOIDyipAs6mKUxDUZgDrWrbAsUb5leL7-oU", + sign_count=4, + rp_id=generate_id(), + ) + flow = create_test_flow() + stage = AuthenticatorValidateStage.objects.create( + name=generate_id(), + not_configured_action=NotConfiguredAction.CONFIGURE, + device_classes=[DeviceClasses.WEBAUTHN], + ) + stage_view = AuthenticatorValidateStageView( + FlowExecutorView(flow=flow, current_stage=stage), request=request + ) + request = get_request("/") + request.session[SESSION_KEY_WEBAUTHN_CHALLENGE] = base64url_to_bytes( + ( + "g98I51mQvZXo5lxLfhrD2zfolhZbLRyCgqkkYap1" + "jwSaJ13BguoJWCF9_Lg3AgO4Wh-Bqa556JE20oKsYbl6RA" + ) + ) + request.session.save() + + stage_view = AuthenticatorValidateStageView( + FlowExecutorView(flow=flow, current_stage=stage), request=request + ) + request.META["SERVER_NAME"] = "localhost" + request.META["SERVER_PORT"] = "9000" + validate_challenge_webauthn( + { + "id": "QKZ97ASJAOIDyipAs6mKUxDUZgDrWrbAsUb5leL7-oU", + "rawId": "QKZ97ASJAOIDyipAs6mKUxDUZgDrWrbAsUb5leL7-oU", + "type": "public-key", + "assertionClientExtensions": "{}", + "response": { + "clientDataJSON": ( + "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiZzk4STUxbVF2WlhvNWx4TGZo" + "ckQyemZvbGhaYkxSeUNncWtrWWFwMWp3U2FKMTNCZ3VvSldDRjlfTGczQWdPNFdoLUJxYTU1" + "NkpFMjBvS3NZYmw2UkEiLCJvcmlnaW4iOiJodHRwOi8vbG9jYWxob3N0OjkwMDAiLCJjcm9z" + "c09yaWdpbiI6ZmFsc2UsIm90aGVyX2tleXNfY2FuX2JlX2FkZGVkX2hlcmUiOiJkbyBub3Qg" + "Y29tcGFyZSBjbGllbnREYXRhSlNPTiBhZ2FpbnN0IGEgdGVtcGxhdGUuIFNlZSBodHRwczov" + "L2dvby5nbC95YWJQZXgifQ==", + ), + "signature": ( + "MEQCIFNlrHf9ablJAalXLWkrqvHB8oIu8kwvRpH3X3rbJVpI" + "AiAqtOK6mIZPk62kZN0OzFsHfuvu_RlOl7zlqSNzDdz_Ag==" + ), + "authenticatorData": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MFAAAABQ==", + "userHandle": None, + }, + }, + stage_view, + self.user, + ) + + def test_validate_challenge_invalid(self): + """Test webauthn""" + request = get_request("/") + request.user = self.user + + WebAuthnDevice.objects.create( + user=self.user, + public_key=( + "pQECAyYgASFYIGsBLkklToCQkT7qJT_bJYN1sEc1oJdbnmoOc4" + "3i0JH6IlggLTXytuhzFVYYAK4PQNj8_coGrbbzSfUxdiPAcZTQCyU" + ), + credential_id="QKZ97ASJAOIDyipAs6mKUxDUZgDrWrbAsUb5leL7-oU", + # One more sign count than above, make it invalid + sign_count=5, + rp_id=generate_id(), + ) + flow = create_test_flow() + stage = AuthenticatorValidateStage.objects.create( + name=generate_id(), + not_configured_action=NotConfiguredAction.CONFIGURE, + device_classes=[DeviceClasses.WEBAUTHN], + ) + stage_view = AuthenticatorValidateStageView( + FlowExecutorView(flow=flow, current_stage=stage), request=request + ) + request = get_request("/") + request.session[SESSION_KEY_WEBAUTHN_CHALLENGE] = base64url_to_bytes( + ( + "g98I51mQvZXo5lxLfhrD2zfolhZbLRyCgqkkYap1j" + "wSaJ13BguoJWCF9_Lg3AgO4Wh-Bqa556JE20oKsYbl6RA" + ) + ) + request.session.save() + + stage_view = AuthenticatorValidateStageView( + FlowExecutorView(flow=flow, current_stage=stage), request=request + ) + request.META["SERVER_NAME"] = "localhost" + request.META["SERVER_PORT"] = "9000" + with self.assertRaises(ValidationError): + validate_challenge_webauthn( + { + "id": "QKZ97ASJAOIDyipAs6mKUxDUZgDrWrbAsUb5leL7-oU", + "rawId": "QKZ97ASJAOIDyipAs6mKUxDUZgDrWrbAsUb5leL7-oU", + "type": "public-key", + "assertionClientExtensions": "{}", + "response": { + "clientDataJSON": ( + "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiZzk4STUxbVF2WlhvNWx4" + "TGZockQyemZvbGhaYkxSeUNncWtrWWFwMWp3U2FKMTNCZ3VvSldDRjlfTGczQWdPNFdo" + "LUJxYTU1NkpFMjBvS3NZYmw2UkEiLCJvcmlnaW4iOiJodHRwOi8vbG9jYWxob3N0Ojkw" + "MDAiLCJjcm9zc09yaWdpbiI6ZmFsc2UsIm90aGVyX2tleXNfY2FuX2JlX2FkZGVkX2hl" + "cmUiOiJkbyBub3QgY29tcGFyZSBjbGllbnREYXRhSlNPTiBhZ2FpbnN0IGEgdGVtcGxh" + "dGUuIFNlZSBodHRwczovL2dvby5nbC95YWJQZXgifQ==" + ), + "signature": ( + "MEQCIFNlrHf9ablJAalXLWkrqvHB8oIu8kwvRpH3X3rbJVpI" + "AiAqtOK6mIZPk62kZN0OzFsHfuvu_RlOl7zlqSNzDdz_Ag==" + ), + "authenticatorData": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MFAAAABQ==", + "userHandle": None, + }, + }, + stage_view, + self.user, + ) diff --git a/authentik/stages/authenticator_webauthn/stage.py b/authentik/stages/authenticator_webauthn/stage.py index f8fba9ffe..dfac992df 100644 --- a/authentik/stages/authenticator_webauthn/stage.py +++ b/authentik/stages/authenticator_webauthn/stage.py @@ -144,4 +144,4 @@ class AuthenticatorWebAuthnStageView(ChallengeStageView): return self.executor.stage_ok() def cleanup(self): - self.request.session.pop(SESSION_KEY_WEBAUTHN_CHALLENGE) + self.request.session.pop(SESSION_KEY_WEBAUTHN_CHALLENGE, None) diff --git a/authentik/stages/authenticator_webauthn/tests.py b/authentik/stages/authenticator_webauthn/tests.py index 9d4441bba..652a2b471 100644 --- a/authentik/stages/authenticator_webauthn/tests.py +++ b/authentik/stages/authenticator_webauthn/tests.py @@ -1,20 +1,93 @@ """Test WebAuthn API""" +from base64 import b64decode + from django.urls import reverse -from rest_framework.test import APITestCase +from webauthn.helpers import bytes_to_base64url -from authentik.core.models import User -from authentik.stages.authenticator_webauthn.models import WebAuthnDevice +from authentik.core.tests.utils import create_test_admin_user, create_test_flow +from authentik.flows.markers import StageMarker +from authentik.flows.models import FlowStageBinding +from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan +from authentik.flows.tests import FlowTestCase +from authentik.flows.views.executor import SESSION_KEY_PLAN +from authentik.lib.generators import generate_id +from authentik.stages.authenticator_webauthn.models import AuthenticateWebAuthnStage, WebAuthnDevice +from authentik.stages.authenticator_webauthn.stage import SESSION_KEY_WEBAUTHN_CHALLENGE -class AuthenticatorWebAuthnStage(APITestCase): +class TestAuthenticatorWebAuthnStage(FlowTestCase): """Test WebAuthn API""" + def setUp(self) -> None: + self.stage = AuthenticateWebAuthnStage.objects.create( + name=generate_id(), + ) + self.flow = create_test_flow() + self.binding = FlowStageBinding.objects.create( + target=self.flow, + stage=self.stage, + order=0, + ) + self.user = create_test_admin_user() + def test_api_delete(self): """Test api delete""" - user = User.objects.create(username="foo") - self.client.force_login(user) - dev = WebAuthnDevice.objects.create(user=user) + self.client.force_login(self.user) + dev = WebAuthnDevice.objects.create(user=self.user) response = self.client.delete( reverse("authentik_api:webauthndevice-detail", kwargs={"pk": dev.pk}) ) self.assertEqual(response.status_code, 204) + + def test_registration_options(self): + """Test registration options""" + plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]) + plan.context[PLAN_CONTEXT_PENDING_USER] = self.user + session = self.client.session + session[SESSION_KEY_PLAN] = plan + session[SESSION_KEY_WEBAUTHN_CHALLENGE] = b64decode( + ( + "o90Yh1osqW3mjGift+6WclWOya5lcdff/G0mqueN3hChacMUz" + "V4mxiDafuQ0x0e1d/fcPai0fx/jMBZ8/nG2qQ==" + ).encode() + ) + session.save() + + response = self.client.get( + reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}), + ) + self.assertEqual(response.status_code, 200) + session = self.client.session + self.assertStageResponse( + response, + self.flow, + self.user, + registration={ + "rp": {"name": "authentik", "id": "testserver"}, + "user": { + "id": bytes_to_base64url(self.user.uid.encode("utf-8")), + "name": self.user.username, + "displayName": self.user.name, + }, + "challenge": bytes_to_base64url(session[SESSION_KEY_WEBAUTHN_CHALLENGE]), + "pubKeyCredParams": [ + {"type": "public-key", "alg": -7}, + {"type": "public-key", "alg": -8}, + {"type": "public-key", "alg": -36}, + {"type": "public-key", "alg": -37}, + {"type": "public-key", "alg": -38}, + {"type": "public-key", "alg": -39}, + {"type": "public-key", "alg": -257}, + {"type": "public-key", "alg": -258}, + {"type": "public-key", "alg": -259}, + ], + "timeout": 60000, + "excludeCredentials": [], + "authenticatorSelection": { + "residentKey": "preferred", + "requireResidentKey": False, + "userVerification": "preferred", + }, + "attestation": "none", + }, + ) From b42eb9464f7c19f651c9bf4609872e8e6b08bec1 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Thu, 9 Jun 2022 20:09:51 +0200 Subject: [PATCH 03/23] lifecycle: run bootstrap tasks inline when using automated install Signed-off-by: Jens Langhammer --- .../core/management/commands/bootstrap_tasks.py | 13 +++++++++++++ authentik/root/celery.py | 16 +++++++++++----- lifecycle/ak | 3 +++ 3 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 authentik/core/management/commands/bootstrap_tasks.py diff --git a/authentik/core/management/commands/bootstrap_tasks.py b/authentik/core/management/commands/bootstrap_tasks.py new file mode 100644 index 000000000..74badb61e --- /dev/null +++ b/authentik/core/management/commands/bootstrap_tasks.py @@ -0,0 +1,13 @@ +"""Run bootstrap tasks""" +from django.core.management.base import BaseCommand + +from authentik.root.celery import _get_startup_tasks + + +class Command(BaseCommand): # pragma: no cover + """Run bootstrap tasks to ensure certain objects are created""" + + def handle(self, **options): + tasks = _get_startup_tasks() + for task in tasks: + task() diff --git a/authentik/root/celery.py b/authentik/root/celery.py index a0c7641fd..2b3d16c85 100644 --- a/authentik/root/celery.py +++ b/authentik/root/celery.py @@ -1,6 +1,7 @@ """authentik core celery""" import os from logging.config import dictConfig +from typing import Callable from celery import Celery from celery.signals import ( @@ -76,23 +77,28 @@ def task_error_hook(task_id, exception: Exception, traceback, *args, **kwargs): Event.new(EventAction.SYSTEM_EXCEPTION, message=exception_to_string(exception)).save() -@worker_ready.connect -def worker_ready_hook(*args, **kwargs): - """Run certain tasks on worker start""" +def _get_startup_tasks() -> list[Callable]: + """Get all tasks to be run on startup""" from authentik.admin.tasks import clear_update_notifications from authentik.managed.tasks import managed_reconcile from authentik.outposts.tasks import outpost_controller_all, outpost_local_connection from authentik.providers.proxy.tasks import proxy_set_defaults - tasks = [ + return [ clear_update_notifications, outpost_local_connection, outpost_controller_all, proxy_set_defaults, managed_reconcile, ] + + +@worker_ready.connect +def worker_ready_hook(*args, **kwargs): + """Run certain tasks on worker start""" + LOGGER.info("Dispatching startup tasks...") - for task in tasks: + for task in _get_startup_tasks(): try: task.delay() except ProgrammingError as exc: diff --git a/lifecycle/ak b/lifecycle/ak index b01ff2533..c304952ff 100755 --- a/lifecycle/ak +++ b/lifecycle/ak @@ -38,6 +38,9 @@ if [[ "$1" == "server" ]]; then wait_for_db echo "server" > $MODE_FILE python -m lifecycle.migrate + if [[ ! -z "${AUTHENTIK_BOOTSTRAP_PASSWORD}" || ! -z "${AUTHENTIK_BOOTSTRAP_TOKEN}" ]]; then + python -m manage bootstrap_tasks + fi /authentik-proxy elif [[ "$1" == "worker" ]]; then wait_for_db From 4c0e19cbea5a5e7df32d2da21c3a19265857dee9 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Fri, 10 Jun 2022 20:02:48 +0200 Subject: [PATCH 04/23] web/flows: remove autofocus from password field of identifications tage closes #2561 Signed-off-by: Jens Langhammer --- web/src/flows/stages/identification/IdentificationStage.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/web/src/flows/stages/identification/IdentificationStage.ts b/web/src/flows/stages/identification/IdentificationStage.ts index 71684683f..48ad42c68 100644 --- a/web/src/flows/stages/identification/IdentificationStage.ts +++ b/web/src/flows/stages/identification/IdentificationStage.ts @@ -240,7 +240,6 @@ export class IdentificationStage extends BaseStage< type="password" name="password" placeholder="${t`Password`}" - autofocus="" autocomplete="current-password" class="pf-c-form-control" required From 85784f796c900ab6ebded297e58fa2da1017af23 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Fri, 10 Jun 2022 20:10:27 +0200 Subject: [PATCH 05/23] root: ignore healthcheck routes in sentry tracing Signed-off-by: Jens Langhammer --- authentik/lib/sentry.py | 10 +++++++++- authentik/root/test_runner.py | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/authentik/lib/sentry.py b/authentik/lib/sentry.py index 0868bd7e3..94b48b8aa 100644 --- a/authentik/lib/sentry.py +++ b/authentik/lib/sentry.py @@ -56,7 +56,6 @@ def sentry_init(**sentry_init_kwargs): """Configure sentry SDK""" sentry_env = CONFIG.y("error_reporting.environment", "customer") kwargs = { - "traces_sample_rate": float(CONFIG.y("error_reporting.sample_rate", 0.5)), "environment": sentry_env, "send_default_pii": CONFIG.y_bool("error_reporting.send_pii", False), } @@ -71,6 +70,7 @@ def sentry_init(**sentry_init_kwargs): ThreadingIntegration(propagate_hub=True), ], before_send=before_send, + traces_sampler=traces_sampler, release=f"authentik@{__version__}", **kwargs, ) @@ -83,6 +83,14 @@ def sentry_init(**sentry_init_kwargs): ) +def traces_sampler(sampling_context: dict) -> float: + """Custom sampler to ignore certain routes""" + # Ignore all healthcheck routes + if sampling_context.get("asgi_scope", {}).get("path", "").startswith("/-/health/"): + return 0 + return float(CONFIG.y("error_reporting.sample_rate", 0.5)) + + def before_send(event: dict, hint: dict) -> Optional[dict]: """Check if error is database error, and ignore if so""" # pylint: disable=no-name-in-module diff --git a/authentik/root/test_runner.py b/authentik/root/test_runner.py index 9cbbf7bb3..2f3f7716f 100644 --- a/authentik/root/test_runner.py +++ b/authentik/root/test_runner.py @@ -33,8 +33,8 @@ class PytestTestRunner: # pragma: no cover "outposts.container_image_base", f"ghcr.io/goauthentik/dev-%(type)s:{get_docker_tag()}", ) + CONFIG.y_set("error_reporting.sample_rate", 1.0) sentry_init( - sample_rate=1.0, environment="testing", send_default_pii=True, ) From 59b899ddffbd4e4c2c88f7fd5f223be33ac930ea Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Fri, 10 Jun 2022 22:19:48 +0200 Subject: [PATCH 06/23] internal: skip tracing for go healthcheck and metrics endpoints Signed-off-by: Jens Langhammer --- authentik/lib/sentry.py | 3 +- authentik/root/urls.py | 2 +- cmd/server/main.go | 3 +- internal/outpost/ak/global.go | 9 +++--- internal/outpost/ldap/metrics/metrics.go | 2 ++ internal/outpost/proxyv2/handlers.go | 3 +- internal/outpost/proxyv2/metrics/metrics.go | 2 ++ internal/outpost/proxyv2/proxyv2.go | 3 +- internal/utils/sentry/sentry.go | 34 +++++++++++++++++++++ internal/web/metrics.go | 4 ++- 10 files changed, 55 insertions(+), 10 deletions(-) create mode 100644 internal/utils/sentry/sentry.go diff --git a/authentik/lib/sentry.py b/authentik/lib/sentry.py index 94b48b8aa..10057d84e 100644 --- a/authentik/lib/sentry.py +++ b/authentik/lib/sentry.py @@ -85,8 +85,9 @@ def sentry_init(**sentry_init_kwargs): def traces_sampler(sampling_context: dict) -> float: """Custom sampler to ignore certain routes""" + path = sampling_context.get("asgi_scope", {}).get("path", "") # Ignore all healthcheck routes - if sampling_context.get("asgi_scope", {}).get("path", "").startswith("/-/health/"): + if path.startswith("/-/health") or path.startswith("/-/metrics"): return 0 return float(CONFIG.y("error_reporting.sample_rate", 0.5)) diff --git a/authentik/root/urls.py b/authentik/root/urls.py index 6a7385d28..e1874a041 100644 --- a/authentik/root/urls.py +++ b/authentik/root/urls.py @@ -44,7 +44,7 @@ for _authentik_app in get_apps(): ) urlpatterns += [ - path("metrics/", MetricsView.as_view(), name="metrics"), + path("-/metrics/", MetricsView.as_view(), name="metrics"), path("-/health/live/", LiveView.as_view(), name="health-live"), path("-/health/ready/", ReadyView.as_view(), name="health-ready"), ] diff --git a/cmd/server/main.go b/cmd/server/main.go index 583925b3a..02b0164a5 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -15,6 +15,7 @@ import ( "goauthentik.io/internal/gounicorn" "goauthentik.io/internal/outpost/ak" "goauthentik.io/internal/outpost/proxyv2" + sentryutils "goauthentik.io/internal/utils/sentry" "goauthentik.io/internal/web" "goauthentik.io/internal/web/tenant_tls" ) @@ -51,7 +52,7 @@ func main() { err := sentry.Init(sentry.ClientOptions{ Dsn: config.G.ErrorReporting.DSN, AttachStacktrace: true, - TracesSampleRate: config.G.ErrorReporting.SampleRate, + TracesSampler: sentryutils.SamplerFunc(config.G.ErrorReporting.SampleRate), Release: fmt.Sprintf("authentik@%s", constants.VERSION), Environment: config.G.ErrorReporting.Environment, IgnoreErrors: []string{ diff --git a/internal/outpost/ak/global.go b/internal/outpost/ak/global.go index 154ce4a0d..1b7136fd8 100644 --- a/internal/outpost/ak/global.go +++ b/internal/outpost/ak/global.go @@ -11,6 +11,7 @@ import ( log "github.com/sirupsen/logrus" "goauthentik.io/api/v3" "goauthentik.io/internal/constants" + sentryutils "goauthentik.io/internal/utils/sentry" ) var initialSetup = false @@ -47,10 +48,10 @@ func doGlobalSetup(outpost api.Outpost, globalConfig *api.Config) { l.WithField("env", globalConfig.ErrorReporting.Environment).Debug("Error reporting enabled") } err := sentry.Init(sentry.ClientOptions{ - Dsn: dsn, - Environment: globalConfig.ErrorReporting.Environment, - TracesSampleRate: float64(globalConfig.ErrorReporting.TracesSampleRate), - Release: fmt.Sprintf("authentik@%s", constants.VERSION), + Dsn: dsn, + Environment: globalConfig.ErrorReporting.Environment, + TracesSampler: sentryutils.SamplerFunc(float64(globalConfig.ErrorReporting.TracesSampleRate)), + Release: fmt.Sprintf("authentik@%s", constants.VERSION), IgnoreErrors: []string{ http.ErrAbortHandler.Error(), }, diff --git a/internal/outpost/ldap/metrics/metrics.go b/internal/outpost/ldap/metrics/metrics.go index ecccb73d9..bbe233691 100644 --- a/internal/outpost/ldap/metrics/metrics.go +++ b/internal/outpost/ldap/metrics/metrics.go @@ -4,6 +4,7 @@ import ( "net/http" log "github.com/sirupsen/logrus" + "goauthentik.io/internal/utils/sentry" "github.com/gorilla/mux" "github.com/prometheus/client_golang/prometheus" @@ -25,6 +26,7 @@ var ( func RunServer() { m := mux.NewRouter() l := log.WithField("logger", "authentik.outpost.metrics") + m.Use(sentry.SentryNoSampleMiddleware) m.HandleFunc("/outpost.goauthentik.io/ping", func(rw http.ResponseWriter, r *http.Request) { rw.WriteHeader(204) }) diff --git a/internal/outpost/proxyv2/handlers.go b/internal/outpost/proxyv2/handlers.go index d547542af..bbcd567b2 100644 --- a/internal/outpost/proxyv2/handlers.go +++ b/internal/outpost/proxyv2/handlers.go @@ -11,6 +11,7 @@ import ( "goauthentik.io/api/v3" "goauthentik.io/internal/outpost/proxyv2/application" "goauthentik.io/internal/outpost/proxyv2/metrics" + sentryutils "goauthentik.io/internal/utils/sentry" "goauthentik.io/internal/utils/web" staticWeb "goauthentik.io/web" ) @@ -89,7 +90,7 @@ func (ps *ProxyServer) Handle(rw http.ResponseWriter, r *http.Request) { return } if strings.HasPrefix(r.URL.Path, "/outpost.goauthentik.io/ping") { - ps.HandlePing(rw, r) + sentryutils.SentryNoSample(ps.HandlePing)(rw, r) return } a, host := ps.lookupApp(r) diff --git a/internal/outpost/proxyv2/metrics/metrics.go b/internal/outpost/proxyv2/metrics/metrics.go index 770d2ce01..2848fff94 100644 --- a/internal/outpost/proxyv2/metrics/metrics.go +++ b/internal/outpost/proxyv2/metrics/metrics.go @@ -4,6 +4,7 @@ import ( "net/http" log "github.com/sirupsen/logrus" + "goauthentik.io/internal/utils/sentry" "github.com/gorilla/mux" "github.com/prometheus/client_golang/prometheus" @@ -25,6 +26,7 @@ var ( func RunServer() { m := mux.NewRouter() l := log.WithField("logger", "authentik.outpost.metrics") + m.Use(sentry.SentryNoSampleMiddleware) m.HandleFunc("/outpost.goauthentik.io/ping", func(rw http.ResponseWriter, r *http.Request) { rw.WriteHeader(204) }) diff --git a/internal/outpost/proxyv2/proxyv2.go b/internal/outpost/proxyv2/proxyv2.go index 8159e27c0..7cf87b66a 100644 --- a/internal/outpost/proxyv2/proxyv2.go +++ b/internal/outpost/proxyv2/proxyv2.go @@ -18,6 +18,7 @@ import ( "goauthentik.io/internal/outpost/ak" "goauthentik.io/internal/outpost/proxyv2/application" "goauthentik.io/internal/outpost/proxyv2/metrics" + sentryutils "goauthentik.io/internal/utils/sentry" "goauthentik.io/internal/utils/web" ) @@ -65,7 +66,7 @@ func NewProxyServer(ac *ak.APIController, portOffset int) *ProxyServer { defaultCert: defaultCert, } globalMux.PathPrefix("/outpost.goauthentik.io/static").HandlerFunc(s.HandleStatic) - globalMux.Path("/outpost.goauthentik.io/ping").HandlerFunc(s.HandlePing) + globalMux.Path("/outpost.goauthentik.io/ping").HandlerFunc(sentryutils.SentryNoSample(s.HandlePing)) rootMux.PathPrefix("/").HandlerFunc(s.Handle) return s } diff --git a/internal/utils/sentry/sentry.go b/internal/utils/sentry/sentry.go new file mode 100644 index 000000000..fb3f04d01 --- /dev/null +++ b/internal/utils/sentry/sentry.go @@ -0,0 +1,34 @@ +package sentry + +import ( + "context" + "net/http" + + "github.com/getsentry/sentry-go" +) + +type contextSentryNoSample struct{} + +func SentryNoSample(handler func(rw http.ResponseWriter, r *http.Request)) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + ctx := context.WithValue(r.Context(), contextSentryNoSample{}, true) + handler(w, r.WithContext(ctx)) + } +} + +func SentryNoSampleMiddleware(h http.Handler) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + ctx := context.WithValue(r.Context(), contextSentryNoSample{}, true) + h.ServeHTTP(rw, r.WithContext(ctx)) + }) +} + +func SamplerFunc(defaultRate float64) sentry.TracesSamplerFunc { + return sentry.TracesSamplerFunc(func(ctx sentry.SamplingContext) sentry.Sampled { + data, ok := ctx.Span.Context().Value(contextSentryNoSample{}).(bool) + if data && ok { + return sentry.SampledFalse + } + return sentry.UniformTracesSampler(defaultRate).Sample(ctx) + }) +} diff --git a/internal/web/metrics.go b/internal/web/metrics.go index 8762910bb..b102aab64 100644 --- a/internal/web/metrics.go +++ b/internal/web/metrics.go @@ -10,6 +10,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" log "github.com/sirupsen/logrus" "goauthentik.io/internal/config" + "goauthentik.io/internal/utils/sentry" ) var ( @@ -22,6 +23,7 @@ var ( func RunMetricsServer() { m := mux.NewRouter() l := log.WithField("logger", "authentik.router.metrics") + m.Use(sentry.SentryNoSampleMiddleware) m.Path("/metrics").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { promhttp.InstrumentMetricHandler( prometheus.DefaultRegisterer, promhttp.HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{ @@ -30,7 +32,7 @@ func RunMetricsServer() { ).ServeHTTP(rw, r) // Get upstream metrics - re, err := http.NewRequest("GET", "http://localhost:8000/metrics/", nil) + re, err := http.NewRequest("GET", "http://localhost:8000/-/metrics/", nil) if err != nil { l.WithError(err).Warning("failed to get upstream metrics") return From ec5ed67f6c5a5ff17fbd8895342eed3b3731ee4d Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Fri, 10 Jun 2022 22:50:40 +0200 Subject: [PATCH 07/23] web/flows: add divider to identification stage for security key Signed-off-by: Jens Langhammer --- .../identification/IdentificationStage.ts | 18 ++++++++++-------- web/src/locales/de.po | 4 ++++ web/src/locales/en.po | 4 ++++ web/src/locales/es.po | 4 ++++ web/src/locales/fr_FR.po | 4 ++++ web/src/locales/pl.po | 4 ++++ web/src/locales/pseudo-LOCALE.po | 4 ++++ web/src/locales/tr.po | 4 ++++ web/src/locales/zh-Hans.po | 4 ++++ web/src/locales/zh-Hant.po | 4 ++++ web/src/locales/zh_TW.po | 4 ++++ 11 files changed, 50 insertions(+), 8 deletions(-) diff --git a/web/src/flows/stages/identification/IdentificationStage.ts b/web/src/flows/stages/identification/IdentificationStage.ts index 48ad42c68..6926417e9 100644 --- a/web/src/flows/stages/identification/IdentificationStage.ts +++ b/web/src/flows/stages/identification/IdentificationStage.ts @@ -19,6 +19,7 @@ import { UserFieldsEnum, } from "@goauthentik/api"; +import "../../../elements/Divider"; import "../../../elements/EmptyState"; import "../../../elements/forms/FormElement"; import { BaseStage } from "../base"; @@ -257,14 +258,15 @@ export class IdentificationStage extends BaseStage< ${this.challenge.passwordlessUrl - ? html`` + ? html`${t`Or`} + ` : html``}`; } diff --git a/web/src/locales/de.po b/web/src/locales/de.po index 2697f7019..ab2b3a587 100644 --- a/web/src/locales/de.po +++ b/web/src/locales/de.po @@ -3680,6 +3680,10 @@ msgstr "Legen Sie optional den Wert „FriendlyName“ des Assertion-Attributs f #~ msgid "Optionally set this to your parent domain, if you want authentication and authorization to happen on a domain level. If you're running applications as app1.domain.tld, app2.domain.tld, set this to 'domain.tld'." #~ msgstr "Setzen Sie dies optional auf Ihre übergeordnete Domäne, wenn Sie die Authentifizierung und Autorisierung auf Domänenebene durchführen möchten. Wenn Sie Anwendungen als app1.domain.tld, app2.domain.tld ausführen, setzen Sie dies auf „domain.tld“." +#: src/flows/stages/identification/IdentificationStage.ts +msgid "Or" +msgstr "" + #: src/pages/flows/BoundStagesList.ts #: src/pages/flows/StageBindingForm.ts #: src/pages/policies/BoundPoliciesList.ts diff --git a/web/src/locales/en.po b/web/src/locales/en.po index dea6d0602..4072d640e 100644 --- a/web/src/locales/en.po +++ b/web/src/locales/en.po @@ -3740,6 +3740,10 @@ msgstr "Optionally set the 'FriendlyName' value of the Assertion attribute." #~ msgid "Optionally set this to your parent domain, if you want authentication and authorization to happen on a domain level. If you're running applications as app1.domain.tld, app2.domain.tld, set this to 'domain.tld'." #~ msgstr "Optionally set this to your parent domain, if you want authentication and authorization to happen on a domain level. If you're running applications as app1.domain.tld, app2.domain.tld, set this to 'domain.tld'." +#: src/flows/stages/identification/IdentificationStage.ts +msgid "Or" +msgstr "Or" + #: src/pages/flows/BoundStagesList.ts #: src/pages/flows/StageBindingForm.ts #: src/pages/policies/BoundPoliciesList.ts diff --git a/web/src/locales/es.po b/web/src/locales/es.po index ca2410463..12b596db0 100644 --- a/web/src/locales/es.po +++ b/web/src/locales/es.po @@ -3673,6 +3673,10 @@ msgstr "Si lo desea, defina el valor «FriendlyName» del atributo Assertion." #~ msgid "Optionally set this to your parent domain, if you want authentication and authorization to happen on a domain level. If you're running applications as app1.domain.tld, app2.domain.tld, set this to 'domain.tld'." #~ msgstr "Si lo desea, configúrelo en su dominio principal, si desea que la autenticación y la autorización se realicen a nivel de dominio. Si ejecuta aplicaciones como app1.domain.tld, app2.domain.tld, defina esto en «domain.tld»." +#: src/flows/stages/identification/IdentificationStage.ts +msgid "Or" +msgstr "" + #: src/pages/flows/BoundStagesList.ts #: src/pages/flows/StageBindingForm.ts #: src/pages/policies/BoundPoliciesList.ts diff --git a/web/src/locales/fr_FR.po b/web/src/locales/fr_FR.po index c6753fabf..e6d373793 100644 --- a/web/src/locales/fr_FR.po +++ b/web/src/locales/fr_FR.po @@ -3708,6 +3708,10 @@ msgstr "Indiquer la valeur \"FriendlyName\" de l'attribut d'assertion (optionnel #~ msgid "Optionally set this to your parent domain, if you want authentication and authorization to happen on a domain level. If you're running applications as app1.domain.tld, app2.domain.tld, set this to 'domain.tld'." #~ msgstr "Indiquer votre domaine parent (optionnel), si vous souhaitez que l'authentification et l'autorisation soient réalisés au niveau du domaine. Si vous exécutez des applications sur app1.domain.tld, app2.domain.tld, indiquez ici \"domain.tld\"." +#: src/flows/stages/identification/IdentificationStage.ts +msgid "Or" +msgstr "" + #: src/pages/flows/BoundStagesList.ts #: src/pages/flows/StageBindingForm.ts #: src/pages/policies/BoundPoliciesList.ts diff --git a/web/src/locales/pl.po b/web/src/locales/pl.po index c56520cc1..088a2035b 100644 --- a/web/src/locales/pl.po +++ b/web/src/locales/pl.po @@ -3670,6 +3670,10 @@ msgstr "Opcjonalnie ustaw wartość „FriendlyName” atrybutu asercji." #~ msgid "Optionally set this to your parent domain, if you want authentication and authorization to happen on a domain level. If you're running applications as app1.domain.tld, app2.domain.tld, set this to 'domain.tld'." #~ msgstr "Opcjonalnie ustaw to na swoją domenę nadrzędną, jeśli chcesz, aby uwierzytelnianie i autoryzacja odbywały się na poziomie domeny. Jeśli używasz aplikacji jako app1.domain.tld, app2.domain.tld, ustaw to na „domain.tld”." +#: src/flows/stages/identification/IdentificationStage.ts +msgid "Or" +msgstr "" + #: src/pages/flows/BoundStagesList.ts #: src/pages/flows/StageBindingForm.ts #: src/pages/policies/BoundPoliciesList.ts diff --git a/web/src/locales/pseudo-LOCALE.po b/web/src/locales/pseudo-LOCALE.po index 8238a0969..e1e80a1e7 100644 --- a/web/src/locales/pseudo-LOCALE.po +++ b/web/src/locales/pseudo-LOCALE.po @@ -3722,6 +3722,10 @@ msgstr "" #~ msgid "Optionally set this to your parent domain, if you want authentication and authorization to happen on a domain level. If you're running applications as app1.domain.tld, app2.domain.tld, set this to 'domain.tld'." #~ msgstr "" +#: src/flows/stages/identification/IdentificationStage.ts +msgid "Or" +msgstr "" + #: src/pages/flows/BoundStagesList.ts #: src/pages/flows/StageBindingForm.ts #: src/pages/policies/BoundPoliciesList.ts diff --git a/web/src/locales/tr.po b/web/src/locales/tr.po index 8540c9763..208cd662d 100644 --- a/web/src/locales/tr.po +++ b/web/src/locales/tr.po @@ -3675,6 +3675,10 @@ msgstr "İsteğe bağlı olarak onaylama özniteliğinin 'FriendlyName' değerin #~ msgid "Optionally set this to your parent domain, if you want authentication and authorization to happen on a domain level. If you're running applications as app1.domain.tld, app2.domain.tld, set this to 'domain.tld'." #~ msgstr "Kimlik doğrulama ve yetkilendirmenin etki alanı düzeyinde gerçekleşmesini istiyorsanız, isteğe bağlı olarak bunu üst etki alanınıza ayarlayın. Uygulamaları app1.domain.tld, app2.domain.tld olarak çalıştırıyorsanız, bunu 'domain.tld' olarak ayarlayın." +#: src/flows/stages/identification/IdentificationStage.ts +msgid "Or" +msgstr "" + #: src/pages/flows/BoundStagesList.ts #: src/pages/flows/StageBindingForm.ts #: src/pages/policies/BoundPoliciesList.ts diff --git a/web/src/locales/zh-Hans.po b/web/src/locales/zh-Hans.po index 6e12090f0..55170cb28 100644 --- a/web/src/locales/zh-Hans.po +++ b/web/src/locales/zh-Hans.po @@ -3651,6 +3651,10 @@ msgstr "可选,设置断言属性的 'FriendlyName' 值。" #~ msgid "Optionally set this to your parent domain, if you want authentication and authorization to happen on a domain level. If you're running applications as app1.domain.tld, app2.domain.tld, set this to 'domain.tld'." #~ msgstr "如果您希望在域名级别进行身份验证和授权,可以选择将其设置为您的父域名。如果您运行应用 app1.domain.tld、app2.domain.tld,请将其设置为 'domain.tld'。" +#: src/flows/stages/identification/IdentificationStage.ts +msgid "Or" +msgstr "" + #: src/pages/flows/BoundStagesList.ts #: src/pages/flows/StageBindingForm.ts #: src/pages/policies/BoundPoliciesList.ts diff --git a/web/src/locales/zh-Hant.po b/web/src/locales/zh-Hant.po index c1e709751..1c0f3b33a 100644 --- a/web/src/locales/zh-Hant.po +++ b/web/src/locales/zh-Hant.po @@ -3656,6 +3656,10 @@ msgstr "(可选)设置 “断言” 属性的'友好名称'值。" #~ msgid "Optionally set this to your parent domain, if you want authentication and authorization to happen on a domain level. If you're running applications as app1.domain.tld, app2.domain.tld, set this to 'domain.tld'." #~ msgstr "如果您希望在域级别进行身份验证和授权,可以选择将其设置为您的父域。如果你以 app1.domain.tld、app2.domain.tld 的身份运行应用程序,请将其设置为 “domain.tld”。" +#: src/flows/stages/identification/IdentificationStage.ts +msgid "Or" +msgstr "" + #: src/pages/flows/BoundStagesList.ts #: src/pages/flows/StageBindingForm.ts #: src/pages/policies/BoundPoliciesList.ts diff --git a/web/src/locales/zh_TW.po b/web/src/locales/zh_TW.po index c5b739176..8da4eeb3c 100644 --- a/web/src/locales/zh_TW.po +++ b/web/src/locales/zh_TW.po @@ -3656,6 +3656,10 @@ msgstr "(可选)设置 “断言” 属性的'友好名称'值。" #~ msgid "Optionally set this to your parent domain, if you want authentication and authorization to happen on a domain level. If you're running applications as app1.domain.tld, app2.domain.tld, set this to 'domain.tld'." #~ msgstr "如果您希望在域级别进行身份验证和授权,可以选择将其设置为您的父域。如果你以 app1.domain.tld、app2.domain.tld 的身份运行应用程序,请将其设置为 “domain.tld”。" +#: src/flows/stages/identification/IdentificationStage.ts +msgid "Or" +msgstr "" + #: src/pages/flows/BoundStagesList.ts #: src/pages/flows/StageBindingForm.ts #: src/pages/policies/BoundPoliciesList.ts From d0eb6af7e9088714f73692bc24a71d3f87669ef8 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Fri, 10 Jun 2022 22:59:59 +0200 Subject: [PATCH 08/23] web/admin: remove invalid requirement for usernames Signed-off-by: Jens Langhammer --- web/src/locales/de.po | 9 +++++++-- web/src/locales/en.po | 9 +++++++-- web/src/locales/es.po | 9 +++++++-- web/src/locales/fr_FR.po | 9 +++++++-- web/src/locales/pl.po | 9 +++++++-- web/src/locales/pseudo-LOCALE.po | 9 +++++++-- web/src/locales/tr.po | 9 +++++++-- web/src/locales/zh-Hans.po | 9 +++++++-- web/src/locales/zh-Hant.po | 9 +++++++-- web/src/locales/zh_TW.po | 9 +++++++-- web/src/pages/users/ServiceAccountForm.ts | 2 +- web/src/pages/users/UserForm.ts | 2 +- 12 files changed, 72 insertions(+), 22 deletions(-) diff --git a/web/src/locales/de.po b/web/src/locales/de.po index ab2b3a587..1c6f0ac05 100644 --- a/web/src/locales/de.po +++ b/web/src/locales/de.po @@ -4285,8 +4285,8 @@ msgstr "Erforderlich" #: src/pages/users/ServiceAccountForm.ts #: src/pages/users/UserForm.ts -msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." -msgstr "Erforderlich. 150 Zeichen oder weniger. Nur Buchstaben, Zahlen und @/./+/-/_." +#~ msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." +#~ msgstr "Erforderlich. 150 Zeichen oder weniger. Nur Buchstaben, Zahlen und @/./+/-/_." #: src/pages/users/UserViewPage.ts msgid "Reset Password" @@ -6210,6 +6210,11 @@ msgstr "Avatar des Benutzers" msgid "User's display name." msgstr "Anzeigename" +#: src/pages/users/ServiceAccountForm.ts +#: src/pages/users/UserForm.ts +msgid "User's primary identifier. 150 characters or fewer." +msgstr "" + #: src/pages/users/RelatedUserList.ts #: src/pages/users/UserListPage.ts msgid "User(s)" diff --git a/web/src/locales/en.po b/web/src/locales/en.po index 4072d640e..f684393b9 100644 --- a/web/src/locales/en.po +++ b/web/src/locales/en.po @@ -4363,8 +4363,8 @@ msgstr "Required." #: src/pages/users/ServiceAccountForm.ts #: src/pages/users/UserForm.ts -msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." -msgstr "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." +#~ msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." +#~ msgstr "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." #: src/pages/users/UserViewPage.ts msgid "Reset Password" @@ -6336,6 +6336,11 @@ msgstr "User's avatar" msgid "User's display name." msgstr "User's display name." +#: src/pages/users/ServiceAccountForm.ts +#: src/pages/users/UserForm.ts +msgid "User's primary identifier. 150 characters or fewer." +msgstr "User's primary identifier. 150 characters or fewer." + #: src/pages/users/RelatedUserList.ts #: src/pages/users/UserListPage.ts msgid "User(s)" diff --git a/web/src/locales/es.po b/web/src/locales/es.po index 12b596db0..54fe201f5 100644 --- a/web/src/locales/es.po +++ b/web/src/locales/es.po @@ -4278,8 +4278,8 @@ msgstr "Necesario." #: src/pages/users/ServiceAccountForm.ts #: src/pages/users/UserForm.ts -msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." -msgstr "Obligatorio. 150 caracteres o menos. Solo letras, dígitos y @/./+/-/_." +#~ msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." +#~ msgstr "Obligatorio. 150 caracteres o menos. Solo letras, dígitos y @/./+/-/_." #: src/pages/users/UserViewPage.ts msgid "Reset Password" @@ -6204,6 +6204,11 @@ msgstr "Avatar del usuario" msgid "User's display name." msgstr "Nombre para mostrar del usuario." +#: src/pages/users/ServiceAccountForm.ts +#: src/pages/users/UserForm.ts +msgid "User's primary identifier. 150 characters or fewer." +msgstr "" + #: src/pages/users/RelatedUserList.ts #: src/pages/users/UserListPage.ts msgid "User(s)" diff --git a/web/src/locales/fr_FR.po b/web/src/locales/fr_FR.po index e6d373793..32fd8f284 100644 --- a/web/src/locales/fr_FR.po +++ b/web/src/locales/fr_FR.po @@ -4325,8 +4325,8 @@ msgstr "Obligatoire." #: src/pages/users/ServiceAccountForm.ts #: src/pages/users/UserForm.ts -msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." -msgstr "Obligatoire. 150 caractères ou moins. Lettres, chiffres et @/./+/-/_ uniquement." +#~ msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." +#~ msgstr "Obligatoire. 150 caractères ou moins. Lettres, chiffres et @/./+/-/_ uniquement." #: src/pages/users/UserViewPage.ts msgid "Reset Password" @@ -6265,6 +6265,11 @@ msgstr "Avatar de l'utilisateu" msgid "User's display name." msgstr "Nom d'affichage de l'utilisateur" +#: src/pages/users/ServiceAccountForm.ts +#: src/pages/users/UserForm.ts +msgid "User's primary identifier. 150 characters or fewer." +msgstr "" + #: src/pages/users/RelatedUserList.ts #: src/pages/users/UserListPage.ts msgid "User(s)" diff --git a/web/src/locales/pl.po b/web/src/locales/pl.po index 088a2035b..3e92e7dea 100644 --- a/web/src/locales/pl.po +++ b/web/src/locales/pl.po @@ -4275,8 +4275,8 @@ msgstr "Wymagany." #: src/pages/users/ServiceAccountForm.ts #: src/pages/users/UserForm.ts -msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." -msgstr "Wymagane. 150 znaków lub mniej. Tylko litery, cyfry i @/./+/-/_." +#~ msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." +#~ msgstr "Wymagane. 150 znaków lub mniej. Tylko litery, cyfry i @/./+/-/_." #: src/pages/users/UserViewPage.ts msgid "Reset Password" @@ -6201,6 +6201,11 @@ msgstr "Awatar użytkownika" msgid "User's display name." msgstr "Wyświetlana nazwa użytkownika." +#: src/pages/users/ServiceAccountForm.ts +#: src/pages/users/UserForm.ts +msgid "User's primary identifier. 150 characters or fewer." +msgstr "" + #: src/pages/users/RelatedUserList.ts #: src/pages/users/UserListPage.ts msgid "User(s)" diff --git a/web/src/locales/pseudo-LOCALE.po b/web/src/locales/pseudo-LOCALE.po index e1e80a1e7..4500804c9 100644 --- a/web/src/locales/pseudo-LOCALE.po +++ b/web/src/locales/pseudo-LOCALE.po @@ -4343,8 +4343,8 @@ msgstr "" #: src/pages/users/ServiceAccountForm.ts #: src/pages/users/UserForm.ts -msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." -msgstr "" +#~ msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." +#~ msgstr "" #: src/pages/users/UserViewPage.ts msgid "Reset Password" @@ -6306,6 +6306,11 @@ msgstr "" msgid "User's display name." msgstr "" +#: src/pages/users/ServiceAccountForm.ts +#: src/pages/users/UserForm.ts +msgid "User's primary identifier. 150 characters or fewer." +msgstr "" + #: src/pages/users/RelatedUserList.ts #: src/pages/users/UserListPage.ts msgid "User(s)" diff --git a/web/src/locales/tr.po b/web/src/locales/tr.po index 208cd662d..4f2a01929 100644 --- a/web/src/locales/tr.po +++ b/web/src/locales/tr.po @@ -4280,8 +4280,8 @@ msgstr "Zorunlu." #: src/pages/users/ServiceAccountForm.ts #: src/pages/users/UserForm.ts -msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." -msgstr "Gerekli. 150 karakter veya daha az. Harfler, rakamlar ve yalnızca @/./+/-/_." +#~ msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." +#~ msgstr "Gerekli. 150 karakter veya daha az. Harfler, rakamlar ve yalnızca @/./+/-/_." #: src/pages/users/UserViewPage.ts msgid "Reset Password" @@ -6206,6 +6206,11 @@ msgstr "Kullanıcının avatarı" msgid "User's display name." msgstr "Kullanıcının görünen adı." +#: src/pages/users/ServiceAccountForm.ts +#: src/pages/users/UserForm.ts +msgid "User's primary identifier. 150 characters or fewer." +msgstr "" + #: src/pages/users/RelatedUserList.ts #: src/pages/users/UserListPage.ts msgid "User(s)" diff --git a/web/src/locales/zh-Hans.po b/web/src/locales/zh-Hans.po index 55170cb28..76b63e602 100644 --- a/web/src/locales/zh-Hans.po +++ b/web/src/locales/zh-Hans.po @@ -4247,8 +4247,8 @@ msgstr "必需。" #: src/pages/users/ServiceAccountForm.ts #: src/pages/users/UserForm.ts -msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." -msgstr "必填。不超过 150 个字符。仅限字母、数字和 @/./+/-/_ 。" +#~ msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." +#~ msgstr "必填。不超过 150 个字符。仅限字母、数字和 @/./+/-/_ 。" #: src/pages/users/UserViewPage.ts msgid "Reset Password" @@ -6163,6 +6163,11 @@ msgstr "用户的头像" msgid "User's display name." msgstr "用户的显示名称" +#: src/pages/users/ServiceAccountForm.ts +#: src/pages/users/UserForm.ts +msgid "User's primary identifier. 150 characters or fewer." +msgstr "" + #: src/pages/users/RelatedUserList.ts #: src/pages/users/UserListPage.ts msgid "User(s)" diff --git a/web/src/locales/zh-Hant.po b/web/src/locales/zh-Hant.po index 1c0f3b33a..d6b2471c0 100644 --- a/web/src/locales/zh-Hant.po +++ b/web/src/locales/zh-Hant.po @@ -4253,8 +4253,8 @@ msgstr "必需。" #: src/pages/users/ServiceAccountForm.ts #: src/pages/users/UserForm.ts -msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." -msgstr "必填。不超过 150 个字符。仅限字母、数字和 @/./+/-/_ 。" +#~ msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." +#~ msgstr "必填。不超过 150 个字符。仅限字母、数字和 @/./+/-/_ 。" #: src/pages/users/UserViewPage.ts msgid "Reset Password" @@ -6172,6 +6172,11 @@ msgstr "用户的头像" msgid "User's display name." msgstr "用户的显示名称。" +#: src/pages/users/ServiceAccountForm.ts +#: src/pages/users/UserForm.ts +msgid "User's primary identifier. 150 characters or fewer." +msgstr "" + #: src/pages/users/RelatedUserList.ts #: src/pages/users/UserListPage.ts msgid "User(s)" diff --git a/web/src/locales/zh_TW.po b/web/src/locales/zh_TW.po index 8da4eeb3c..7c9966763 100644 --- a/web/src/locales/zh_TW.po +++ b/web/src/locales/zh_TW.po @@ -4253,8 +4253,8 @@ msgstr "必需。" #: src/pages/users/ServiceAccountForm.ts #: src/pages/users/UserForm.ts -msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." -msgstr "必填。不超过 150 个字符。仅限字母、数字和 @/./+/-/_ 。" +#~ msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." +#~ msgstr "必填。不超过 150 个字符。仅限字母、数字和 @/./+/-/_ 。" #: src/pages/users/UserViewPage.ts msgid "Reset Password" @@ -6172,6 +6172,11 @@ msgstr "用户的头像" msgid "User's display name." msgstr "用户的显示名称。" +#: src/pages/users/ServiceAccountForm.ts +#: src/pages/users/UserForm.ts +msgid "User's primary identifier. 150 characters or fewer." +msgstr "" + #: src/pages/users/RelatedUserList.ts #: src/pages/users/UserListPage.ts msgid "User(s)" diff --git a/web/src/pages/users/ServiceAccountForm.ts b/web/src/pages/users/ServiceAccountForm.ts index 3a39ac657..eddb800ee 100644 --- a/web/src/pages/users/ServiceAccountForm.ts +++ b/web/src/pages/users/ServiceAccountForm.ts @@ -42,7 +42,7 @@ export class ServiceAccountForm extends Form {

- ${t`Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.`} + ${t`User's primary identifier. 150 characters or fewer.`}

diff --git a/web/src/pages/users/UserForm.ts b/web/src/pages/users/UserForm.ts index 87add8cfb..822c50eac 100644 --- a/web/src/pages/users/UserForm.ts +++ b/web/src/pages/users/UserForm.ts @@ -66,7 +66,7 @@ export class UserForm extends ModelForm { required />

- ${t`Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.`} + ${t`User's primary identifier. 150 characters or fewer.`}

From caed30634696c5f87d5b248190f10a3a7b882c7c Mon Sep 17 00:00:00 2001 From: Jens L Date: Fri, 10 Jun 2022 23:32:57 +0200 Subject: [PATCH 09/23] providers/oauth2: if a redirect_uri cannot be parsed as regex, compare strict (#3070) Signed-off-by: Jens Langhammer --- authentik/core/tests/utils.py | 2 +- authentik/crypto/builder.py | 10 +-- .../providers/oauth2/tests/test_authorize.py | 67 ++++++++++++++++--- .../providers/oauth2/tests/test_token.py | 32 ++++----- authentik/providers/oauth2/tests/utils.py | 8 +++ authentik/providers/oauth2/views/authorize.py | 16 +++-- authentik/providers/oauth2/views/token.py | 22 +++--- 7 files changed, 108 insertions(+), 49 deletions(-) diff --git a/authentik/core/tests/utils.py b/authentik/core/tests/utils.py index 74fbc048a..84ef0845e 100644 --- a/authentik/core/tests/utils.py +++ b/authentik/core/tests/utils.py @@ -47,11 +47,11 @@ def create_test_tenant() -> Tenant: def create_test_cert() -> CertificateKeyPair: """Generate a certificate for testing""" - CertificateKeyPair.objects.filter(name="goauthentik.io").delete() builder = CertificateBuilder() builder.common_name = "goauthentik.io" builder.build( subject_alt_names=["goauthentik.io"], validity_days=360, ) + builder.name = generate_id() return builder.save() diff --git a/authentik/crypto/builder.py b/authentik/crypto/builder.py index 2881750eb..1316d2555 100644 --- a/authentik/crypto/builder.py +++ b/authentik/crypto/builder.py @@ -53,10 +53,7 @@ class CertificateBuilder: .subject_name( x509.Name( [ - x509.NameAttribute( - NameOID.COMMON_NAME, - self.common_name, - ), + x509.NameAttribute(NameOID.COMMON_NAME, self.common_name), x509.NameAttribute(NameOID.ORGANIZATION_NAME, "authentik"), x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "Self-signed"), ] @@ -65,10 +62,7 @@ class CertificateBuilder: .issuer_name( x509.Name( [ - x509.NameAttribute( - NameOID.COMMON_NAME, - f"authentik {__version__}", - ), + x509.NameAttribute(NameOID.COMMON_NAME, f"authentik {__version__}"), ] ) ) diff --git a/authentik/providers/oauth2/tests/test_authorize.py b/authentik/providers/oauth2/tests/test_authorize.py index a5b2a8c7d..9f0aebb4e 100644 --- a/authentik/providers/oauth2/tests/test_authorize.py +++ b/authentik/providers/oauth2/tests/test_authorize.py @@ -3,7 +3,7 @@ from django.test import RequestFactory from django.urls import reverse from authentik.core.models import Application -from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow +from authentik.core.tests.utils import create_test_admin_user, create_test_flow from authentik.flows.challenge import ChallengeTypes from authentik.lib.generators import generate_id, generate_key from authentik.providers.oauth2.errors import AuthorizeError, ClientIdError, RedirectUriError @@ -39,7 +39,7 @@ class TestAuthorize(OAuthTestCase): def test_request(self): """test request param""" OAuth2Provider.objects.create( - name="test", + name=generate_id(), client_id="test", authorization_flow=create_test_flow(), redirect_uris="http://local.invalid/Foo", @@ -59,7 +59,7 @@ class TestAuthorize(OAuthTestCase): def test_invalid_redirect_uri(self): """test missing/invalid redirect URI""" OAuth2Provider.objects.create( - name="test", + name=generate_id(), client_id="test", authorization_flow=create_test_flow(), redirect_uris="http://local.invalid", @@ -78,10 +78,55 @@ class TestAuthorize(OAuthTestCase): ) OAuthAuthorizationParams.from_request(request) + def test_invalid_redirect_uri_empty(self): + """test missing/invalid redirect URI""" + provider = OAuth2Provider.objects.create( + name=generate_id(), + client_id="test", + authorization_flow=create_test_flow(), + redirect_uris="", + ) + with self.assertRaises(RedirectUriError): + request = self.factory.get("/", data={"response_type": "code", "client_id": "test"}) + OAuthAuthorizationParams.from_request(request) + request = self.factory.get( + "/", + data={ + "response_type": "code", + "client_id": "test", + "redirect_uri": "+", + }, + ) + OAuthAuthorizationParams.from_request(request) + provider.refresh_from_db() + self.assertEqual(provider.redirect_uris, "+") + def test_invalid_redirect_uri_regex(self): """test missing/invalid redirect URI""" OAuth2Provider.objects.create( - name="test", + name=generate_id(), + client_id="test", + authorization_flow=create_test_flow(), + redirect_uris="http://local.invalid?", + ) + with self.assertRaises(RedirectUriError): + request = self.factory.get("/", data={"response_type": "code", "client_id": "test"}) + OAuthAuthorizationParams.from_request(request) + with self.assertRaises(RedirectUriError): + request = self.factory.get( + "/", + data={ + "response_type": "code", + "client_id": "test", + "redirect_uri": "http://localhost", + }, + ) + OAuthAuthorizationParams.from_request(request) + + def test_redirect_uri_invalid_regex(self): + """test missing/invalid redirect URI (invalid regex)""" + OAuth2Provider.objects.create( + name=generate_id(), client_id="test", authorization_flow=create_test_flow(), redirect_uris="+", @@ -103,7 +148,7 @@ class TestAuthorize(OAuthTestCase): def test_empty_redirect_uri(self): """test empty redirect URI (configure in provider)""" OAuth2Provider.objects.create( - name="test", + name=generate_id(), client_id="test", authorization_flow=create_test_flow(), ) @@ -123,7 +168,7 @@ class TestAuthorize(OAuthTestCase): def test_response_type(self): """test response_type""" OAuth2Provider.objects.create( - name="test", + name=generate_id(), client_id="test", authorization_flow=create_test_flow(), redirect_uris="http://local.invalid/Foo", @@ -201,7 +246,7 @@ class TestAuthorize(OAuthTestCase): """Test full authorization""" flow = create_test_flow() provider = OAuth2Provider.objects.create( - name="test", + name=generate_id(), client_id="test", authorization_flow=flow, redirect_uris="foo://localhost", @@ -237,12 +282,12 @@ class TestAuthorize(OAuthTestCase): """Test full authorization""" flow = create_test_flow() provider = OAuth2Provider.objects.create( - name="test", + name=generate_id(), client_id="test", client_secret=generate_key(), authorization_flow=flow, redirect_uris="http://localhost", - signing_key=create_test_cert(), + signing_key=self.keypair, ) Application.objects.create(name="app", slug="app", provider=provider) state = generate_id() @@ -281,12 +326,12 @@ class TestAuthorize(OAuthTestCase): """Test full authorization (form_post response)""" flow = create_test_flow() provider = OAuth2Provider.objects.create( - name="test", + name=generate_id(), client_id="test", client_secret=generate_key(), authorization_flow=flow, redirect_uris="http://localhost", - signing_key=create_test_cert(), + signing_key=self.keypair, ) Application.objects.create(name="app", slug="app", provider=provider) state = generate_id() diff --git a/authentik/providers/oauth2/tests/test_token.py b/authentik/providers/oauth2/tests/test_token.py index bc1dba975..0da18abcc 100644 --- a/authentik/providers/oauth2/tests/test_token.py +++ b/authentik/providers/oauth2/tests/test_token.py @@ -5,7 +5,7 @@ from django.test import RequestFactory from django.urls import reverse from authentik.core.models import Application -from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow +from authentik.core.tests.utils import create_test_admin_user, create_test_flow from authentik.events.models import Event, EventAction from authentik.lib.generators import generate_id, generate_key from authentik.providers.oauth2.constants import ( @@ -24,17 +24,17 @@ class TestToken(OAuthTestCase): def setUp(self) -> None: super().setUp() self.factory = RequestFactory() - self.app = Application.objects.create(name="test", slug="test") + self.app = Application.objects.create(name=generate_id(), slug="test") def test_request_auth_code(self): """test request param""" provider = OAuth2Provider.objects.create( - name="test", + name=generate_id(), client_id=generate_id(), client_secret=generate_key(), authorization_flow=create_test_flow(), redirect_uris="http://testserver", - signing_key=create_test_cert(), + signing_key=self.keypair, ) header = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode() user = create_test_admin_user() @@ -56,12 +56,12 @@ class TestToken(OAuthTestCase): def test_request_auth_code_invalid(self): """test request param""" provider = OAuth2Provider.objects.create( - name="test", + name=generate_id(), client_id=generate_id(), client_secret=generate_key(), authorization_flow=create_test_flow(), redirect_uris="http://testserver", - signing_key=create_test_cert(), + signing_key=self.keypair, ) header = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode() request = self.factory.post( @@ -79,12 +79,12 @@ class TestToken(OAuthTestCase): def test_request_refresh_token(self): """test request param""" provider = OAuth2Provider.objects.create( - name="test", + name=generate_id(), client_id=generate_id(), client_secret=generate_key(), authorization_flow=create_test_flow(), redirect_uris="http://local.invalid", - signing_key=create_test_cert(), + signing_key=self.keypair, ) header = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode() user = create_test_admin_user() @@ -108,12 +108,12 @@ class TestToken(OAuthTestCase): def test_auth_code_view(self): """test request param""" provider = OAuth2Provider.objects.create( - name="test", + name=generate_id(), client_id=generate_id(), client_secret=generate_key(), authorization_flow=create_test_flow(), redirect_uris="http://local.invalid", - signing_key=create_test_cert(), + signing_key=self.keypair, ) # Needs to be assigned to an application for iss to be set self.app.provider = provider @@ -150,12 +150,12 @@ class TestToken(OAuthTestCase): def test_refresh_token_view(self): """test request param""" provider = OAuth2Provider.objects.create( - name="test", + name=generate_id(), client_id=generate_id(), client_secret=generate_key(), authorization_flow=create_test_flow(), redirect_uris="http://local.invalid", - signing_key=create_test_cert(), + signing_key=self.keypair, ) # Needs to be assigned to an application for iss to be set self.app.provider = provider @@ -199,12 +199,12 @@ class TestToken(OAuthTestCase): def test_refresh_token_view_invalid_origin(self): """test request param""" provider = OAuth2Provider.objects.create( - name="test", + name=generate_id(), client_id=generate_id(), client_secret=generate_key(), authorization_flow=create_test_flow(), redirect_uris="http://local.invalid", - signing_key=create_test_cert(), + signing_key=self.keypair, ) header = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode() user = create_test_admin_user() @@ -244,12 +244,12 @@ class TestToken(OAuthTestCase): def test_refresh_token_revoke(self): """test request param""" provider = OAuth2Provider.objects.create( - name="test", + name=generate_id(), client_id=generate_id(), client_secret=generate_key(), authorization_flow=create_test_flow(), redirect_uris="http://testserver", - signing_key=create_test_cert(), + signing_key=self.keypair, ) # Needs to be assigned to an application for iss to be set self.app.provider = provider diff --git a/authentik/providers/oauth2/tests/utils.py b/authentik/providers/oauth2/tests/utils.py index 6c36dee17..85c1dc848 100644 --- a/authentik/providers/oauth2/tests/utils.py +++ b/authentik/providers/oauth2/tests/utils.py @@ -2,12 +2,15 @@ from django.test import TestCase from jwt import decode +from authentik.core.tests.utils import create_test_cert +from authentik.crypto.models import CertificateKeyPair from authentik.providers.oauth2.models import JWTAlgorithms, OAuth2Provider, RefreshToken class OAuthTestCase(TestCase): """OAuth test helpers""" + keypair: CertificateKeyPair required_jwt_keys = [ "exp", "iat", @@ -17,6 +20,11 @@ class OAuthTestCase(TestCase): "iss", ] + @classmethod + def setUpClass(cls) -> None: + cls.keypair = create_test_cert() + super().setUpClass() + def validate_jwt(self, token: RefreshToken, provider: OAuth2Provider): """Validate that all required fields are set""" key, alg = provider.get_jwt_key() diff --git a/authentik/providers/oauth2/views/authorize.py b/authentik/providers/oauth2/views/authorize.py index 634b1abc2..aeeb6d82a 100644 --- a/authentik/providers/oauth2/views/authorize.py +++ b/authentik/providers/oauth2/views/authorize.py @@ -2,7 +2,7 @@ from dataclasses import dataclass, field from datetime import timedelta from re import error as RegexError -from re import escape, fullmatch +from re import fullmatch from typing import Optional from urllib.parse import parse_qs, urlencode, urlparse, urlsplit, urlunsplit from uuid import uuid4 @@ -181,7 +181,7 @@ class OAuthAuthorizationParams: if self.provider.redirect_uris == "": LOGGER.info("Setting redirect for blank redirect_uris", redirect=self.redirect_uri) - self.provider.redirect_uris = escape(self.redirect_uri) + self.provider.redirect_uris = self.redirect_uri self.provider.save() allowed_redirect_urls = self.provider.redirect_uris.split() @@ -194,14 +194,20 @@ class OAuthAuthorizationParams: try: if not any(fullmatch(x, self.redirect_uri) for x in allowed_redirect_urls): LOGGER.warning( - "Invalid redirect uri", + "Invalid redirect uri (regex comparison)", redirect_uri=self.redirect_uri, expected=allowed_redirect_urls, ) raise RedirectUriError(self.redirect_uri, allowed_redirect_urls) except RegexError as exc: - LOGGER.warning("Invalid regular expression configured", exc=exc) - raise RedirectUriError(self.redirect_uri, allowed_redirect_urls) + LOGGER.info("Failed to parse regular expression, checking directly", exc=exc) + if not any(x == self.redirect_uri for x in allowed_redirect_urls): + LOGGER.warning( + "Invalid redirect uri (strict comparison)", + redirect_uri=self.redirect_uri, + expected=allowed_redirect_urls, + ) + raise RedirectUriError(self.redirect_uri, allowed_redirect_urls) if self.request: raise AuthorizeError( self.redirect_uri, "request_not_supported", self.grant_type, self.state diff --git a/authentik/providers/oauth2/views/token.py b/authentik/providers/oauth2/views/token.py index d286da06b..a3c8735e2 100644 --- a/authentik/providers/oauth2/views/token.py +++ b/authentik/providers/oauth2/views/token.py @@ -154,7 +154,7 @@ class TokenParams: try: if not any(fullmatch(x, self.redirect_uri) for x in allowed_redirect_urls): LOGGER.warning( - "Invalid redirect uri", + "Invalid redirect uri (regex comparison)", redirect_uri=self.redirect_uri, expected=allowed_redirect_urls, ) @@ -167,13 +167,19 @@ class TokenParams: ).from_http(request) raise TokenError("invalid_client") except RegexError as exc: - LOGGER.warning("Invalid regular expression configured", exc=exc) - Event.new( - EventAction.CONFIGURATION_ERROR, - message="Invalid redirect_uri RegEx configured", - provider=self.provider, - ).from_http(request) - raise TokenError("invalid_client") + LOGGER.info("Failed to parse regular expression, checking directly", exc=exc) + if not any(x == self.redirect_uri for x in allowed_redirect_urls): + LOGGER.warning( + "Invalid redirect uri (strict comparison)", + redirect_uri=self.redirect_uri, + expected=allowed_redirect_urls, + ) + Event.new( + EventAction.CONFIGURATION_ERROR, + message="Invalid redirect_uri configured", + provider=self.provider, + ).from_http(request) + raise TokenError("invalid_client") try: self.authorization_code = AuthorizationCode.objects.get(code=raw_code) From b33bff92eea942ef60a2f466098c9d21ff6e4de8 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sun, 12 Jun 2022 17:42:23 +0200 Subject: [PATCH 10/23] web/flows: fix error when webauthn operations failed and user retries Signed-off-by: Jens Langhammer --- .../AuthenticatorValidateStageWebAuthn.ts | 17 +++++++++-------- .../WebAuthnAuthenticatorRegisterStage.ts | 17 +++++++++-------- web/src/locales/zh-Hans.po | 8 ++------ 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/web/src/flows/stages/authenticator_validate/AuthenticatorValidateStageWebAuthn.ts b/web/src/flows/stages/authenticator_validate/AuthenticatorValidateStageWebAuthn.ts index 3da5f2fc3..f47cab5c7 100644 --- a/web/src/flows/stages/authenticator_validate/AuthenticatorValidateStageWebAuthn.ts +++ b/web/src/flows/stages/authenticator_validate/AuthenticatorValidateStageWebAuthn.ts @@ -40,6 +40,8 @@ export class AuthenticatorValidateStageWebAuthn extends BaseStage< @property({ type: Boolean }) showBackButton = false; + transformedCredentialRequestOptions?: PublicKeyCredentialRequestOptions; + static get styles(): CSSResult[] { return [ PFBase, @@ -55,19 +57,12 @@ export class AuthenticatorValidateStageWebAuthn extends BaseStage< } async authenticate(): Promise { - // convert certain members of the PublicKeyCredentialRequestOptions into - // byte arrays as expected by the spec. - const credentialRequestOptions = this.deviceChallenge - ?.challenge as PublicKeyCredentialRequestOptions; - const transformedCredentialRequestOptions = - transformCredentialRequestOptions(credentialRequestOptions); - // request the authenticator to create an assertion signature using the // credential private key let assertion; try { assertion = await navigator.credentials.get({ - publicKey: transformedCredentialRequestOptions, + publicKey: this.transformedCredentialRequestOptions, }); if (!assertion) { throw new Error(t`Assertions is empty`); @@ -93,6 +88,12 @@ export class AuthenticatorValidateStageWebAuthn extends BaseStage< } firstUpdated(): void { + // convert certain members of the PublicKeyCredentialRequestOptions into + // byte arrays as expected by the spec. + const credentialRequestOptions = this.deviceChallenge + ?.challenge as PublicKeyCredentialRequestOptions; + this.transformedCredentialRequestOptions = + transformCredentialRequestOptions(credentialRequestOptions); this.authenticateWrapper(); } diff --git a/web/src/flows/stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage.ts b/web/src/flows/stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage.ts index 6ffe9b755..1c25998bd 100644 --- a/web/src/flows/stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage.ts +++ b/web/src/flows/stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage.ts @@ -39,6 +39,8 @@ export class WebAuthnAuthenticatorRegisterStage extends BaseStage< @property() registerMessage = ""; + publicKeyCredentialCreateOptions?: PublicKeyCredentialCreationOptions; + static get styles(): CSSResult[] { return [PFBase, PFLogin, PFFormControl, PFForm, PFTitle, PFButton, AKGlobal]; } @@ -47,18 +49,11 @@ export class WebAuthnAuthenticatorRegisterStage extends BaseStage< if (!this.challenge) { return; } - // convert certain members of the PublicKeyCredentialCreateOptions into - // byte arrays as expected by the spec. - const publicKeyCredentialCreateOptions = transformCredentialCreateOptions( - this.challenge?.registration as PublicKeyCredentialCreationOptions, - this.challenge?.registration.user.id, - ); - // request the authenticator(s) to create a new credential keypair. let credential; try { credential = (await navigator.credentials.create({ - publicKey: publicKeyCredentialCreateOptions, + publicKey: this.publicKeyCredentialCreateOptions, })) as PublicKeyCredential; if (!credential) { throw new Error("Credential is empty"); @@ -98,6 +93,12 @@ export class WebAuthnAuthenticatorRegisterStage extends BaseStage< } firstUpdated(): void { + // convert certain members of the PublicKeyCredentialCreateOptions into + // byte arrays as expected by the spec. + this.publicKeyCredentialCreateOptions = transformCredentialCreateOptions( + this.challenge?.registration as PublicKeyCredentialCreationOptions, + this.challenge?.registration.user.id, + ); this.registerWrapper(); } diff --git a/web/src/locales/zh-Hans.po b/web/src/locales/zh-Hans.po index 76b63e602..6f68b0941 100644 --- a/web/src/locales/zh-Hans.po +++ b/web/src/locales/zh-Hans.po @@ -1538,7 +1538,6 @@ msgstr "删除 {0}" msgid "Deny the user access" msgstr "拒绝用户访问" -#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts msgid "Deprecated. Instead of using this field, configure the JWKS data/URL in Sources." msgstr "已弃用。请在身份来源中配置 JWKS 数据 / URL 代替此字段。" @@ -2448,7 +2447,6 @@ msgstr "隐藏服务账户" #: src/pages/outposts/OutpostForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts -#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/saml/SAMLProviderForm.ts #: src/pages/sources/ldap/LDAPSourceForm.ts @@ -2731,7 +2729,6 @@ msgstr "" #~ msgid "JWT Algorithm" #~ msgstr "JWT 算法" -#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts msgid "JWTs signed by certificates configured here can be used to authenticate to the provider." msgstr "此处配置的证书签名的 JWT 可以用于此提供程序的身份验证。" @@ -2913,7 +2910,6 @@ msgstr "正在加载" #: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts -#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts @@ -6261,8 +6257,8 @@ msgid "Verification Certificate" msgstr "验证证书" #: src/pages/providers/oauth2/OAuth2ProviderForm.ts -msgid "Verification certificates" -msgstr "验证证书" +#~ msgid "Verification certificates" +#~ msgstr "验证证书" #~ msgid "Verify only" #~ msgstr "仅验证" From 440cacbafef694d0f899432e829de71d12a3f4a5 Mon Sep 17 00:00:00 2001 From: Jens L Date: Wed, 15 Jun 2022 20:56:27 +0200 Subject: [PATCH 11/23] webiste/docs: use autogenerated pages and categories (#3102) Signed-off-by: Jens Langhammer --- website/docs/installation/index.md | 7 - .../providers/proxy/{proxy.md => index.md} | 0 website/integrations/index.md | 6 - .../sources/{index.md => general.md} | 3 +- website/sidebars.js | 72 +++++-- website/sidebarsIntegrations.js | 175 ++++++++++++------ 6 files changed, 177 insertions(+), 86 deletions(-) delete mode 100644 website/docs/installation/index.md rename website/docs/providers/proxy/{proxy.md => index.md} (100%) delete mode 100644 website/integrations/index.md rename website/integrations/sources/{index.md => general.md} (96%) diff --git a/website/docs/installation/index.md b/website/docs/installation/index.md deleted file mode 100644 index 289e061a7..000000000 --- a/website/docs/installation/index.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: Installation ---- - -If you want to try out authentik, or only want a small deployment you should use [docker-compose](./docker-compose). - -If you want a larger deployment, or you want High-Availability, you should use [Kubernetes](./kubernetes). diff --git a/website/docs/providers/proxy/proxy.md b/website/docs/providers/proxy/index.md similarity index 100% rename from website/docs/providers/proxy/proxy.md rename to website/docs/providers/proxy/index.md diff --git a/website/integrations/index.md b/website/integrations/index.md deleted file mode 100644 index dbada61c0..000000000 --- a/website/integrations/index.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Integrations -slug: / ---- - -Here you can find a full list of applications that have been documented to work with authentik. If you find any mistake or a step does not work for you, open a GitHub issue [here](https://github.com/goauthentik/authentik/issues/new/choose). diff --git a/website/integrations/sources/index.md b/website/integrations/sources/general.md similarity index 96% rename from website/integrations/sources/index.md rename to website/integrations/sources/general.md index 778bf3799..f42b60433 100644 --- a/website/integrations/sources/index.md +++ b/website/integrations/sources/general.md @@ -1,5 +1,6 @@ --- -title: Overview +title: General +slug: general --- Sources allow you to connect authentik to an existing user directory. They can also be used for social logins, using external providers such as Facebook, Twitter, etc. diff --git a/website/sidebars.js b/website/sidebars.js index cf4e1030a..e693eb78d 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -7,8 +7,14 @@ module.exports = { { type: "category", label: "Installation", + link: { + type: "generated-index", + title: "Installation", + slug: "installation", + description: + "Everything you need to get authentik up and running!", + }, items: [ - "installation/index", "installation/docker-compose", "installation/kubernetes", "installation/beta", @@ -36,17 +42,21 @@ module.exports = { { type: "category", label: "OAuth2 Provider", - items: [ - "providers/oauth2/index", - "providers/oauth2/client_credentials", - ], + link: { + type: "doc", + id: "providers/oauth2/index", + }, + items: ["providers/oauth2/client_credentials"], }, "providers/saml", { type: "category", label: "Proxy Provider", + link: { + type: "doc", + id: "providers/proxy/index", + }, items: [ - "providers/proxy/proxy", "providers/proxy/custom_headers", "providers/proxy/forward_auth", ], @@ -57,8 +67,11 @@ module.exports = { { type: "category", label: "Outposts", + link: { + type: "doc", + id: "outposts/index", + }, items: [ - "outposts/index", "outposts/embedded/embedded", { type: "category", @@ -82,8 +95,11 @@ module.exports = { { type: "category", label: "Flows", + link: { + type: "doc", + id: "flow/index", + }, items: [ - "flow/index", "flow/layouts", "flow/inspector", "flow/examples", @@ -101,6 +117,12 @@ module.exports = { { type: "category", label: "Stages", + link: { + type: "generated-index", + title: "Stages", + slug: "flow/stages", + description: "Overview of all available stages", + }, items: [ "flow/stages/authenticator_duo/index", "flow/stages/authenticator_sms/index", @@ -124,21 +146,29 @@ module.exports = { { type: "category", label: "Policies", - items: ["policies/index", "policies/expression"], + link: { + type: "doc", + id: "policies/index", + }, + items: ["policies/expression"], }, { type: "category", label: "Property Mappings", - items: ["property-mappings/index", "property-mappings/expression"], + link: { + type: "doc", + id: "property-mappings/index", + }, + items: ["property-mappings/expression"], }, { type: "category", label: "Events", - items: [ - "events/index", - "events/notifications", - "events/transports", - ], + link: { + type: "doc", + id: "events/index", + }, + items: ["events/notifications", "events/transports"], }, { type: "category", @@ -164,6 +194,12 @@ module.exports = { { type: "category", label: "Release Notes", + link: { + type: "generated-index", + title: "Releases", + slug: "releases", + description: "Release notes for recent authentik versions", + }, items: [ "releases/v2022.6", "releases/v2022.5", @@ -198,6 +234,12 @@ module.exports = { { type: "category", label: "Troubleshooting", + link: { + type: "generated-index", + title: "Troubleshooting", + slug: "troubleshooting", + description: "Troubleshooting various issues", + }, items: [ "troubleshooting/access", "troubleshooting/emails", diff --git a/website/sidebarsIntegrations.js b/website/sidebarsIntegrations.js index 889d57d81..b3a937a61 100644 --- a/website/sidebarsIntegrations.js +++ b/website/sidebarsIntegrations.js @@ -1,76 +1,137 @@ module.exports = { integrations: [ - { - type: "doc", - id: "index", - }, { type: "category", label: "Applications", + link: { + type: "generated-index", + title: "Applications", + slug: "/", + description: "Applications which integrate with authentik", + }, items: [ - "services/apache-guacamole/index", - "services/aws/index", - "services/awx-tower/index", - "services/bookstack/index", - "services/budibase/index", - "services/fortimanager/index", - "services/gitea/index", - "services/gitlab/index", - "services/grafana/index", - "services/harbor/index", - "services/hashicorp-vault/index", - "services/hedgedoc/index", + { + type: "category", + label: "Monitoring & Infarstructure", + items: [ + { + type: "category", + label: "Hypervisors / Orchestrators", + items: [ + "services/portainer/index", + "services/proxmox-ve/index", + "services/rancher/index", + "services/vmware-vcenter/index", + ], + }, + { + type: "category", + label: "Monitoring", + items: [ + "services/grafana/index", + "services/ubuntu-landscape/index", + "services/uptime-kuma/index", + "services/zabbix/index", + ], + }, + "services/apache-guacamole/index", + "services/awx-tower/index", + "services/fortimanager/index", + "services/harbor/index", + "services/hashicorp-vault/index", + "services/minio/index", + "services/opnsense/index", + "services/pfsense/index", + "services/pgadmin/index", + "services/powerdns-admin/index", + "services/veeam-enterprise-manager/index", + ], + }, + { + type: "category", + label: "Cloud Providers", + items: ["services/aws/index"], + }, + { + type: "category", + label: "Chat, Communication & Collaboration", + items: [ + "services/bookstack/index", + "services/hedgedoc/index", + "services/matrix-synapse/index", + "services/nextcloud/index", + "services/onlyoffice/index", + "services/paperless-ng/index", + "services/rocketchat/index", + "services/roundcube/index", + "services/vikunja/index", + "services/wekan/index", + "services/wiki-js/index", + "services/zulip/index", + ], + }, + { + type: "category", + label: "Platforms", + items: [ + "services/budibase/index", + "services/wordpress/index", + ], + }, + { + type: "category", + label: "Developer tools", + items: [ + "services/gitea/index", + "services/gitlab/index", + "services/sentry/index", + "services/sssd/index", + "services/weblate/index", + ], + }, "services/home-assistant/index", "services/kimai/index", - "services/matrix-synapse/index", - "services/minio/index", - "services/nextcloud/index", - "services/onlyoffice/index", - "services/opnsense/index", - "services/paperless-ng/index", - "services/pfsense/index", - "services/pgadmin/index", - "services/portainer/index", - "services/powerdns-admin/index", - "services/proxmox-ve/index", - "services/rancher/index", - "services/rocketchat/index", - "services/roundcube/index", - "services/sentry/index", "services/sonarr/index", - "services/sssd/index", "services/tautulli/index", - "services/ubuntu-landscape/index", - "services/uptime-kuma/index", - "services/veeam-enterprise-manager/index", - "services/vikunja/index", - "services/vmware-vcenter/index", - "services/weblate/index", - "services/wekan/index", - "services/wiki-js/index", - "services/wordpress/index", - "services/zabbix/index", - "services/zulip/index", ], }, { type: "category", label: "Federation & Social login", + link: { + type: "generated-index", + title: "Sources", + slug: "sources", + description: + "Sources of users which can be federated with authentik", + }, items: [ - "sources/index", - "sources/active-directory/index", - "sources/apple/index", - "sources/azure-ad/index", - "sources/discord/index", - "sources/freeipa/index", - "sources/github/index", - "sources/google/index", - "sources/ldap/index", - "sources/mailcow/index", - "sources/oauth/index", - "sources/plex/index", - "sources/saml/index", - "sources/twitter/index", + "sources/general", + { + type: "category", + label: "Social Logins", + items: [ + "sources/apple/index", + "sources/azure-ad/index", + "sources/discord/index", + "sources/github/index", + "sources/google/index", + "sources/mailcow/index", + "sources/oauth/index", + "sources/plex/index", + "sources/saml/index", + "sources/twitter/index", + ], + }, + { + type: "category", + label: "Directory syncronization", + items: [ + "sources/active-directory/index", + "sources/freeipa/index", + "sources/ldap/index", + ], + }, ], }, ], From 4b7c3c38cd51d087efe7c2161699ac698315cd64 Mon Sep 17 00:00:00 2001 From: Jens L Date: Wed, 15 Jun 2022 21:31:34 +0200 Subject: [PATCH 12/23] website/docs: support levels (#3103) * website/docs: add badges for integration level Signed-off-by: Jens Langhammer * add badge for sources Signed-off-by: Jens Langhammer --- .../docs/writing-documentation.md | 4 +++ website/integrations/_template/service.md | 28 +++++++++++++++++++ .../services/apache-guacamole/index.mdx | 2 ++ website/integrations/services/aws/index.md | 2 ++ .../integrations/services/awx-tower/index.md | 2 ++ .../integrations/services/bookstack/index.md | 2 ++ .../integrations/services/budibase/index.md | 2 ++ .../services/fortimanager/index.md | 2 ++ website/integrations/services/gitea/index.md | 2 ++ website/integrations/services/gitlab/index.md | 2 ++ .../integrations/services/grafana/index.mdx | 2 ++ website/integrations/services/harbor/index.md | 2 ++ .../services/hashicorp-vault/index.md | 2 ++ .../integrations/services/hedgedoc/index.md | 2 ++ .../services/home-assistant/index.md | 2 ++ website/integrations/services/index.mdx | 25 +++++++++++++++++ website/integrations/services/kimai/index.md | 2 ++ .../services/matrix-synapse/index.md | 2 ++ website/integrations/services/minio/index.md | 2 ++ .../integrations/services/nextcloud/index.md | 2 ++ .../integrations/services/onlyoffice/index.md | 2 ++ .../integrations/services/opnsense/index.md | 2 ++ .../services/paperless-ng/index.md | 2 ++ .../integrations/services/pfsense/index.md | 4 ++- .../integrations/services/pgadmin/index.md | 2 ++ .../integrations/services/portainer/index.md | 2 ++ .../services/powerdns-admin/index.md | 2 ++ .../integrations/services/proxmox-ve/index.md | 2 ++ .../integrations/services/rancher/index.md | 2 ++ .../integrations/services/rocketchat/index.md | 2 ++ .../integrations/services/roundcube/index.md | 2 ++ website/integrations/services/sentry/index.md | 2 ++ website/integrations/services/sonarr/index.md | 2 ++ website/integrations/services/sssd/index.md | 2 ++ .../integrations/services/tautulli/index.md | 2 ++ .../services/ubuntu-landscape/index.md | 2 ++ .../services/uptime-kuma/index.md | 4 ++- .../veeam-enterprise-manager/index.md | 2 ++ .../integrations/services/vikunja/index.md | 2 ++ .../services/vmware-vcenter/index.md | 2 ++ .../integrations/services/weblate/index.md | 2 ++ website/integrations/services/wekan/index.mdx | 2 ++ .../integrations/services/wiki-js/index.md | 2 ++ .../integrations/services/wordpress/index.md | 2 ++ website/integrations/services/zabbix/index.md | 2 ++ website/integrations/services/zulip/index.md | 2 ++ .../sources/active-directory/index.md | 2 ++ website/integrations/sources/apple/index.md | 2 ++ .../integrations/sources/azure-ad/index.md | 2 ++ website/integrations/sources/discord/index.md | 2 ++ website/integrations/sources/freeipa/index.md | 2 ++ website/integrations/sources/github/index.md | 2 ++ website/integrations/sources/google/index.md | 2 ++ website/integrations/sources/mailcow/index.md | 2 ++ website/integrations/sources/oauth/index.md | 4 +-- website/integrations/sources/plex/index.md | 2 ++ website/integrations/sources/saml/index.md | 2 -- website/integrations/sources/twitter/index.md | 2 ++ website/sidebarsIntegrations.js | 16 +++++++---- 59 files changed, 176 insertions(+), 13 deletions(-) create mode 100644 website/integrations/_template/service.md create mode 100644 website/integrations/services/index.mdx diff --git a/website/developer-docs/docs/writing-documentation.md b/website/developer-docs/docs/writing-documentation.md index 7334de1d4..12dce5a07 100644 --- a/website/developer-docs/docs/writing-documentation.md +++ b/website/developer-docs/docs/writing-documentation.md @@ -43,6 +43,8 @@ If you find any documentation that doesn't match these guidelines, feel free to These guidelines apply in addition to the ones above. +See the template in `/website/integrations/_template/service.md`. + - For placeholders, use angle brackets (``). Make sure to also define if the placeholder is something the user needs to define, something another system defines, or randomly generated. @@ -52,3 +54,5 @@ These guidelines apply in addition to the ones above. - For placeholder domains, use `authentik.company` and `app-name.company`, where `app-name` is the name of the application you are writing documentation for. - Try to order the documentation in the order that makes it easiest for the user to configure. + +- Make sure to add the service to a fitting category in `/website/sidebarsIntegrations.js` diff --git a/website/integrations/_template/service.md b/website/integrations/_template/service.md new file mode 100644 index 000000000..41abbcce5 --- /dev/null +++ b/website/integrations/_template/service.md @@ -0,0 +1,28 @@ +--- +title: Service Name +--- + +Support level: Community + +## What is Service Name + +From https://service.name + +:::note +Insert a quick overview of what Service Name is and what it does +::: + +## Preparation + +The following placeholders will be used: + +- `service.company` is the FQDN of the Service install. (Remove this for SaaS) +- `authentik.company` is the FQDN of the authentik install. + +## Service Configuration + +Insert Service configuration + +## authentik Configuration + +Insert authentik configuration diff --git a/website/integrations/services/apache-guacamole/index.mdx b/website/integrations/services/apache-guacamole/index.mdx index ab6070389..6b266099c 100644 --- a/website/integrations/services/apache-guacamole/index.mdx +++ b/website/integrations/services/apache-guacamole/index.mdx @@ -2,6 +2,8 @@ title: Apache Guacamole™ --- +Support level: authentik + ## What is Apache Guacamole™ From https://guacamole.apache.org/ diff --git a/website/integrations/services/aws/index.md b/website/integrations/services/aws/index.md index 95ae45bfc..3d5a1daf3 100644 --- a/website/integrations/services/aws/index.md +++ b/website/integrations/services/aws/index.md @@ -2,6 +2,8 @@ title: Amazon Web Services --- +Support level: authentik + ## What is AWS :::note diff --git a/website/integrations/services/awx-tower/index.md b/website/integrations/services/awx-tower/index.md index 62970490f..1ffaa6e7c 100644 --- a/website/integrations/services/awx-tower/index.md +++ b/website/integrations/services/awx-tower/index.md @@ -2,6 +2,8 @@ title: Ansible Tower / AWX --- + + ## What is Tower From https://docs.ansible.com/ansible/2.5/reference_appendices/tower.html diff --git a/website/integrations/services/bookstack/index.md b/website/integrations/services/bookstack/index.md index 4e10b46fb..3717153fc 100644 --- a/website/integrations/services/bookstack/index.md +++ b/website/integrations/services/bookstack/index.md @@ -2,6 +2,8 @@ title: Bookstack --- +Support level: Community + ## What is Bookstack From https://en.wikipedia.org/wiki/BookStack diff --git a/website/integrations/services/budibase/index.md b/website/integrations/services/budibase/index.md index f1334bf3e..052a80c09 100644 --- a/website/integrations/services/budibase/index.md +++ b/website/integrations/services/budibase/index.md @@ -2,6 +2,8 @@ title: Budibase --- +Support level: Community + ## What is Budibase From https://github.com/Budibase/budibase diff --git a/website/integrations/services/fortimanager/index.md b/website/integrations/services/fortimanager/index.md index 14ecb4f23..2fff408c6 100644 --- a/website/integrations/services/fortimanager/index.md +++ b/website/integrations/services/fortimanager/index.md @@ -2,6 +2,8 @@ title: FortiManager --- +Support level: Community + ## What is FortiManager From https://www.fortinet.com/products/management/fortimanager diff --git a/website/integrations/services/gitea/index.md b/website/integrations/services/gitea/index.md index 675c5f722..43ce832b9 100644 --- a/website/integrations/services/gitea/index.md +++ b/website/integrations/services/gitea/index.md @@ -2,6 +2,8 @@ title: Gitea --- +Support level: Community + ## What is Gitea From https://gitea.io/ diff --git a/website/integrations/services/gitlab/index.md b/website/integrations/services/gitlab/index.md index e1dbd15fb..e446d0408 100644 --- a/website/integrations/services/gitlab/index.md +++ b/website/integrations/services/gitlab/index.md @@ -2,6 +2,8 @@ title: GitLab --- +Support level: authentik + ## What is GitLab From https://about.gitlab.com/what-is-gitlab/ diff --git a/website/integrations/services/grafana/index.mdx b/website/integrations/services/grafana/index.mdx index 49c8c512f..6ad16e884 100644 --- a/website/integrations/services/grafana/index.mdx +++ b/website/integrations/services/grafana/index.mdx @@ -2,6 +2,8 @@ title: Grafana --- +Support level: authentik + ## What is Grafana From https://en.wikipedia.org/wiki/Grafana diff --git a/website/integrations/services/harbor/index.md b/website/integrations/services/harbor/index.md index 8a1ce9367..05598ab79 100644 --- a/website/integrations/services/harbor/index.md +++ b/website/integrations/services/harbor/index.md @@ -2,6 +2,8 @@ title: Harbor --- +Support level: Community + ## What is Harbor From https://goharbor.io diff --git a/website/integrations/services/hashicorp-vault/index.md b/website/integrations/services/hashicorp-vault/index.md index 57e55b993..1da157f19 100644 --- a/website/integrations/services/hashicorp-vault/index.md +++ b/website/integrations/services/hashicorp-vault/index.md @@ -2,6 +2,8 @@ title: Hashicorp Vault --- +Support level: authentik + ## What is Vault From https://vaultproject.io diff --git a/website/integrations/services/hedgedoc/index.md b/website/integrations/services/hedgedoc/index.md index 97389afda..dc2d1c2d9 100644 --- a/website/integrations/services/hedgedoc/index.md +++ b/website/integrations/services/hedgedoc/index.md @@ -2,6 +2,8 @@ title: HedgeDoc --- +Support level: Community + ## What is HedgeDoc From https://github.com/hedgedoc/hedgedoc diff --git a/website/integrations/services/home-assistant/index.md b/website/integrations/services/home-assistant/index.md index 32adf5533..11760e4b3 100644 --- a/website/integrations/services/home-assistant/index.md +++ b/website/integrations/services/home-assistant/index.md @@ -2,6 +2,8 @@ title: Home-Assistant --- +Support level: Community + ## What is Home-Assistant From https://www.home-assistant.io/ diff --git a/website/integrations/services/index.mdx b/website/integrations/services/index.mdx new file mode 100644 index 000000000..3f91095cd --- /dev/null +++ b/website/integrations/services/index.mdx @@ -0,0 +1,25 @@ +--- +title: Applications +slug: / +--- + +import DocCardList from "@theme/DocCardList"; +import { useCurrentSidebarCategory } from "@docusaurus/theme-common"; + +Below is a list of all applications that are known to work with authentik. + +All integrations will have a combination of these badges: + +- Support level: Community + + The integration is community maintained. + +- Support level: Vendor + + The integration is supported by the vendor. + +- Support level: authentik + + The integration is regularly tested by the authentik team. + + diff --git a/website/integrations/services/kimai/index.md b/website/integrations/services/kimai/index.md index b63487c3d..57269070c 100644 --- a/website/integrations/services/kimai/index.md +++ b/website/integrations/services/kimai/index.md @@ -2,6 +2,8 @@ title: Kimai --- +Support level: Community + ## What is Kimai From https://www.kimai.org/about/ diff --git a/website/integrations/services/matrix-synapse/index.md b/website/integrations/services/matrix-synapse/index.md index b326a99f2..b07c4a9e4 100644 --- a/website/integrations/services/matrix-synapse/index.md +++ b/website/integrations/services/matrix-synapse/index.md @@ -2,6 +2,8 @@ title: Matrix Synapse --- +Support level: Community + ## What is Matrix Synapse From https://matrix.org/ diff --git a/website/integrations/services/minio/index.md b/website/integrations/services/minio/index.md index 3f3fe58e2..57aba9924 100644 --- a/website/integrations/services/minio/index.md +++ b/website/integrations/services/minio/index.md @@ -2,6 +2,8 @@ title: MinIO --- +Support level: authentik + ## What is MinIO From https://en.wikipedia.org/wiki/MinIO diff --git a/website/integrations/services/nextcloud/index.md b/website/integrations/services/nextcloud/index.md index 1ffbffe09..ad7db0899 100644 --- a/website/integrations/services/nextcloud/index.md +++ b/website/integrations/services/nextcloud/index.md @@ -2,6 +2,8 @@ title: NextCloud --- +Support level: Community + ## What is NextCloud From https://en.wikipedia.org/wiki/Nextcloud diff --git a/website/integrations/services/onlyoffice/index.md b/website/integrations/services/onlyoffice/index.md index 65c74d95f..237f39de7 100644 --- a/website/integrations/services/onlyoffice/index.md +++ b/website/integrations/services/onlyoffice/index.md @@ -2,6 +2,8 @@ title: OnlyOffice --- +Support level: Community + ## What is OnlyOffice From https://en.wikipedia.org/wiki/OnlyOffice diff --git a/website/integrations/services/opnsense/index.md b/website/integrations/services/opnsense/index.md index 2af0f2b52..4af5f8a0a 100644 --- a/website/integrations/services/opnsense/index.md +++ b/website/integrations/services/opnsense/index.md @@ -2,6 +2,8 @@ title: OPNsense --- +Support level: Community + ## What is OPNsense From https://opnsense.org/ diff --git a/website/integrations/services/paperless-ng/index.md b/website/integrations/services/paperless-ng/index.md index ec6762259..af2c81a1f 100644 --- a/website/integrations/services/paperless-ng/index.md +++ b/website/integrations/services/paperless-ng/index.md @@ -2,6 +2,8 @@ title: Paperless-ng --- +Support level: Community + ## What is Paperless-ng Modified from https://github.com/jonaswinkler/paperless-ng diff --git a/website/integrations/services/pfsense/index.md b/website/integrations/services/pfsense/index.md index 3b4f0d7c6..d1719c4cf 100644 --- a/website/integrations/services/pfsense/index.md +++ b/website/integrations/services/pfsense/index.md @@ -2,6 +2,8 @@ title: pfSense --- +Support level: Community + ## What is pfSense From https://www.pfsense.org/ @@ -58,7 +60,7 @@ In authentik, create an outpost (under _Applications/Outposts_) of type `LDAP` t ## pfSense unsecure setup (without SSL) :::warning -This setup should only be used for testing purpose, because passwords will be sent in clear text to authentik. +This setup should only be used for testing purpose, because passwords will be sent in clear text to authentik. ::: Add your authentik LDAP server to pfSense by going to your pfSense Web UI and clicking the `+ Add` under _System/User Manager/Authentication Servers_. diff --git a/website/integrations/services/pgadmin/index.md b/website/integrations/services/pgadmin/index.md index 6596cc68e..e8a6f4433 100644 --- a/website/integrations/services/pgadmin/index.md +++ b/website/integrations/services/pgadmin/index.md @@ -2,6 +2,8 @@ title: pgAdmin --- +Support level: Community + ## What is pgAdmin From https://www.pgadmin.org/ diff --git a/website/integrations/services/portainer/index.md b/website/integrations/services/portainer/index.md index 7eeb02217..2fdc3932c 100644 --- a/website/integrations/services/portainer/index.md +++ b/website/integrations/services/portainer/index.md @@ -2,6 +2,8 @@ title: Portainer --- +Support level: Community + ## What is Portainer From https://www.portainer.io/ diff --git a/website/integrations/services/powerdns-admin/index.md b/website/integrations/services/powerdns-admin/index.md index edfb8f801..b5d90507b 100644 --- a/website/integrations/services/powerdns-admin/index.md +++ b/website/integrations/services/powerdns-admin/index.md @@ -2,6 +2,8 @@ title: PowerDNS-Admin --- +Support level: Community + ## What is PowerDNS-Admin From https://github.com/ngoduykhanh/PowerDNS-Admin diff --git a/website/integrations/services/proxmox-ve/index.md b/website/integrations/services/proxmox-ve/index.md index 745f19632..5eb0ad6c5 100644 --- a/website/integrations/services/proxmox-ve/index.md +++ b/website/integrations/services/proxmox-ve/index.md @@ -2,6 +2,8 @@ title: Proxmox VE --- +Support level: Community + ## What is Proxmox VE From https://pve.proxmox.com/wiki/Main_Page diff --git a/website/integrations/services/rancher/index.md b/website/integrations/services/rancher/index.md index 5f8650241..481f3a139 100644 --- a/website/integrations/services/rancher/index.md +++ b/website/integrations/services/rancher/index.md @@ -2,6 +2,8 @@ title: Rancher --- +Support level: authentik + ## What is Rancher From https://rancher.com/products/rancher diff --git a/website/integrations/services/rocketchat/index.md b/website/integrations/services/rocketchat/index.md index ec267649b..8c692af6f 100644 --- a/website/integrations/services/rocketchat/index.md +++ b/website/integrations/services/rocketchat/index.md @@ -2,6 +2,8 @@ title: Rocket.chat --- +Support level: Community + ## What is Rocket.chat From https://github.com/RocketChat/Rocket.Chat diff --git a/website/integrations/services/roundcube/index.md b/website/integrations/services/roundcube/index.md index 10ab6471e..72a1fc294 100644 --- a/website/integrations/services/roundcube/index.md +++ b/website/integrations/services/roundcube/index.md @@ -2,6 +2,8 @@ title: Roundcube --- +Support level: Community + ## What is Roundcube From https://roundcube.net diff --git a/website/integrations/services/sentry/index.md b/website/integrations/services/sentry/index.md index b0484514d..25f9e8663 100644 --- a/website/integrations/services/sentry/index.md +++ b/website/integrations/services/sentry/index.md @@ -2,6 +2,8 @@ title: Sentry --- +Support level: authentik + ## What is Sentry From https://sentry.io diff --git a/website/integrations/services/sonarr/index.md b/website/integrations/services/sonarr/index.md index 212bd5a9b..d85b5e4f3 100644 --- a/website/integrations/services/sonarr/index.md +++ b/website/integrations/services/sonarr/index.md @@ -2,6 +2,8 @@ title: Sonarr --- +Support level: Community + :::note These instructions apply to all projects in the \*arr Family. If you use multiple of these projects, you can assign them to the same Outpost. ::: diff --git a/website/integrations/services/sssd/index.md b/website/integrations/services/sssd/index.md index 90d053bb0..631132c61 100644 --- a/website/integrations/services/sssd/index.md +++ b/website/integrations/services/sssd/index.md @@ -2,6 +2,8 @@ title: sssd --- +Support level: Community + ## What is sssd From https://sssd.io/ diff --git a/website/integrations/services/tautulli/index.md b/website/integrations/services/tautulli/index.md index 556175145..c9405ddd9 100644 --- a/website/integrations/services/tautulli/index.md +++ b/website/integrations/services/tautulli/index.md @@ -2,6 +2,8 @@ title: Tautulli --- +Support level: Community + ## What is Tautulli From https://tautulli.com/ diff --git a/website/integrations/services/ubuntu-landscape/index.md b/website/integrations/services/ubuntu-landscape/index.md index 42f789356..b9590de9c 100644 --- a/website/integrations/services/ubuntu-landscape/index.md +++ b/website/integrations/services/ubuntu-landscape/index.md @@ -2,6 +2,8 @@ title: Ubuntu Landscape --- +Support level: Community + ## What is Ubuntu Landscape From https://en.wikipedia.org/wiki/Landscape_(software) diff --git a/website/integrations/services/uptime-kuma/index.md b/website/integrations/services/uptime-kuma/index.md index 7a8424bfc..0d5a725ba 100644 --- a/website/integrations/services/uptime-kuma/index.md +++ b/website/integrations/services/uptime-kuma/index.md @@ -2,6 +2,8 @@ title: Uptime Kuma --- +Support level: Community + ## What is Uptime Kuma From https://github.com/louislam/uptime-kuma @@ -29,7 +31,7 @@ Create an application in authentik. Create a Proxy provider with the following p - External host - `https://uptime-kuma.company` + `https://uptime-kuma.company` Set this to the external URL you will be accessing Uptime Kuma from. - Skip path regex diff --git a/website/integrations/services/veeam-enterprise-manager/index.md b/website/integrations/services/veeam-enterprise-manager/index.md index f709dc03b..bb0663b53 100644 --- a/website/integrations/services/veeam-enterprise-manager/index.md +++ b/website/integrations/services/veeam-enterprise-manager/index.md @@ -2,6 +2,8 @@ title: Veeam Enterprise Manager --- +Support level: Community + ## What is Veeam Enterprise Manager From https://helpcenter.veeam.com/docs/backup/em/introduction.html?ver=100 diff --git a/website/integrations/services/vikunja/index.md b/website/integrations/services/vikunja/index.md index 3bdd9138c..4662bd8e5 100644 --- a/website/integrations/services/vikunja/index.md +++ b/website/integrations/services/vikunja/index.md @@ -2,6 +2,8 @@ title: Vikunja --- +Support level: Community + ## What is Vikunja From https://vikunja.io/ diff --git a/website/integrations/services/vmware-vcenter/index.md b/website/integrations/services/vmware-vcenter/index.md index 5faeb3767..87969f4c5 100644 --- a/website/integrations/services/vmware-vcenter/index.md +++ b/website/integrations/services/vmware-vcenter/index.md @@ -2,6 +2,8 @@ title: VMware vCenter --- +Support level: Community + ## What is vCenter From https://en.wikipedia.org/wiki/VCenter diff --git a/website/integrations/services/weblate/index.md b/website/integrations/services/weblate/index.md index baebb805e..8dde0b033 100644 --- a/website/integrations/services/weblate/index.md +++ b/website/integrations/services/weblate/index.md @@ -2,6 +2,8 @@ title: Weblate --- +Support level: Community + ## What is Weblate From https://weblate.org/en/ diff --git a/website/integrations/services/wekan/index.mdx b/website/integrations/services/wekan/index.mdx index 9b2f7f3d9..37914950d 100644 --- a/website/integrations/services/wekan/index.mdx +++ b/website/integrations/services/wekan/index.mdx @@ -2,6 +2,8 @@ title: Wekan --- +Support level: Community + ## What is Wekan From https://github.com/wekan/wekan/wiki diff --git a/website/integrations/services/wiki-js/index.md b/website/integrations/services/wiki-js/index.md index a26325015..0ea884b63 100644 --- a/website/integrations/services/wiki-js/index.md +++ b/website/integrations/services/wiki-js/index.md @@ -2,6 +2,8 @@ title: Wiki.js --- +Support level: Community + ## What is Wiki.js From https://en.wikipedia.org/wiki/Wiki.js diff --git a/website/integrations/services/wordpress/index.md b/website/integrations/services/wordpress/index.md index 2131dcc3c..e24cebe9a 100644 --- a/website/integrations/services/wordpress/index.md +++ b/website/integrations/services/wordpress/index.md @@ -2,6 +2,8 @@ title: Wordpress --- +Support level: Community + ## What is Wordpress From https://en.wikipedia.org/wiki/WordPress diff --git a/website/integrations/services/zabbix/index.md b/website/integrations/services/zabbix/index.md index f4d16f994..98b7d9937 100644 --- a/website/integrations/services/zabbix/index.md +++ b/website/integrations/services/zabbix/index.md @@ -2,6 +2,8 @@ title: Zabbix --- +Support level: Community + ## What is Zabbix From https://www.zabbix.com/features diff --git a/website/integrations/services/zulip/index.md b/website/integrations/services/zulip/index.md index 3c9e27da4..603ae3c9e 100644 --- a/website/integrations/services/zulip/index.md +++ b/website/integrations/services/zulip/index.md @@ -2,6 +2,8 @@ title: Zulip --- +Support level: Community + ## What is Zulip From https://zulip.com diff --git a/website/integrations/sources/active-directory/index.md b/website/integrations/sources/active-directory/index.md index a590eab5f..b9da89333 100644 --- a/website/integrations/sources/active-directory/index.md +++ b/website/integrations/sources/active-directory/index.md @@ -2,6 +2,8 @@ title: Active Directory --- +Support level: Community + ## Preparation The following placeholders will be used: diff --git a/website/integrations/sources/apple/index.md b/website/integrations/sources/apple/index.md index a156f14ab..05853ed7a 100644 --- a/website/integrations/sources/apple/index.md +++ b/website/integrations/sources/apple/index.md @@ -2,6 +2,8 @@ title: Apple --- +Support level: authentik + Allows users to authenticate using their Apple ID. ## Preparation diff --git a/website/integrations/sources/azure-ad/index.md b/website/integrations/sources/azure-ad/index.md index a6dda53c6..8e9b01910 100644 --- a/website/integrations/sources/azure-ad/index.md +++ b/website/integrations/sources/azure-ad/index.md @@ -2,6 +2,8 @@ title: Azure AD --- +Support level: Community + ## Preparation The following placeholders will be used: diff --git a/website/integrations/sources/discord/index.md b/website/integrations/sources/discord/index.md index 296fe9fdc..54d7fe393 100644 --- a/website/integrations/sources/discord/index.md +++ b/website/integrations/sources/discord/index.md @@ -2,6 +2,8 @@ title: Discord --- +Support level: authentik + Allows users to authenticate using their Discord credentials ## Preparation diff --git a/website/integrations/sources/freeipa/index.md b/website/integrations/sources/freeipa/index.md index 434e1d3e9..1805113b7 100644 --- a/website/integrations/sources/freeipa/index.md +++ b/website/integrations/sources/freeipa/index.md @@ -2,6 +2,8 @@ title: FreeIPA --- +Support level: Community + ## Preparation The following placeholders will be used: diff --git a/website/integrations/sources/github/index.md b/website/integrations/sources/github/index.md index 80d8f8085..2e2e21a31 100644 --- a/website/integrations/sources/github/index.md +++ b/website/integrations/sources/github/index.md @@ -2,6 +2,8 @@ title: Github --- +Support level: authentik + Allows users to authenticate using their Github credentials ## Preparation diff --git a/website/integrations/sources/google/index.md b/website/integrations/sources/google/index.md index 79286697b..ecb88537e 100644 --- a/website/integrations/sources/google/index.md +++ b/website/integrations/sources/google/index.md @@ -2,6 +2,8 @@ title: Google --- +Support level: Community + Allows users to authenticate using their Google credentials ## Preparation diff --git a/website/integrations/sources/mailcow/index.md b/website/integrations/sources/mailcow/index.md index 378901127..8e3d537d1 100644 --- a/website/integrations/sources/mailcow/index.md +++ b/website/integrations/sources/mailcow/index.md @@ -2,6 +2,8 @@ title: Mailcow --- +Support level: Community + Allows users to authenticate using their Mailcow credentials ## Preparation diff --git a/website/integrations/sources/oauth/index.md b/website/integrations/sources/oauth/index.md index 56e7c8df6..1909ecda8 100644 --- a/website/integrations/sources/oauth/index.md +++ b/website/integrations/sources/oauth/index.md @@ -1,9 +1,7 @@ --- -title: Generic OAuth Source +title: OAuth --- -## Generic OAuth Source - :::note All Integration-specific Sources are documented in the Integrations Section ::: diff --git a/website/integrations/sources/plex/index.md b/website/integrations/sources/plex/index.md index 608215251..cd72456f2 100644 --- a/website/integrations/sources/plex/index.md +++ b/website/integrations/sources/plex/index.md @@ -2,6 +2,8 @@ title: Plex --- +Support level: Community + Allows users to authenticate using their Plex credentials ## Preparation diff --git a/website/integrations/sources/saml/index.md b/website/integrations/sources/saml/index.md index eb7798101..7bc945345 100644 --- a/website/integrations/sources/saml/index.md +++ b/website/integrations/sources/saml/index.md @@ -2,8 +2,6 @@ title: SAML --- -## SAML Source - This source allows authentik to act as a SAML Service Provider. Just like the SAML Provider, it supports signed requests. Vendor-specific documentation can be found in the Integrations Section. ## Terminology diff --git a/website/integrations/sources/twitter/index.md b/website/integrations/sources/twitter/index.md index 25aeb7d8e..5cac37d0d 100644 --- a/website/integrations/sources/twitter/index.md +++ b/website/integrations/sources/twitter/index.md @@ -2,6 +2,8 @@ title: Twitter --- +Support level: authentik + Allows users to authenticate using their twitter credentials ## Preparation diff --git a/website/sidebarsIntegrations.js b/website/sidebarsIntegrations.js index b3a937a61..3eebfc81a 100644 --- a/website/sidebarsIntegrations.js +++ b/website/sidebarsIntegrations.js @@ -4,10 +4,8 @@ module.exports = { type: "category", label: "Applications", link: { - type: "generated-index", - title: "Applications", - slug: "/", - description: "Applications which integrate with authentik", + type: "doc", + id: "services/index", }, items: [ { @@ -117,9 +115,7 @@ module.exports = { "sources/github/index", "sources/google/index", "sources/mailcow/index", - "sources/oauth/index", "sources/plex/index", - "sources/saml/index", "sources/twitter/index", ], }, @@ -129,7 +125,15 @@ module.exports = { items: [ "sources/active-directory/index", "sources/freeipa/index", + ], + }, + { + type: "category", + label: "Protocols", + items: [ "sources/ldap/index", + "sources/oauth/index", + "sources/saml/index", ], }, ], From 1d10afa2097f579f5e4680c69496e938c2b9a8bc Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Wed, 15 Jun 2022 21:43:44 +0200 Subject: [PATCH 13/23] website/docs: add version dropdown for subdomains Signed-off-by: Jens Langhammer --- website/docs/releases/v0.10.md | 2 +- website/docusaurus.config.js | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/website/docs/releases/v0.10.md b/website/docs/releases/v0.10.md index 191b48395..98e52edb8 100644 --- a/website/docs/releases/v0.10.md +++ b/website/docs/releases/v0.10.md @@ -13,7 +13,7 @@ This update brings a lot of big features, such as: Due to this new OAuth2 Provider, the Application Gateway Provider, now simply called "Proxy Provider" has been revamped as well. The new authentik Proxy integrates more tightly with authentik via the new Outposts system. The new proxy also supports multiple applications per proxy instance, can configure TLS based on authentik Keypairs, and more. - See [Proxy](../providers/proxy/proxy.md) + See [Proxy](../providers/proxy/index.md) - Outpost System diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 94c8ad397..9da48f732 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -1,3 +1,10 @@ +const sidebar = require("./sidebars.js"); + +const releases = sidebar.docs + .filter((doc) => doc.link?.slug === "releases")[0] + .items.filter((release) => typeof release === "string"); +const latestVersion = releases[0].replace("releases/v", ""); + module.exports = { title: "authentik", tagline: "Making authentication simple.", @@ -39,6 +46,20 @@ module.exports = { label: "API", position: "left", }, + { + type: "dropdown", + label: `Version ${latestVersion}`, + position: "right", + items: releases.map((release) => { + const subdomain = release + .replace("releases/v", "") + .replace(".", "-"); + return { + label: release.replace("releases/", ""), + href: `https://version-${subdomain}.goauthentik.io`, + }; + }), + }, { href: "https://github.com/goauthentik/authentik", label: "GitHub", From 2a6fccd22a02a854b8527d156af9dc66309db704 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Thu, 16 Jun 2022 10:32:32 +0200 Subject: [PATCH 14/23] providers/proxy: only send misconfiguration event once Signed-off-by: Jens Langhammer --- internal/outpost/proxyv2/application/mode_common.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/internal/outpost/proxyv2/application/mode_common.go b/internal/outpost/proxyv2/application/mode_common.go index 9f24a192e..c5a95c524 100644 --- a/internal/outpost/proxyv2/application/mode_common.go +++ b/internal/outpost/proxyv2/application/mode_common.go @@ -13,6 +13,8 @@ import ( "goauthentik.io/internal/constants" ) +var hasReportedMisconfiguration = false + func (a *Application) addHeaders(headers http.Header, c *Claims) { // https://goauthentik.io/docs/providers/proxy/proxy @@ -103,6 +105,9 @@ func (a *Application) getNginxForwardUrl(r *http.Request) (*url.URL, error) { func (a *Application) ReportMisconfiguration(r *http.Request, msg string, fields map[string]interface{}) { fields["message"] = msg a.log.WithFields(fields).Error("Reporting configuration error") + if hasReportedMisconfiguration { + return + } req := api.EventRequest{ Action: api.EVENTACTIONS_CONFIGURATION_ERROR, App: "authentik.providers.proxy", // must match python apps.py name @@ -112,6 +117,8 @@ func (a *Application) ReportMisconfiguration(r *http.Request, msg string, fields _, _, err := a.ak.Client.EventsApi.EventsEventsCreate(context.Background()).EventRequest(req).Execute() if err != nil { a.log.WithError(err).Warning("failed to report configuration error") + } else { + hasReportedMisconfiguration = true } } From 49142fa80b2bf4ea8d2563080be8cbb5c7b6b189 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Thu, 16 Jun 2022 11:32:21 +0200 Subject: [PATCH 15/23] internal: dont sample gunicorn proxied requests Signed-off-by: Jens Langhammer --- internal/web/proxy.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/internal/web/proxy.go b/internal/web/proxy.go index d0ce80a74..c383f397d 100644 --- a/internal/web/proxy.go +++ b/internal/web/proxy.go @@ -9,6 +9,7 @@ import ( "time" "github.com/prometheus/client_golang/prometheus" + "goauthentik.io/internal/utils/sentry" "goauthentik.io/internal/utils/web" ) @@ -30,6 +31,8 @@ func (ws *WebServer) configureProxy() { rp := &httputil.ReverseProxy{Director: director} rp.ErrorHandler = ws.proxyErrorHandler rp.ModifyResponse = ws.proxyModifyResponse + proxyMux := ws.m.NewRoute().Subrouter() + proxyMux.Use(sentry.SentryNoSampleMiddleware) ws.m.PathPrefix("/outpost.goauthentik.io").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { if ws.ProxyServer != nil { before := time.Now() @@ -41,10 +44,10 @@ func (ws *WebServer) configureProxy() { } ws.proxyErrorHandler(rw, r, fmt.Errorf("proxy not running")) }) - ws.m.Path("/-/health/live/").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + proxyMux.Path("/-/health/live/").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { rw.WriteHeader(204) }) - ws.m.PathPrefix("/").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + proxyMux.PathPrefix("/").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { if !ws.p.IsRunning() { ws.proxyErrorHandler(rw, r, fmt.Errorf("authentik core not running yet")) return From 8faa909c32baae0f0aa16bf37447b7fd16d41e99 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Thu, 16 Jun 2022 17:05:27 +0200 Subject: [PATCH 16/23] internal: fix routing to embedded outpost Signed-off-by: Jens Langhammer --- internal/web/proxy.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/internal/web/proxy.go b/internal/web/proxy.go index c383f397d..24364e2e4 100644 --- a/internal/web/proxy.go +++ b/internal/web/proxy.go @@ -31,8 +31,6 @@ func (ws *WebServer) configureProxy() { rp := &httputil.ReverseProxy{Director: director} rp.ErrorHandler = ws.proxyErrorHandler rp.ModifyResponse = ws.proxyModifyResponse - proxyMux := ws.m.NewRoute().Subrouter() - proxyMux.Use(sentry.SentryNoSampleMiddleware) ws.m.PathPrefix("/outpost.goauthentik.io").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { if ws.ProxyServer != nil { before := time.Now() @@ -44,10 +42,10 @@ func (ws *WebServer) configureProxy() { } ws.proxyErrorHandler(rw, r, fmt.Errorf("proxy not running")) }) - proxyMux.Path("/-/health/live/").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + ws.m.Path("/-/health/live/").HandlerFunc(sentry.SentryNoSample(func(rw http.ResponseWriter, r *http.Request) { rw.WriteHeader(204) - }) - proxyMux.PathPrefix("/").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + })) + ws.m.PathPrefix("/").HandlerFunc(sentry.SentryNoSample(func(rw http.ResponseWriter, r *http.Request) { if !ws.p.IsRunning() { ws.proxyErrorHandler(rw, r, fmt.Errorf("authentik core not running yet")) return @@ -66,7 +64,7 @@ func (ws *WebServer) configureProxy() { }).Observe(float64(time.Since(before))) ws.log.WithField("host", web.GetHost(r)).Trace("routing to application server") rp.ServeHTTP(rw, r) - }) + })) } func (ws *WebServer) proxyErrorHandler(rw http.ResponseWriter, req *http.Request, err error) { From 91f91b08e518ca0f47e45ce051ae3f4c08342d37 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Fri, 17 Jun 2022 10:09:56 +0200 Subject: [PATCH 17/23] core: fix migrations when creating bootstrap token Signed-off-by: Jens Langhammer --- ...8_auto_20210330_1345_squashed_0028_alter_token_intent.py | 6 ++++-- authentik/core/migrations/0027_bootstrap_token.py | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/authentik/core/migrations/0018_auto_20210330_1345_squashed_0028_alter_token_intent.py b/authentik/core/migrations/0018_auto_20210330_1345_squashed_0028_alter_token_intent.py index 9909c54df..c24c37858 100644 --- a/authentik/core/migrations/0018_auto_20210330_1345_squashed_0028_alter_token_intent.py +++ b/authentik/core/migrations/0018_auto_20210330_1345_squashed_0028_alter_token_intent.py @@ -36,8 +36,10 @@ def fix_duplicates(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): def create_default_user_token(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): - # We have to use a direct import here, otherwise we get an object manager error - from authentik.core.models import Token, TokenIntents, User + from authentik.core.models import TokenIntents + + User = apps.get_model("authentik_core", "User") + Token = apps.get_model("authentik_core", "Token") db_alias = schema_editor.connection.alias diff --git a/authentik/core/migrations/0027_bootstrap_token.py b/authentik/core/migrations/0027_bootstrap_token.py index c5479f382..3b7054993 100644 --- a/authentik/core/migrations/0027_bootstrap_token.py +++ b/authentik/core/migrations/0027_bootstrap_token.py @@ -7,8 +7,10 @@ from django.db.backends.base.schema import BaseDatabaseSchemaEditor def create_default_user_token(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): - # We have to use a direct import here, otherwise we get an object manager error - from authentik.core.models import Token, TokenIntents, User + from authentik.core.models import TokenIntents + + User = apps.get_model("authentik_core", "User") + Token = apps.get_model("authentik_core", "Token") db_alias = schema_editor.connection.alias From f0c72e85367524fc1704da3841107029c007e145 Mon Sep 17 00:00:00 2001 From: 9p4 <17993169+9p4@users.noreply.github.com> Date: Sat, 18 Jun 2022 16:38:15 +0530 Subject: [PATCH 18/23] providers/oauth2: dont lowercase URL for token requests (#3114) this was a leftover from before the migration regex checking for redirect URIs closes #3076 and #3083 --- authentik/providers/oauth2/views/token.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authentik/providers/oauth2/views/token.py b/authentik/providers/oauth2/views/token.py index a3c8735e2..2964b0a4c 100644 --- a/authentik/providers/oauth2/views/token.py +++ b/authentik/providers/oauth2/views/token.py @@ -89,7 +89,7 @@ class TokenParams: provider=provider, client_id=client_id, client_secret=client_secret, - redirect_uri=request.POST.get("redirect_uri", "").lower(), + redirect_uri=request.POST.get("redirect_uri", ""), grant_type=request.POST.get("grant_type", ""), state=request.POST.get("state", ""), scope=request.POST.get("scope", "").split(), From 1faba11a573adb07bf08341b001549ed9804d299 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sat, 18 Jun 2022 13:13:36 +0200 Subject: [PATCH 19/23] providers/oauth2: add test to ensure capitalised redirect_uri isn't changed Signed-off-by: Jens Langhammer #3114 --- authentik/providers/oauth2/tests/test_token.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/authentik/providers/oauth2/tests/test_token.py b/authentik/providers/oauth2/tests/test_token.py index 0da18abcc..203fd7686 100644 --- a/authentik/providers/oauth2/tests/test_token.py +++ b/authentik/providers/oauth2/tests/test_token.py @@ -33,7 +33,7 @@ class TestToken(OAuthTestCase): client_id=generate_id(), client_secret=generate_key(), authorization_flow=create_test_flow(), - redirect_uris="http://testserver", + redirect_uris="http://TestServer", signing_key=self.keypair, ) header = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode() @@ -44,7 +44,7 @@ class TestToken(OAuthTestCase): data={ "grant_type": GRANT_TYPE_AUTHORIZATION_CODE, "code": code.code, - "redirect_uri": "http://testserver", + "redirect_uri": "http://TestServer", }, HTTP_AUTHORIZATION=f"Basic {header}", ) From c824af5bc31da92f7223d1fa1c283b5cc139f047 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sat, 18 Jun 2022 13:36:18 +0200 Subject: [PATCH 20/23] web/elements: add spinner when loading dynamic routes Signed-off-by: Jens Langhammer --- web/src/elements/router/Route.ts | 7 ++++++- web/src/elements/router/RouterOutlet.ts | 9 ++++----- web/src/interfaces/UserInterface.ts | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/web/src/elements/router/Route.ts b/web/src/elements/router/Route.ts index 7aab1c69f..cfc332f64 100644 --- a/web/src/elements/router/Route.ts +++ b/web/src/elements/router/Route.ts @@ -1,6 +1,8 @@ import { TemplateResult, html } from "lit"; import { until } from "lit/directives/until.js"; +import "../EmptyState"; + export const SLUG_REGEX = "[-a-zA-Z0-9_]+"; export const ID_REGEX = "\\d+"; export const UUID_REGEX = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"; @@ -47,7 +49,10 @@ export class Route { render(args: RouteArgs): TemplateResult { if (this.callback) { - return html`${until(this.callback(args))}`; + return html`${until( + this.callback(args), + html``, + )}`; } if (this.element) { return this.element; diff --git a/web/src/elements/router/RouterOutlet.ts b/web/src/elements/router/RouterOutlet.ts index 25f01f0f4..1e9f61517 100644 --- a/web/src/elements/router/RouterOutlet.ts +++ b/web/src/elements/router/RouterOutlet.ts @@ -103,12 +103,11 @@ export class RouterOutlet extends LitElement { }); if (!matchedRoute) { console.debug(`authentik/router: route "${activeUrl}" not defined`); - const route = new Route( - RegExp(""), - html`
+ const route = new Route(RegExp(""), async () => { + return html`
-
`, - ); +
`; + }); matchedRoute = new RouteMatch(route); matchedRoute.arguments = route.url.exec(activeUrl)?.groups || {}; matchedRoute.fullUrl = activeUrl; diff --git a/web/src/interfaces/UserInterface.ts b/web/src/interfaces/UserInterface.ts index bd0b8d583..5f682c447 100644 --- a/web/src/interfaces/UserInterface.ts +++ b/web/src/interfaces/UserInterface.ts @@ -53,7 +53,7 @@ export class UserInterface extends LitElement { tenant: CurrentTenant = DefaultTenant; @property({ type: Number }) - notificationsCount = -1; + notificationsCount = 0; static get styles(): CSSResult[] { return [ From db557401aa25d5fd9b657fb977caac070781287c Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sun, 19 Jun 2022 21:22:47 +0200 Subject: [PATCH 21/23] web/admin: lint bound group under policies Signed-off-by: Jens Langhammer --- web/src/pages/policies/BoundPoliciesList.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/src/pages/policies/BoundPoliciesList.ts b/web/src/pages/policies/BoundPoliciesList.ts index 92656bfcb..ec2a03af9 100644 --- a/web/src/pages/policies/BoundPoliciesList.ts +++ b/web/src/pages/policies/BoundPoliciesList.ts @@ -67,6 +67,9 @@ export class BoundPoliciesList extends Table { if (item.user) { return html` ${label} `; } + if (item.group) { + return html` ${label} `; + } return html`${label}`; } From 5385feb428fe3aab62db1dde3bdf1d97b5d5a6c6 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sun, 19 Jun 2022 21:41:22 +0200 Subject: [PATCH 22/23] website/docs: add 2022.6.3 release notes Signed-off-by: Jens Langhammer --- website/docs/releases/v2022.6.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/website/docs/releases/v2022.6.md b/website/docs/releases/v2022.6.md index 1850ace8c..cd3f0d45b 100644 --- a/website/docs/releases/v2022.6.md +++ b/website/docs/releases/v2022.6.md @@ -59,6 +59,27 @@ slug: "2022.6" - stages/authenticator_validate: fix error in passwordless webauthn - web/elements: add error handler when table fails to fetch objects +## Fixed in 2022.6.3 + +- core: fix migrations when creating bootstrap token +- internal: dont sample gunicorn proxied requests +- internal: fix routing to embedded outpost +- internal: skip tracing for go healthcheck and metrics endpoints +- lifecycle: run bootstrap tasks inline when using automated install +- policies: consolidate log user and application +- providers/oauth2: add test to ensure capitalised redirect_uri isn't changed +- providers/oauth2: dont lowercase URL for token requests (#3114) +- providers/oauth2: if a redirect_uri cannot be parsed as regex, compare strict (#3070) +- providers/proxy: only send misconfiguration event once +- root: ignore healthcheck routes in sentry tracing +- stages/authenticator_validate: add webauthn tests (#3069) +- web/admin: lint bound group under policies +- web/admin: remove invalid requirement for usernames +- web/elements: add spinner when loading dynamic routes +- web/flows: add divider to identification stage for security key +- web/flows: fix error when webauthn operations failed and user retries +- web/flows: remove autofocus from password field of identifications tage + ## Upgrading This release does not introduce any new requirements. From 9201fc1834699c468f11720b8828835d3cc54127 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sun, 19 Jun 2022 22:01:06 +0200 Subject: [PATCH 23/23] release: 2022.6.3 --- .bumpversion.cfg | 2 +- .github/workflows/release-publish.yml | 10 +++++----- authentik/__init__.py | 2 +- docker-compose.yml | 4 ++-- internal/constants/constants.go | 2 +- pyproject.toml | 2 +- schema.yml | 2 +- web/src/constants.ts | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index d89e6172a..c5ebff303 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 2022.6.2 +current_version = 2022.6.3 tag = True commit = True parse = (?P\d+)\.(?P\d+)\.(?P\d+)\-?(?P.*) diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index e26acad46..73ac030fa 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -30,9 +30,9 @@ jobs: with: push: ${{ github.event_name == 'release' }} tags: | - beryju/authentik:2022.6.2, + beryju/authentik:2022.6.3, beryju/authentik:latest, - ghcr.io/goauthentik/server:2022.6.2, + ghcr.io/goauthentik/server:2022.6.3, ghcr.io/goauthentik/server:latest platforms: linux/amd64,linux/arm64 context: . @@ -69,9 +69,9 @@ jobs: with: push: ${{ github.event_name == 'release' }} tags: | - beryju/authentik-${{ matrix.type }}:2022.6.2, + beryju/authentik-${{ matrix.type }}:2022.6.3, beryju/authentik-${{ matrix.type }}:latest, - ghcr.io/goauthentik/${{ matrix.type }}:2022.6.2, + ghcr.io/goauthentik/${{ matrix.type }}:2022.6.3, ghcr.io/goauthentik/${{ matrix.type }}:latest file: ${{ matrix.type }}.Dockerfile platforms: linux/amd64,linux/arm64 @@ -152,7 +152,7 @@ jobs: SENTRY_PROJECT: authentik SENTRY_URL: https://sentry.beryju.org with: - version: authentik@2022.6.2 + version: authentik@2022.6.3 environment: beryjuorg-prod sourcemaps: './web/dist' url_prefix: '~/static/dist' diff --git a/authentik/__init__.py b/authentik/__init__.py index de37dc6ca..c4a0520bf 100644 --- a/authentik/__init__.py +++ b/authentik/__init__.py @@ -2,7 +2,7 @@ from os import environ from typing import Optional -__version__ = "2022.6.2" +__version__ = "2022.6.3" ENV_GIT_HASH_KEY = "GIT_BUILD_HASH" diff --git a/docker-compose.yml b/docker-compose.yml index 149838bd9..6d35ddc6a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -29,7 +29,7 @@ services: retries: 5 timeout: 3s server: - image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2022.6.2} + image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2022.6.3} restart: unless-stopped command: server environment: @@ -50,7 +50,7 @@ services: - "0.0.0.0:${AUTHENTIK_PORT_HTTP:-9000}:9000" - "0.0.0.0:${AUTHENTIK_PORT_HTTPS:-9443}:9443" worker: - image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2022.6.2} + image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2022.6.3} restart: unless-stopped command: worker environment: diff --git a/internal/constants/constants.go b/internal/constants/constants.go index e1d0d5a33..ebd885b1b 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -25,4 +25,4 @@ func OutpostUserAgent() string { return fmt.Sprintf("goauthentik.io/outpost/%s", FullVersion()) } -const VERSION = "2022.6.2" +const VERSION = "2022.6.3" diff --git a/pyproject.toml b/pyproject.toml index fd4ae2588..e3a17c60e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -90,7 +90,7 @@ addopts = "-p no:celery --junitxml=unittest.xml" [tool.poetry] name = "authentik" -version = "2022.6.2" +version = "2022.6.3" description = "" authors = ["authentik Team "] diff --git a/schema.yml b/schema.yml index 16ce5b89e..c66fe9b7f 100644 --- a/schema.yml +++ b/schema.yml @@ -1,7 +1,7 @@ openapi: 3.0.3 info: title: authentik - version: 2022.6.2 + version: 2022.6.3 description: Making authentication simple. contact: email: hello@goauthentik.io diff --git a/web/src/constants.ts b/web/src/constants.ts index 3e9b592d3..e66700b9c 100644 --- a/web/src/constants.ts +++ b/web/src/constants.ts @@ -3,7 +3,7 @@ export const SUCCESS_CLASS = "pf-m-success"; export const ERROR_CLASS = "pf-m-danger"; export const PROGRESS_CLASS = "pf-m-in-progress"; export const CURRENT_CLASS = "pf-m-current"; -export const VERSION = "2022.6.2"; +export const VERSION = "2022.6.3"; export const TITLE_DEFAULT = "authentik"; export const ROUTE_SEPARATOR = ";";