mirror of
https://github.com/goauthentik/authentik
synced 2026-04-28 10:28:22 +02:00
Compare commits
78 Commits
admin/vers
...
2025-6-mem
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
969201360d | ||
|
|
1cff6fdd67 | ||
|
|
70845b1d5c | ||
|
|
7a39d8ce64 | ||
|
|
12682bf08e | ||
|
|
86f430b7b1 | ||
|
|
f57d345c9a | ||
|
|
41e2106d9f | ||
|
|
467a26faf5 | ||
|
|
1525a4c2e5 | ||
|
|
9645bdc9b9 | ||
|
|
952edb75fd | ||
|
|
4b73a1ec38 | ||
|
|
600052bd0f | ||
|
|
5d5fb39736 | ||
|
|
cc7b25d828 | ||
|
|
a8134e26c8 | ||
|
|
1c637bc0d1 | ||
|
|
8af668a9d2 | ||
|
|
96266e2e2b | ||
|
|
65373ab217 | ||
|
|
88590e1134 | ||
|
|
f6ff31e3de | ||
|
|
cbc14524b3 | ||
|
|
d807038d05 | ||
|
|
ba131a50ba | ||
|
|
0e37a66751 | ||
|
|
57c220dfc2 | ||
|
|
7339a22080 | ||
|
|
8c2d72affe | ||
|
|
9c31e08bd9 | ||
|
|
805b9afa80 | ||
|
|
bc809bae1e | ||
|
|
66773b69ab | ||
|
|
c149701501 | ||
|
|
b13b51b73a | ||
|
|
ee30cc1ede | ||
|
|
de095f4a10 | ||
|
|
abf3aa3a7f | ||
|
|
3ebae72a76 | ||
|
|
b6b47b669e | ||
|
|
8422568c42 | ||
|
|
9973064f50 | ||
|
|
4fea65f5cc | ||
|
|
784446a47d | ||
|
|
516bc65fc4 | ||
|
|
efb59adeff | ||
|
|
43a2ad66f0 | ||
|
|
ec0c59f1fc | ||
|
|
8f80072321 | ||
|
|
33f95c837b | ||
|
|
43637b8a75 | ||
|
|
7a4518be26 | ||
|
|
b94fb53821 | ||
|
|
2be5c9633b | ||
|
|
e729e42595 | ||
|
|
01d591b84e | ||
|
|
dd08e1bf66 | ||
|
|
150705f221 | ||
|
|
6b39f6495e | ||
|
|
639c57245b | ||
|
|
730600aea4 | ||
|
|
e15ce5a3f0 | ||
|
|
1fc91b004b | ||
|
|
644705e6fe | ||
|
|
ff8ef523db | ||
|
|
1051dd19ea | ||
|
|
04cb4fd267 | ||
|
|
da9508f839 | ||
|
|
841a286a25 | ||
|
|
63c48d7b99 | ||
|
|
5994fd2c61 | ||
|
|
5f745e682e | ||
|
|
6f1b16e7f9 | ||
|
|
57bce19e7a | ||
|
|
850c5d5a45 | ||
|
|
8b7d11f94c | ||
|
|
45737909f6 |
@@ -1,5 +1,5 @@
|
||||
[bumpversion]
|
||||
current_version = 2025.4.1
|
||||
current_version = 2025.6.3
|
||||
tag = True
|
||||
commit = True
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-(?P<rc_t>[a-zA-Z-]+)(?P<rc_n>[1-9]\\d*))?
|
||||
@@ -21,6 +21,8 @@ optional_value = final
|
||||
|
||||
[bumpversion:file:package.json]
|
||||
|
||||
[bumpversion:file:package-lock.json]
|
||||
|
||||
[bumpversion:file:docker-compose.yml]
|
||||
|
||||
[bumpversion:file:schema.yml]
|
||||
@@ -31,6 +33,4 @@ optional_value = final
|
||||
|
||||
[bumpversion:file:internal/constants/constants.go]
|
||||
|
||||
[bumpversion:file:web/src/common/constants.ts]
|
||||
|
||||
[bumpversion:file:lifecycle/aws/template.yaml]
|
||||
|
||||
2
.github/workflows/ci-main.yml
vendored
2
.github/workflows/ci-main.yml
vendored
@@ -202,7 +202,7 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: web/dist
|
||||
key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'web/src/**', 'web/packages/sfe/src/**') }}-b
|
||||
key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'package-lock.json', 'web/src/**', 'web/packages/sfe/src/**') }}-b
|
||||
- name: prepare web ui
|
||||
if: steps.cache-web.outputs.cache-hit != 'true'
|
||||
working-directory: web
|
||||
|
||||
1
.github/workflows/ci-website.yml
vendored
1
.github/workflows/ci-website.yml
vendored
@@ -49,6 +49,7 @@ jobs:
|
||||
matrix:
|
||||
job:
|
||||
- build
|
||||
- build:integrations
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
|
||||
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@@ -2,7 +2,7 @@ name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, "*", next, version*]
|
||||
branches: [main, next, version*]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
schedule:
|
||||
|
||||
@@ -96,7 +96,7 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
|
||||
# Stage 5: Download uv
|
||||
FROM ghcr.io/astral-sh/uv:0.7.8 AS uv
|
||||
# Stage 6: Base python image
|
||||
FROM ghcr.io/goauthentik/fips-python:3.13.3-slim-bookworm-fips AS python-base
|
||||
FROM ghcr.io/goauthentik/fips-python:3.13.5-slim-bookworm-fips AS python-base
|
||||
|
||||
ENV VENV_PATH="/ak-root/.venv" \
|
||||
PATH="/lifecycle:/ak-root/.venv/bin:$PATH" \
|
||||
|
||||
4
Makefile
4
Makefile
@@ -86,6 +86,10 @@ dev-create-db:
|
||||
|
||||
dev-reset: dev-drop-db dev-create-db migrate ## Drop and restore the Authentik PostgreSQL instance to a "fresh install" state.
|
||||
|
||||
update-test-mmdb: ## Update test GeoIP and ASN Databases
|
||||
curl -L https://raw.githubusercontent.com/maxmind/MaxMind-DB/refs/heads/main/test-data/GeoLite2-ASN-Test.mmdb -o ${PWD}/tests/GeoLite2-ASN-Test.mmdb
|
||||
curl -L https://raw.githubusercontent.com/maxmind/MaxMind-DB/refs/heads/main/test-data/GeoLite2-City-Test.mmdb -o ${PWD}/tests/GeoLite2-City-Test.mmdb
|
||||
|
||||
#########################
|
||||
## API Schema
|
||||
#########################
|
||||
|
||||
@@ -20,8 +20,8 @@ Even if the issue is not a CVE, we still greatly appreciate your help in hardeni
|
||||
|
||||
| Version | Supported |
|
||||
| --------- | --------- |
|
||||
| 2025.2.x | ✅ |
|
||||
| 2025.4.x | ✅ |
|
||||
| 2025.6.x | ✅ |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from os import environ
|
||||
|
||||
__version__ = "2025.4.1"
|
||||
__version__ = "2025.6.3"
|
||||
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ from collections.abc import Callable
|
||||
from django.apps import apps
|
||||
from django.test import TestCase
|
||||
|
||||
from authentik.blueprints.v1.importer import is_model_allowed
|
||||
from authentik.lib.models import SerializerModel
|
||||
from authentik.providers.oauth2.models import RefreshToken
|
||||
|
||||
@@ -22,10 +21,13 @@ def serializer_tester_factory(test_model: type[SerializerModel]) -> Callable:
|
||||
return
|
||||
model_class = test_model()
|
||||
self.assertTrue(isinstance(model_class, SerializerModel))
|
||||
# Models that have subclasses don't have to have a serializer
|
||||
if len(test_model.__subclasses__()) > 0:
|
||||
return
|
||||
self.assertIsNotNone(model_class.serializer)
|
||||
if model_class.serializer.Meta().model == RefreshToken:
|
||||
return
|
||||
self.assertEqual(model_class.serializer.Meta().model, test_model)
|
||||
self.assertTrue(issubclass(test_model, model_class.serializer.Meta().model))
|
||||
|
||||
return tester
|
||||
|
||||
@@ -34,6 +36,6 @@ for app in apps.get_app_configs():
|
||||
if not app.label.startswith("authentik"):
|
||||
continue
|
||||
for model in app.get_models():
|
||||
if not is_model_allowed(model):
|
||||
if not issubclass(model, SerializerModel):
|
||||
continue
|
||||
setattr(TestModels, f"test_{app.label}_{model.__name__}", serializer_tester_factory(model))
|
||||
|
||||
@@ -148,3 +148,14 @@ class TestBrands(APITestCase):
|
||||
"default_locale": "",
|
||||
},
|
||||
)
|
||||
|
||||
def test_custom_css(self):
|
||||
"""Test custom_css"""
|
||||
brand = create_test_brand()
|
||||
brand.branding_custom_css = """* {
|
||||
font-family: "Foo bar";
|
||||
}"""
|
||||
brand.save()
|
||||
res = self.client.get(reverse("authentik_core:if-user"))
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertIn(brand.branding_custom_css, res.content.decode())
|
||||
|
||||
@@ -5,6 +5,8 @@ from typing import Any
|
||||
from django.db.models import F, Q
|
||||
from django.db.models import Value as V
|
||||
from django.http.request import HttpRequest
|
||||
from django.utils.html import _json_script_escapes
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from authentik import get_full_version
|
||||
from authentik.brands.models import Brand
|
||||
@@ -32,8 +34,13 @@ def context_processor(request: HttpRequest) -> dict[str, Any]:
|
||||
"""Context Processor that injects brand object into every template"""
|
||||
brand = getattr(request, "brand", DEFAULT_BRAND)
|
||||
tenant = getattr(request, "tenant", Tenant())
|
||||
# similarly to `json_script` we escape everything HTML-related, however django
|
||||
# only directly exposes this as a function that also wraps it in a <script> tag
|
||||
# which we dont want for CSS
|
||||
brand_css = mark_safe(str(brand.branding_custom_css).translate(_json_script_escapes)) # nosec
|
||||
return {
|
||||
"brand": brand,
|
||||
"brand_css": brand_css,
|
||||
"footer_links": tenant.footer_links,
|
||||
"html_meta": {**get_http_meta()},
|
||||
"version": get_full_version(),
|
||||
|
||||
24
authentik/core/migrations/0049_alter_token_options.py
Normal file
24
authentik/core/migrations/0049_alter_token_options.py
Normal file
@@ -0,0 +1,24 @@
|
||||
# Generated by Django 5.1.11 on 2025-07-03 13:08
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_core", "0048_delete_oldauthenticatedsession_content_type"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name="token",
|
||||
options={
|
||||
"permissions": [
|
||||
("view_token_key", "View token's key"),
|
||||
("set_token_key", "Set a token's key"),
|
||||
],
|
||||
"verbose_name": "Token",
|
||||
"verbose_name_plural": "Tokens",
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -953,7 +953,10 @@ class Token(SerializerModel, ManagedModel, ExpiringModel):
|
||||
models.Index(fields=["identifier"]),
|
||||
models.Index(fields=["key"]),
|
||||
]
|
||||
permissions = [("view_token_key", _("View token's key"))]
|
||||
permissions = [
|
||||
("view_token_key", _("View token's key")),
|
||||
("set_token_key", _("Set a token's key")),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
description = f"{self.identifier}"
|
||||
@@ -1082,6 +1085,12 @@ class AuthenticatedSession(SerializerModel):
|
||||
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
from authentik.core.api.authenticated_sessions import AuthenticatedSessionSerializer
|
||||
|
||||
return AuthenticatedSessionSerializer
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Authenticated Session")
|
||||
verbose_name_plural = _("Authenticated Sessions")
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
{% block head_before %}
|
||||
{% endblock %}
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/authentik.css' %}">
|
||||
<style>{{ brand.branding_custom_css }}</style>
|
||||
<style>{{ brand_css }}</style>
|
||||
<script src="{% versioned_script 'dist/poly-%v.js' %}" type="module"></script>
|
||||
<script src="{% versioned_script 'dist/standalone/loading/index-%v.js' %}" type="module"></script>
|
||||
{% block head %}
|
||||
|
||||
@@ -16,7 +16,7 @@ from authentik.stages.authenticator.models import Device
|
||||
|
||||
|
||||
class AuthenticatorEndpointGDTCStage(ConfigurableStage, FriendlyNamedStage, Stage):
|
||||
"""Setup Google Chrome Device-trust connection"""
|
||||
"""Setup Google Chrome Device Trust connection"""
|
||||
|
||||
credentials = models.JSONField()
|
||||
|
||||
|
||||
@@ -15,13 +15,13 @@ class MMDBContextProcessor(EventContextProcessor):
|
||||
self.reader: Reader | None = None
|
||||
self._last_mtime: float = 0.0
|
||||
self.logger = get_logger()
|
||||
self.open()
|
||||
self.load()
|
||||
|
||||
def path(self) -> str | None:
|
||||
"""Get the path to the MMDB file to load"""
|
||||
raise NotImplementedError
|
||||
|
||||
def open(self):
|
||||
def load(self):
|
||||
"""Get GeoIP Reader, if configured, otherwise none"""
|
||||
path = self.path()
|
||||
if path == "" or not path:
|
||||
@@ -44,7 +44,7 @@ class MMDBContextProcessor(EventContextProcessor):
|
||||
diff = self._last_mtime < mtime
|
||||
if diff > 0:
|
||||
self.logger.info("Found new MMDB Database, reopening", diff=diff, path=path)
|
||||
self.open()
|
||||
self.load()
|
||||
except OSError as exc:
|
||||
self.logger.warning("Failed to check MMDB age", exc=exc)
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ class SyncTasks:
|
||||
def sync_objects(
|
||||
self, object_type: str, page: int, provider_pk: int, override_dry_run=False, **filter
|
||||
):
|
||||
_object_type = path_to_class(object_type)
|
||||
_object_type: type[Model] = path_to_class(object_type)
|
||||
self.logger = get_logger().bind(
|
||||
provider_type=class_to_path(self._provider_model),
|
||||
provider_pk=provider_pk,
|
||||
@@ -156,7 +156,11 @@ class SyncTasks:
|
||||
messages.append(
|
||||
asdict(
|
||||
LogEvent(
|
||||
_("Syncing page {page} of groups".format(page=page)),
|
||||
_(
|
||||
"Syncing page {page} of {object_type}".format(
|
||||
page=page, object_type=_object_type._meta.verbose_name_plural
|
||||
)
|
||||
),
|
||||
log_level="info",
|
||||
logger=f"{provider._meta.verbose_name}@{object_type}",
|
||||
)
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
"""Websocket tests"""
|
||||
|
||||
from dataclasses import asdict
|
||||
from unittest.mock import patch
|
||||
|
||||
from channels.routing import URLRouter
|
||||
from channels.testing import WebsocketCommunicator
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TransactionTestCase
|
||||
|
||||
from authentik import __version__
|
||||
@@ -16,12 +14,6 @@ from authentik.providers.proxy.models import ProxyProvider
|
||||
from authentik.root import websocket
|
||||
|
||||
|
||||
def patched__get_ct_cached(app_label, codename):
|
||||
"""Caches `ContentType` instances like its `QuerySet` does."""
|
||||
return ContentType.objects.get(app_label=app_label, permission__codename=codename)
|
||||
|
||||
|
||||
@patch("guardian.shortcuts._get_ct_cached", patched__get_ct_cached)
|
||||
class TestOutpostWS(TransactionTestCase):
|
||||
"""Websocket tests"""
|
||||
|
||||
|
||||
@@ -102,6 +102,7 @@ class IngressReconciler(KubernetesObjectReconciler[V1Ingress]):
|
||||
# Buffer sizes for large headers with JWTs
|
||||
"nginx.ingress.kubernetes.io/proxy-buffers-number": "4",
|
||||
"nginx.ingress.kubernetes.io/proxy-buffer-size": "16k",
|
||||
"nginx.ingress.kubernetes.io/proxy-busy-buffers-size": "32k",
|
||||
# Enable TLS in traefik
|
||||
"traefik.ingress.kubernetes.io/router.tls": "true",
|
||||
}
|
||||
|
||||
@@ -66,7 +66,10 @@ class RACClientConsumer(AsyncWebsocketConsumer):
|
||||
def init_outpost_connection(self):
|
||||
"""Initialize guac connection settings"""
|
||||
self.token = (
|
||||
ConnectionToken.filter_not_expired(token=self.scope["url_route"]["kwargs"]["token"])
|
||||
ConnectionToken.filter_not_expired(
|
||||
token=self.scope["url_route"]["kwargs"]["token"],
|
||||
session__session__session_key=self.scope["session"].session_key,
|
||||
)
|
||||
.select_related("endpoint", "provider", "session", "session__user")
|
||||
.first()
|
||||
)
|
||||
|
||||
@@ -166,7 +166,6 @@ class ConnectionToken(ExpiringModel):
|
||||
always_merger.merge(settings, default_settings)
|
||||
always_merger.merge(settings, self.endpoint.provider.settings)
|
||||
always_merger.merge(settings, self.endpoint.settings)
|
||||
always_merger.merge(settings, self.settings)
|
||||
|
||||
def mapping_evaluator(mappings: QuerySet):
|
||||
for mapping in mappings:
|
||||
@@ -191,6 +190,7 @@ class ConnectionToken(ExpiringModel):
|
||||
mapping_evaluator(
|
||||
RACPropertyMapping.objects.filter(endpoint__in=[self.endpoint]).order_by("name")
|
||||
)
|
||||
always_merger.merge(settings, self.settings)
|
||||
|
||||
settings["drive-path"] = f"/tmp/connection/{self.token}" # nosec
|
||||
settings["create-drive-path"] = "true"
|
||||
|
||||
@@ -90,23 +90,6 @@ class TestModels(TransactionTestCase):
|
||||
"resize-method": "display-update",
|
||||
},
|
||||
)
|
||||
# Set settings in token
|
||||
token.settings = {
|
||||
"level": "token",
|
||||
}
|
||||
token.save()
|
||||
self.assertEqual(
|
||||
token.get_settings(),
|
||||
{
|
||||
"hostname": self.endpoint.host.split(":")[0],
|
||||
"port": "1324",
|
||||
"client-name": f"authentik - {self.user}",
|
||||
"drive-path": path,
|
||||
"create-drive-path": "true",
|
||||
"level": "token",
|
||||
"resize-method": "display-update",
|
||||
},
|
||||
)
|
||||
# Set settings in property mapping (provider)
|
||||
mapping = RACPropertyMapping.objects.create(
|
||||
name=generate_id(),
|
||||
@@ -151,3 +134,22 @@ class TestModels(TransactionTestCase):
|
||||
"resize-method": "display-update",
|
||||
},
|
||||
)
|
||||
# Set settings in token
|
||||
token.settings = {
|
||||
"level": "token",
|
||||
}
|
||||
token.save()
|
||||
self.assertEqual(
|
||||
token.get_settings(),
|
||||
{
|
||||
"hostname": self.endpoint.host.split(":")[0],
|
||||
"port": "1324",
|
||||
"client-name": f"authentik - {self.user}",
|
||||
"drive-path": path,
|
||||
"create-drive-path": "true",
|
||||
"foo": "true",
|
||||
"bar": "6",
|
||||
"resize-method": "display-update",
|
||||
"level": "token",
|
||||
},
|
||||
)
|
||||
|
||||
@@ -87,3 +87,22 @@ class TestRACViews(APITestCase):
|
||||
)
|
||||
body = loads(flow_response.content)
|
||||
self.assertEqual(body["component"], "ak-stage-access-denied")
|
||||
|
||||
def test_different_session(self):
|
||||
"""Test request"""
|
||||
self.client.force_login(self.user)
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"authentik_providers_rac:start",
|
||||
kwargs={"app": self.app.slug, "endpoint": str(self.endpoint.pk)},
|
||||
)
|
||||
)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
flow_response = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
||||
)
|
||||
body = loads(flow_response.content)
|
||||
next_url = body["to"]
|
||||
self.client.logout()
|
||||
final_response = self.client.get(next_url)
|
||||
self.assertEqual(final_response.url, reverse("authentik_core:if-user"))
|
||||
|
||||
@@ -20,6 +20,9 @@ from authentik.lib.utils.time import timedelta_from_string
|
||||
from authentik.policies.engine import PolicyEngine
|
||||
from authentik.policies.views import PolicyAccessView
|
||||
from authentik.providers.rac.models import ConnectionToken, Endpoint, RACProvider
|
||||
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
|
||||
|
||||
PLAN_CONNECTION_SETTINGS = "connection_settings"
|
||||
|
||||
|
||||
class RACStartView(PolicyAccessView):
|
||||
@@ -65,7 +68,10 @@ class RACInterface(InterfaceView):
|
||||
|
||||
def dispatch(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
|
||||
# Early sanity check to ensure token still exists
|
||||
token = ConnectionToken.filter_not_expired(token=self.kwargs["token"]).first()
|
||||
token = ConnectionToken.filter_not_expired(
|
||||
token=self.kwargs["token"],
|
||||
session__session__session_key=request.session.session_key,
|
||||
).first()
|
||||
if not token:
|
||||
return redirect("authentik_core:if-user")
|
||||
self.token = token
|
||||
@@ -109,10 +115,15 @@ class RACFinalStage(RedirectStage):
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_challenge(self, *args, **kwargs) -> RedirectChallenge:
|
||||
settings = self.executor.plan.context.get(PLAN_CONNECTION_SETTINGS)
|
||||
if not settings:
|
||||
settings = self.executor.plan.context.get(PLAN_CONTEXT_PROMPT, {}).get(
|
||||
PLAN_CONNECTION_SETTINGS
|
||||
)
|
||||
token = ConnectionToken.objects.create(
|
||||
provider=self.provider,
|
||||
endpoint=self.endpoint,
|
||||
settings=self.executor.plan.context.get("connection_settings", {}),
|
||||
settings=settings or {},
|
||||
session=self.request.session["authenticatedsession"],
|
||||
expires=now() + timedelta_from_string(self.provider.connection_expiry),
|
||||
expiring=True,
|
||||
|
||||
@@ -3,25 +3,46 @@
|
||||
import os
|
||||
from argparse import ArgumentParser
|
||||
from unittest import TestCase
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from django.conf import settings
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test.runner import DiscoverRunner
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.events.context_processors.asn import ASN_CONTEXT_PROCESSOR
|
||||
from authentik.events.context_processors.geoip import GEOIP_CONTEXT_PROCESSOR
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.sentry import sentry_init
|
||||
from authentik.root.signals import post_startup, pre_startup, startup
|
||||
from tests.e2e.utils import get_docker_tag
|
||||
|
||||
# globally set maxDiff to none to show full assert error
|
||||
TestCase.maxDiff = None
|
||||
|
||||
|
||||
def get_docker_tag() -> str:
|
||||
"""Get docker-tag based off of CI variables"""
|
||||
env_pr_branch = "GITHUB_HEAD_REF"
|
||||
default_branch = "GITHUB_REF"
|
||||
branch_name = os.environ.get(default_branch, "main")
|
||||
if os.environ.get(env_pr_branch, "") != "":
|
||||
branch_name = os.environ[env_pr_branch]
|
||||
branch_name = branch_name.replace("refs/heads/", "").replace("/", "-")
|
||||
return f"gh-{branch_name}"
|
||||
|
||||
|
||||
def patched__get_ct_cached(app_label, codename):
|
||||
"""Caches `ContentType` instances like its `QuerySet` does."""
|
||||
return ContentType.objects.get(app_label=app_label, permission__codename=codename)
|
||||
|
||||
|
||||
class PytestTestRunner(DiscoverRunner): # pragma: no cover
|
||||
"""Runs pytest to discover and run tests."""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.logger = get_logger().bind(runner="pytest")
|
||||
|
||||
self.args = []
|
||||
if self.failfast:
|
||||
@@ -48,6 +69,10 @@ class PytestTestRunner(DiscoverRunner): # pragma: no cover
|
||||
CONFIG.set("error_reporting.sample_rate", 0)
|
||||
CONFIG.set("error_reporting.environment", "testing")
|
||||
CONFIG.set("error_reporting.send_pii", True)
|
||||
|
||||
ASN_CONTEXT_PROCESSOR.load()
|
||||
GEOIP_CONTEXT_PROCESSOR.load()
|
||||
|
||||
sentry_init()
|
||||
|
||||
pre_startup.send(sender=self, mode="test")
|
||||
@@ -113,4 +138,10 @@ class PytestTestRunner(DiscoverRunner): # pragma: no cover
|
||||
f"path instead."
|
||||
)
|
||||
|
||||
return pytest.main(self.args)
|
||||
self.logger.info("Running tests", test_files=self.args)
|
||||
with patch("guardian.shortcuts._get_ct_cached", patched__get_ct_cached):
|
||||
try:
|
||||
return pytest.main(self.args)
|
||||
except Exception as e:
|
||||
self.logger.error("Error running tests", error=str(e), test_files=self.args)
|
||||
return 1
|
||||
|
||||
@@ -71,37 +71,31 @@ def ldap_sync_single(source_pk: str):
|
||||
return
|
||||
# Delete all sync tasks from the cache
|
||||
DBSystemTask.objects.filter(name="ldap_sync", uid__startswith=source.slug).delete()
|
||||
task = chain(
|
||||
# User and group sync can happen at once, they have no dependencies on each other
|
||||
group(
|
||||
ldap_sync_paginator(source, UserLDAPSynchronizer)
|
||||
+ ldap_sync_paginator(source, GroupLDAPSynchronizer),
|
||||
),
|
||||
# Membership sync needs to run afterwards
|
||||
group(
|
||||
ldap_sync_paginator(source, MembershipLDAPSynchronizer),
|
||||
),
|
||||
# Finally, deletions. What we'd really like to do here is something like
|
||||
# ```
|
||||
# user_identifiers = <ldap query>
|
||||
# User.objects.exclude(
|
||||
# usersourceconnection__identifier__in=user_uniqueness_identifiers,
|
||||
# ).delete()
|
||||
# ```
|
||||
# This runs into performance issues in large installations. So instead we spread the
|
||||
# work out into three steps:
|
||||
# 1. Get every object from the LDAP source.
|
||||
# 2. Mark every object as "safe" in the database. This is quick, but any error could
|
||||
# mean deleting users which should not be deleted, so we do it immediately, in
|
||||
# large chunks, and only queue the deletion step afterwards.
|
||||
# 3. Delete every unmarked item. This is slow, so we spread it over many tasks in
|
||||
# small chunks.
|
||||
group(
|
||||
ldap_sync_paginator(source, UserLDAPForwardDeletion)
|
||||
+ ldap_sync_paginator(source, GroupLDAPForwardDeletion),
|
||||
),
|
||||
|
||||
# The order of these operations needs to be preserved as each depends on the previous one(s)
|
||||
# 1. User and group sync can happen simultaneously
|
||||
# 2. Membership sync needs to run afterwards
|
||||
# 3. Finally, user and group deletions can happen simultaneously
|
||||
user_group_sync = ldap_sync_paginator(source, UserLDAPSynchronizer) + ldap_sync_paginator(
|
||||
source, GroupLDAPSynchronizer
|
||||
)
|
||||
task()
|
||||
membership_sync = ldap_sync_paginator(source, MembershipLDAPSynchronizer)
|
||||
user_group_deletion = ldap_sync_paginator(
|
||||
source, UserLDAPForwardDeletion
|
||||
) + ldap_sync_paginator(source, GroupLDAPForwardDeletion)
|
||||
|
||||
# Celery is buggy with empty groups, so we are careful only to add non-empty groups.
|
||||
# See https://github.com/celery/celery/issues/9772
|
||||
task_groups = []
|
||||
if user_group_sync:
|
||||
task_groups.append(group(user_group_sync))
|
||||
if membership_sync:
|
||||
task_groups.append(group(membership_sync))
|
||||
if user_group_deletion:
|
||||
task_groups.append(group(user_group_deletion))
|
||||
|
||||
all_tasks = chain(task_groups)
|
||||
all_tasks()
|
||||
|
||||
|
||||
def ldap_sync_paginator(source: LDAPSource, sync: type[BaseLDAPSynchronizer]) -> list:
|
||||
|
||||
@@ -151,9 +151,7 @@ class AuthenticatorValidateStageWebAuthnTests(FlowTestCase):
|
||||
webauthn_user_verification=UserVerification.PREFERRED,
|
||||
)
|
||||
stage.webauthn_allowed_device_types.set(
|
||||
WebAuthnDeviceType.objects.filter(
|
||||
description="Android Authenticator with SafetyNet Attestation"
|
||||
)
|
||||
WebAuthnDeviceType.objects.filter(description="YubiKey 5 Series")
|
||||
)
|
||||
session = self.client.session
|
||||
plan = FlowPlan(flow_pk=flow.pk.hex)
|
||||
@@ -339,9 +337,7 @@ class AuthenticatorValidateStageWebAuthnTests(FlowTestCase):
|
||||
device_classes=[DeviceClasses.WEBAUTHN],
|
||||
)
|
||||
stage.webauthn_allowed_device_types.set(
|
||||
WebAuthnDeviceType.objects.filter(
|
||||
description="Android Authenticator with SafetyNet Attestation"
|
||||
)
|
||||
WebAuthnDeviceType.objects.filter(description="YubiKey 5 Series")
|
||||
)
|
||||
session = self.client.session
|
||||
plan = FlowPlan(flow_pk=flow.pk.hex)
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -141,9 +141,7 @@ class TestAuthenticatorWebAuthnStage(FlowTestCase):
|
||||
"""Test registration with restricted devices (fail)"""
|
||||
webauthn_mds_import.delay(force=True).get()
|
||||
self.stage.device_type_restrictions.set(
|
||||
WebAuthnDeviceType.objects.filter(
|
||||
description="Android Authenticator with SafetyNet Attestation"
|
||||
)
|
||||
WebAuthnDeviceType.objects.filter(description="YubiKey 5 Series")
|
||||
)
|
||||
|
||||
plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()])
|
||||
|
||||
@@ -100,9 +100,11 @@ def send_mail(
|
||||
# Because we use the Message-ID as UID for the task, manually assign it
|
||||
message_object.extra_headers["Message-ID"] = message_id
|
||||
|
||||
# Add the logo (we can't add it in the previous message since MIMEImage
|
||||
# can't be converted to json)
|
||||
message_object.attach(logo_data())
|
||||
# Add the logo if it is used in the email body (we can't add it in the
|
||||
# previous message since MIMEImage can't be converted to json)
|
||||
body = get_email_body(message_object)
|
||||
if "cid:logo" in body:
|
||||
message_object.attach(logo_data())
|
||||
|
||||
if (
|
||||
message_object.to
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
<table width="100%" style="background-color: #FFFFFF; border-spacing: 0; margin-top: 15px;">
|
||||
<tr height="80">
|
||||
<td align="center" style="padding: 20px 0;">
|
||||
<img src="{% block logo_url %}cid:logo.png{% endblock %}" border="0=" alt="authentik logo" class="flexibleImage logo">
|
||||
<img src="{% block logo_url %}cid:logo{% endblock %}" border="0=" alt="authentik logo" class="flexibleImage logo">
|
||||
</td>
|
||||
</tr>
|
||||
{% block content %}
|
||||
|
||||
@@ -19,7 +19,8 @@ def logo_data() -> MIMEImage:
|
||||
path = Path("web/dist/assets/icons/icon_left_brand.png")
|
||||
with open(path, "rb") as _logo_file:
|
||||
logo = MIMEImage(_logo_file.read())
|
||||
logo.add_header("Content-ID", "logo.png")
|
||||
logo.add_header("Content-ID", "<logo>")
|
||||
logo.add_header("Content-Disposition", "inline", filename="logo.png")
|
||||
return logo
|
||||
|
||||
|
||||
|
||||
@@ -101,9 +101,9 @@ class BoundSessionMiddleware(SessionMiddleware):
|
||||
SESSION_KEY_BINDING_GEO, GeoIPBinding.NO_BINDING
|
||||
)
|
||||
if configured_binding_net != NetworkBinding.NO_BINDING:
|
||||
self.recheck_session_net(configured_binding_net, last_ip, new_ip)
|
||||
BoundSessionMiddleware.recheck_session_net(configured_binding_net, last_ip, new_ip)
|
||||
if configured_binding_geo != GeoIPBinding.NO_BINDING:
|
||||
self.recheck_session_geo(configured_binding_geo, last_ip, new_ip)
|
||||
BoundSessionMiddleware.recheck_session_geo(configured_binding_geo, last_ip, new_ip)
|
||||
# If we got to this point without any error being raised, we need to
|
||||
# update the last saved IP to the current one
|
||||
if SESSION_KEY_BINDING_NET in request.session or SESSION_KEY_BINDING_GEO in request.session:
|
||||
@@ -111,7 +111,8 @@ class BoundSessionMiddleware(SessionMiddleware):
|
||||
# (== basically requires the user to be logged in)
|
||||
request.session[request.session.model.Keys.LAST_IP] = new_ip
|
||||
|
||||
def recheck_session_net(self, binding: NetworkBinding, last_ip: str, new_ip: str):
|
||||
@staticmethod
|
||||
def recheck_session_net(binding: NetworkBinding, last_ip: str, new_ip: str):
|
||||
"""Check network/ASN binding"""
|
||||
last_asn = ASN_CONTEXT_PROCESSOR.asn(last_ip)
|
||||
new_asn = ASN_CONTEXT_PROCESSOR.asn(new_ip)
|
||||
@@ -158,7 +159,8 @@ class BoundSessionMiddleware(SessionMiddleware):
|
||||
new_ip,
|
||||
)
|
||||
|
||||
def recheck_session_geo(self, binding: GeoIPBinding, last_ip: str, new_ip: str):
|
||||
@staticmethod
|
||||
def recheck_session_geo(binding: GeoIPBinding, last_ip: str, new_ip: str):
|
||||
"""Check GeoIP binding"""
|
||||
last_geo = GEOIP_CONTEXT_PROCESSOR.city(last_ip)
|
||||
new_geo = GEOIP_CONTEXT_PROCESSOR.city(new_ip)
|
||||
@@ -179,8 +181,8 @@ class BoundSessionMiddleware(SessionMiddleware):
|
||||
if last_geo.continent != new_geo.continent:
|
||||
raise SessionBindingBroken(
|
||||
"geoip.continent",
|
||||
last_geo.continent,
|
||||
new_geo.continent,
|
||||
last_geo.continent.to_dict(),
|
||||
new_geo.continent.to_dict(),
|
||||
last_ip,
|
||||
new_ip,
|
||||
)
|
||||
@@ -192,8 +194,8 @@ class BoundSessionMiddleware(SessionMiddleware):
|
||||
if last_geo.country != new_geo.country:
|
||||
raise SessionBindingBroken(
|
||||
"geoip.country",
|
||||
last_geo.country,
|
||||
new_geo.country,
|
||||
last_geo.country.to_dict(),
|
||||
new_geo.country.to_dict(),
|
||||
last_ip,
|
||||
new_ip,
|
||||
)
|
||||
@@ -202,8 +204,8 @@ class BoundSessionMiddleware(SessionMiddleware):
|
||||
if last_geo.city != new_geo.city:
|
||||
raise SessionBindingBroken(
|
||||
"geoip.city",
|
||||
last_geo.city,
|
||||
new_geo.city,
|
||||
last_geo.city.to_dict(),
|
||||
new_geo.city.to_dict(),
|
||||
last_ip,
|
||||
new_ip,
|
||||
)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
from time import sleep
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.http import HttpRequest
|
||||
from django.urls import reverse
|
||||
from django.utils.timezone import now
|
||||
|
||||
@@ -17,7 +18,12 @@ from authentik.flows.views.executor import SESSION_KEY_PLAN
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.lib.utils.time import timedelta_from_string
|
||||
from authentik.root.middleware import ClientIPMiddleware
|
||||
from authentik.stages.user_login.models import UserLoginStage
|
||||
from authentik.stages.user_login.middleware import (
|
||||
BoundSessionMiddleware,
|
||||
SessionBindingBroken,
|
||||
logout_extra,
|
||||
)
|
||||
from authentik.stages.user_login.models import GeoIPBinding, NetworkBinding, UserLoginStage
|
||||
|
||||
|
||||
class TestUserLoginStage(FlowTestCase):
|
||||
@@ -192,3 +198,52 @@ class TestUserLoginStage(FlowTestCase):
|
||||
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||
response = self.client.get(reverse("authentik_api:application-list"))
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
def test_binding_net_break_log(self):
|
||||
"""Test logout_extra with exception"""
|
||||
# IPs from https://github.com/maxmind/MaxMind-DB/blob/main/source-data/GeoLite2-ASN-Test.json
|
||||
for args, expect in [
|
||||
[[NetworkBinding.BIND_ASN, "8.8.8.8", "8.8.8.8"], ["network.missing"]],
|
||||
[[NetworkBinding.BIND_ASN, "1.0.0.1", "1.128.0.1"], ["network.asn"]],
|
||||
[
|
||||
[NetworkBinding.BIND_ASN_NETWORK, "12.81.96.1", "12.81.128.1"],
|
||||
["network.asn_network"],
|
||||
],
|
||||
[[NetworkBinding.BIND_ASN_NETWORK_IP, "1.0.0.1", "1.0.0.2"], ["network.ip"]],
|
||||
]:
|
||||
with self.subTest(args[0]):
|
||||
with self.assertRaises(SessionBindingBroken) as cm:
|
||||
BoundSessionMiddleware.recheck_session_net(*args)
|
||||
self.assertEqual(cm.exception.reason, expect[0])
|
||||
# Ensure the request can be logged without throwing errors
|
||||
self.client.force_login(self.user)
|
||||
request = HttpRequest()
|
||||
request.session = self.client.session
|
||||
request.user = self.user
|
||||
logout_extra(request, cm.exception)
|
||||
|
||||
def test_binding_geo_break_log(self):
|
||||
"""Test logout_extra with exception"""
|
||||
# IPs from https://github.com/maxmind/MaxMind-DB/blob/main/source-data/GeoLite2-City-Test.json
|
||||
for args, expect in [
|
||||
[[GeoIPBinding.BIND_CONTINENT, "8.8.8.8", "8.8.8.8"], ["geoip.missing"]],
|
||||
[[GeoIPBinding.BIND_CONTINENT, "2.125.160.216", "67.43.156.1"], ["geoip.continent"]],
|
||||
[
|
||||
[GeoIPBinding.BIND_CONTINENT_COUNTRY, "81.2.69.142", "89.160.20.112"],
|
||||
["geoip.country"],
|
||||
],
|
||||
[
|
||||
[GeoIPBinding.BIND_CONTINENT_COUNTRY_CITY, "2.125.160.216", "81.2.69.142"],
|
||||
["geoip.city"],
|
||||
],
|
||||
]:
|
||||
with self.subTest(args[0]):
|
||||
with self.assertRaises(SessionBindingBroken) as cm:
|
||||
BoundSessionMiddleware.recheck_session_geo(*args)
|
||||
self.assertEqual(cm.exception.reason, expect[0])
|
||||
# Ensure the request can be logged without throwing errors
|
||||
self.client.force_login(self.user)
|
||||
request = HttpRequest()
|
||||
request.session = self.client.session
|
||||
request.user = self.user
|
||||
logout_extra(request, cm.exception)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://goauthentik.io/blueprints/schema.json",
|
||||
"type": "object",
|
||||
"title": "authentik 2025.4.1 Blueprint schema",
|
||||
"title": "authentik 2025.6.3 Blueprint schema",
|
||||
"required": [
|
||||
"version",
|
||||
"entries"
|
||||
|
||||
@@ -31,7 +31,7 @@ services:
|
||||
volumes:
|
||||
- redis:/data
|
||||
server:
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.4.1}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.6.3}
|
||||
restart: unless-stopped
|
||||
command: server
|
||||
environment:
|
||||
@@ -55,7 +55,7 @@ services:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
worker:
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.4.1}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.6.3}
|
||||
restart: unless-stopped
|
||||
command: worker
|
||||
environment:
|
||||
|
||||
2
go.mod
2
go.mod
@@ -27,7 +27,7 @@ require (
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/wwt/guac v1.3.2
|
||||
goauthentik.io/api/v3 v3.2025041.2
|
||||
goauthentik.io/api/v3 v3.2025041.4
|
||||
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
||||
golang.org/x/oauth2 v0.30.0
|
||||
golang.org/x/sync v0.14.0
|
||||
|
||||
4
go.sum
4
go.sum
@@ -290,8 +290,8 @@ go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
goauthentik.io/api/v3 v3.2025041.2 h1:vFYYnhcDcxL95RczZwhzt3i4LptFXMvIRN+vgf8sQYg=
|
||||
goauthentik.io/api/v3 v3.2025041.2/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||
goauthentik.io/api/v3 v3.2025041.4 h1:cGqzWYnUHrWDoaXWDpIL/kWnX9sFrIhkYDye0P0OEAo=
|
||||
goauthentik.io/api/v3 v3.2025041.4/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
|
||||
@@ -33,4 +33,4 @@ func UserAgent() string {
|
||||
return fmt.Sprintf("authentik@%s", FullVersion())
|
||||
}
|
||||
|
||||
const VERSION = "2025.4.1"
|
||||
const VERSION = "2025.6.3"
|
||||
|
||||
@@ -66,8 +66,8 @@ func (g *GoUnicorn) initCmd() {
|
||||
panic(fmt.Errorf("failed to create temporary pid file: %v", err))
|
||||
}
|
||||
g.pidFile = pidFile.Name()
|
||||
command = "gunicorn"
|
||||
args = []string{"-c", "./lifecycle/gunicorn.conf.py", "authentik.root.asgi:application"}
|
||||
command = "fil-profile"
|
||||
args = []string{"-o", os.TempDir(), "run", "-m", "gunicorn", "-c", "./lifecycle/gunicorn.conf.py", "authentik.root.asgi:application"}
|
||||
if g.pidFile != "" {
|
||||
args = append(args, "--pid", g.pidFile)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"encoding/gob"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
@@ -118,8 +119,8 @@ func NewApplication(p api.ProxyOutpostConfig, c *http.Client, server Server, old
|
||||
mux := mux.NewRouter()
|
||||
|
||||
// Save cookie name, based on hashed client ID
|
||||
h := sha256.New()
|
||||
bs := string(h.Sum([]byte(*p.ClientId)))
|
||||
hs := sha256.Sum256([]byte(*p.ClientId))
|
||||
bs := hex.EncodeToString(hs[:])
|
||||
sessionName := fmt.Sprintf("authentik_proxy_%s", bs[:8])
|
||||
|
||||
// When HOST_BROWSER is set, use that as Host header for token requests to make the issuer match
|
||||
|
||||
@@ -3,6 +3,7 @@ package application
|
||||
type ProxyClaims struct {
|
||||
UserAttributes map[string]interface{} `json:"user_attributes"`
|
||||
BackendOverride string `json:"backend_override"`
|
||||
HostHeader string `json:"host_header"`
|
||||
IsSuperuser bool `json:"is_superuser"`
|
||||
}
|
||||
|
||||
|
||||
@@ -74,13 +74,18 @@ func (a *Application) proxyModifyRequest(ou *url.URL) func(req *http.Request) {
|
||||
r.URL.Scheme = ou.Scheme
|
||||
r.URL.Host = ou.Host
|
||||
claims := a.getClaimsFromSession(r)
|
||||
if claims != nil && claims.Proxy != nil && claims.Proxy.BackendOverride != "" {
|
||||
u, err := url.Parse(claims.Proxy.BackendOverride)
|
||||
if err != nil {
|
||||
a.log.WithField("backend_override", claims.Proxy.BackendOverride).WithError(err).Warning("failed parse user backend override")
|
||||
} else {
|
||||
r.URL.Scheme = u.Scheme
|
||||
r.URL.Host = u.Host
|
||||
if claims != nil && claims.Proxy != nil {
|
||||
if claims.Proxy.BackendOverride != "" {
|
||||
u, err := url.Parse(claims.Proxy.BackendOverride)
|
||||
if err != nil {
|
||||
a.log.WithField("backend_override", claims.Proxy.BackendOverride).WithError(err).Warning("failed parse user backend override")
|
||||
} else {
|
||||
r.URL.Scheme = u.Scheme
|
||||
r.URL.Host = u.Host
|
||||
}
|
||||
}
|
||||
if claims.Proxy.HostHeader != "" {
|
||||
r.Host = claims.Proxy.HostHeader
|
||||
}
|
||||
}
|
||||
a.log.WithField("upstream_url", r.URL.String()).Trace("final upstream url")
|
||||
|
||||
@@ -2,6 +2,7 @@ package radius
|
||||
|
||||
import (
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
"time"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
@@ -68,7 +69,9 @@ func (rs *RadiusServer) ServeRADIUS(w radius.ResponseWriter, r *radius.Request)
|
||||
}
|
||||
}
|
||||
if pi == nil {
|
||||
nr.Log().WithField("hashed_secret", string(sha512.New().Sum(r.Secret))).Warning("No provider found")
|
||||
hs := sha512.Sum512([]byte(r.Secret))
|
||||
bs := hex.EncodeToString(hs[:])
|
||||
nr.Log().WithField("hashed_secret", bs).Warning("No provider found")
|
||||
_ = w.Write(r.Response(radius.CodeAccessReject))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ Parameters:
|
||||
Description: authentik Docker image
|
||||
AuthentikVersion:
|
||||
Type: String
|
||||
Default: 2025.4.1
|
||||
Default: 2025.6.3
|
||||
Description: authentik Docker image tag
|
||||
AuthentikServerCPU:
|
||||
Type: Number
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-05-28 11:25+0000\n"
|
||||
"POT-Creation-Date: 2025-06-02 00:12+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -2226,6 +2226,10 @@ msgstr ""
|
||||
msgid "Consider Objects matching this filter to be Users."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "Attribute which matches the value of `group_membership_field`."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "Field which contains members of a group."
|
||||
msgstr ""
|
||||
@@ -3493,10 +3497,6 @@ msgstr ""
|
||||
msgid "No Pending user to login."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/user_login/stage.py
|
||||
msgid "Successfully logged in!"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/user_logout/models.py
|
||||
msgid "User Logout Stage"
|
||||
msgstr ""
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@goauthentik/authentik",
|
||||
"version": "2025.4.1",
|
||||
"version": "2025.6.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@goauthentik/authentik",
|
||||
"version": "2025.4.1",
|
||||
"version": "2025.6.3",
|
||||
"devDependencies": {
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||
"prettier": "^3.3.3",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@goauthentik/authentik",
|
||||
"version": "2025.4.1",
|
||||
"version": "2025.6.3",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "authentik"
|
||||
version = "2025.4.1"
|
||||
version = "2025.6.3"
|
||||
description = ""
|
||||
authors = [{ name = "authentik Team", email = "hello@goauthentik.io" }]
|
||||
requires-python = "==3.13.*"
|
||||
@@ -13,7 +13,7 @@ dependencies = [
|
||||
"dacite==1.9.2",
|
||||
"deepmerge==2.0",
|
||||
"defusedxml==0.7.1",
|
||||
"django==5.1.9",
|
||||
"django==5.1.11",
|
||||
"django-countries==7.6.1",
|
||||
"django-cte==1.3.3",
|
||||
"django-filter==25.1",
|
||||
@@ -32,6 +32,7 @@ dependencies = [
|
||||
"dumb-init==1.2.5.post1",
|
||||
"duo-client==5.5.0",
|
||||
"fido2==2.0.0",
|
||||
"filprofiler>=2024.11.2",
|
||||
"flower==2.0.1",
|
||||
"geoip2==5.1.0",
|
||||
"geopy==2.4.1",
|
||||
@@ -61,7 +62,7 @@ dependencies = [
|
||||
"setproctitle==1.3.6",
|
||||
"structlog==25.3.0",
|
||||
"swagger-spec-validator==3.0.4",
|
||||
"tenant-schemas-celery==4.0.1",
|
||||
"tenant-schemas-celery==3.0.0",
|
||||
"twilio==9.6.1",
|
||||
"ua-parser==1.0.1",
|
||||
"unidecode==1.4.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: authentik
|
||||
version: 2025.4.1
|
||||
version: 2025.6.3
|
||||
description: Making authentication simple.
|
||||
contact:
|
||||
email: hello@goauthentik.io
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 21 KiB |
@@ -2,7 +2,6 @@
|
||||
|
||||
from dataclasses import asdict
|
||||
from time import sleep
|
||||
from unittest.mock import patch
|
||||
|
||||
from guardian.shortcuts import assign_perm
|
||||
from ldap3 import ALL, ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES, SUBTREE, Connection, Server
|
||||
@@ -16,12 +15,10 @@ from authentik.flows.models import Flow
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.outposts.apps import MANAGED_OUTPOST
|
||||
from authentik.outposts.models import Outpost, OutpostConfig, OutpostType
|
||||
from authentik.outposts.tests.test_ws import patched__get_ct_cached
|
||||
from authentik.providers.ldap.models import APIAccessMode, LDAPProvider
|
||||
from tests.e2e.utils import SeleniumTestCase, retry
|
||||
|
||||
|
||||
@patch("guardian.shortcuts._get_ct_cached", patched__get_ct_cached)
|
||||
class TestProviderLDAP(SeleniumTestCase):
|
||||
"""LDAP and Outpost e2e tests"""
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ from json import loads
|
||||
from sys import platform
|
||||
from time import sleep
|
||||
from unittest.case import skip, skipUnless
|
||||
from unittest.mock import patch
|
||||
|
||||
from channels.testing import ChannelsLiveServerTestCase
|
||||
from jwt import decode
|
||||
@@ -18,12 +17,10 @@ from authentik.flows.models import Flow
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.outposts.models import DockerServiceConnection, Outpost, OutpostConfig, OutpostType
|
||||
from authentik.outposts.tasks import outpost_connection_discovery
|
||||
from authentik.outposts.tests.test_ws import patched__get_ct_cached
|
||||
from authentik.providers.proxy.models import ProxyProvider
|
||||
from tests.e2e.utils import SeleniumTestCase, retry
|
||||
|
||||
|
||||
@patch("guardian.shortcuts._get_ct_cached", patched__get_ct_cached)
|
||||
class TestProviderProxy(SeleniumTestCase):
|
||||
"""Proxy and Outpost e2e tests"""
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ from json import loads
|
||||
from pathlib import Path
|
||||
from time import sleep
|
||||
from unittest import skip
|
||||
from unittest.mock import patch
|
||||
|
||||
from selenium.webdriver.common.by import By
|
||||
|
||||
@@ -13,12 +12,10 @@ from authentik.core.models import Application
|
||||
from authentik.flows.models import Flow
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.outposts.models import Outpost, OutpostType
|
||||
from authentik.outposts.tests.test_ws import patched__get_ct_cached
|
||||
from authentik.providers.proxy.models import ProxyMode, ProxyProvider
|
||||
from tests.e2e.utils import SeleniumTestCase, retry
|
||||
|
||||
|
||||
@patch("guardian.shortcuts._get_ct_cached", patched__get_ct_cached)
|
||||
class TestProviderProxyForward(SeleniumTestCase):
|
||||
"""Proxy and Outpost e2e tests"""
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
from dataclasses import asdict
|
||||
from time import sleep
|
||||
from unittest.mock import patch
|
||||
|
||||
from pyrad.client import Client
|
||||
from pyrad.dictionary import Dictionary
|
||||
@@ -13,12 +12,10 @@ from authentik.core.models import Application, User
|
||||
from authentik.flows.models import Flow
|
||||
from authentik.lib.generators import generate_id, generate_key
|
||||
from authentik.outposts.models import Outpost, OutpostConfig, OutpostType
|
||||
from authentik.outposts.tests.test_ws import patched__get_ct_cached
|
||||
from authentik.providers.radius.models import RadiusProvider
|
||||
from tests.e2e.utils import SeleniumTestCase, retry
|
||||
|
||||
|
||||
@patch("guardian.shortcuts._get_ct_cached", patched__get_ct_cached)
|
||||
class TestProviderRadius(SeleniumTestCase):
|
||||
"""Radius Outpost e2e tests"""
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"""authentik e2e testing utilities"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
from collections.abc import Callable
|
||||
from functools import lru_cache, wraps
|
||||
@@ -37,22 +36,12 @@ from authentik.core.api.users import UserSerializer
|
||||
from authentik.core.models import User
|
||||
from authentik.core.tests.utils import create_test_admin_user
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.root.test_runner import get_docker_tag
|
||||
|
||||
IS_CI = "CI" in environ
|
||||
RETRIES = int(environ.get("RETRIES", "3")) if IS_CI else 1
|
||||
|
||||
|
||||
def get_docker_tag() -> str:
|
||||
"""Get docker-tag based off of CI variables"""
|
||||
env_pr_branch = "GITHUB_HEAD_REF"
|
||||
default_branch = "GITHUB_REF"
|
||||
branch_name = os.environ.get(default_branch, "main")
|
||||
if os.environ.get(env_pr_branch, "") != "":
|
||||
branch_name = os.environ[env_pr_branch]
|
||||
branch_name = branch_name.replace("refs/heads/", "").replace("/", "-")
|
||||
return f"gh-{branch_name}"
|
||||
|
||||
|
||||
def get_local_ip() -> str:
|
||||
"""Get the local machine's IP"""
|
||||
hostname = socket.gethostname()
|
||||
|
||||
97
uv.lock
generated
97
uv.lock
generated
@@ -164,7 +164,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "authentik"
|
||||
version = "2025.4.1"
|
||||
version = "2025.6.3"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "argon2-cffi" },
|
||||
@@ -194,6 +194,7 @@ dependencies = [
|
||||
{ name = "dumb-init" },
|
||||
{ name = "duo-client" },
|
||||
{ name = "fido2" },
|
||||
{ name = "filprofiler" },
|
||||
{ name = "flower" },
|
||||
{ name = "geoip2" },
|
||||
{ name = "geopy" },
|
||||
@@ -273,7 +274,7 @@ requires-dist = [
|
||||
{ name = "dacite", specifier = "==1.9.2" },
|
||||
{ name = "deepmerge", specifier = "==2.0" },
|
||||
{ name = "defusedxml", specifier = "==0.7.1" },
|
||||
{ name = "django", specifier = "==5.1.9" },
|
||||
{ name = "django", specifier = "==5.1.11" },
|
||||
{ name = "django-countries", specifier = "==7.6.1" },
|
||||
{ name = "django-cte", specifier = "==1.3.3" },
|
||||
{ name = "django-filter", specifier = "==25.1" },
|
||||
@@ -292,6 +293,7 @@ requires-dist = [
|
||||
{ name = "dumb-init", specifier = "==1.2.5.post1" },
|
||||
{ name = "duo-client", specifier = "==5.5.0" },
|
||||
{ name = "fido2", specifier = "==2.0.0" },
|
||||
{ name = "filprofiler", specifier = ">=2024.11.2" },
|
||||
{ name = "flower", specifier = "==2.0.1" },
|
||||
{ name = "geoip2", specifier = "==5.1.0" },
|
||||
{ name = "geopy", specifier = "==2.4.1" },
|
||||
@@ -321,7 +323,7 @@ requires-dist = [
|
||||
{ name = "setproctitle", specifier = "==1.3.6" },
|
||||
{ name = "structlog", specifier = "==25.3.0" },
|
||||
{ name = "swagger-spec-validator", specifier = "==3.0.4" },
|
||||
{ name = "tenant-schemas-celery", specifier = "==4.0.1" },
|
||||
{ name = "tenant-schemas-celery", specifier = "==3.0.0" },
|
||||
{ name = "twilio", specifier = "==9.6.1" },
|
||||
{ name = "ua-parser", specifier = "==1.0.1" },
|
||||
{ name = "unidecode", specifier = "==1.4.0" },
|
||||
@@ -979,16 +981,16 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "django"
|
||||
version = "5.1.9"
|
||||
version = "5.1.11"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "asgiref" },
|
||||
{ name = "sqlparse" },
|
||||
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/10/08/2e6f05494b3fc0a3c53736846034f882b82ee6351791a7815bbb45715d79/django-5.1.9.tar.gz", hash = "sha256:565881bdd0eb67da36442e9ac788bda90275386b549070d70aee86327781a4fc", size = 10710887, upload-time = "2025-05-07T14:06:45.257Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/83/80/bf0f9b0aa434fca2b46fc6a31c39b08ea714b87a0a72a16566f053fb05a8/django-5.1.11.tar.gz", hash = "sha256:3bcdbd40e4d4623b5e04f59c28834323f3086df583058e65ebce99f9982385ce", size = 10734926, upload-time = "2025-06-10T10:12:48.229Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e1/d1/d8b6b8250b84380d5a123e099ad3298a49407d81598faa13b43a2c6d96d7/django-5.1.9-py3-none-any.whl", hash = "sha256:2fd1d4a0a66a5ba702699eb692e75b0d828b73cc2f4e1fc4b6a854a918967411", size = 8277363, upload-time = "2025-05-07T14:06:37.426Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/91/2972ce330c6c0bd5b3200d4c2ad5cbf47eecff5243220c5a56444d3267a0/django-5.1.11-py3-none-any.whl", hash = "sha256:e48091f364007068728aca938e7450fbfe3f2217079bfd2b8af45122585acf64", size = 8277453, upload-time = "2025-06-10T10:12:42.236Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1273,6 +1275,21 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/7d/a1dba174d7ec4b6b8d6360eed0ac3a4a4e2aa45f234e903592d3184c6c3f/fido2-2.0.0-py3-none-any.whl", hash = "sha256:685f54a50a57e019c6156e2dd699802a603e3abf70bab334f26affdd4fb8d4f7", size = 224761, upload-time = "2025-05-20T09:44:59.029Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filprofiler"
|
||||
version = "2024.11.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "threadpoolctl" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/ea/a9e9f9af45b048f08d5c8b7cfc4cb9b879ef71614b8355f13cf189f901dc/filprofiler-2024.11.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0d8e5355ff0a78afa5b06ce1a2da3cda22edf711e5a4f717da907df083fe4c2e", size = 491090, upload-time = "2024-11-04T00:52:43.772Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/5f/a73a18ac37478413e863e956fe08193156e4a3550e436df26196bcadfa01/filprofiler-2024.11.2-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:07fdd470cb8faae7aa4cc87a5b478ce5c96697ba769195a581f96976200ed0c5", size = 508743, upload-time = "2024-11-04T00:52:45.182Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/73/a64d4ebc66e21121997110f509483b6f27f2b057f17a965107cc241a35e4/filprofiler-2024.11.2-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:93d93a344f9999258b9b13036cff3227e38f3eeecec8646cb7c29441f0f389fd", size = 1472462, upload-time = "2024-11-04T01:37:16.145Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c3/62/81f15b683f62f5b7d4f814fbb06145ef17c164c6540a7174933d8d864c93/filprofiler-2024.11.2-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:f1e05647175bfc83dbadde95e65d4ce3bfe248fff0f64fddf96ebc54c73f34de", size = 1517731, upload-time = "2024-11-04T00:58:40.335Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/65/8d53f2f6d25e62bbe9994a39543942f46ac47fb098fd477c72e5a886a4fe/filprofiler-2024.11.2-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:c662c341f87faf460a94af10c6fc0723295e0341b6ba49e2eae64326698b8ce7", size = 1517999, upload-time = "2024-11-04T00:58:42.203Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flower"
|
||||
version = "2.0.1"
|
||||
@@ -2383,16 +2400,16 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "protobuf"
|
||||
version = "6.30.2"
|
||||
version = "6.31.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c8/8c/cf2ac658216eebe49eaedf1e06bc06cbf6a143469236294a1171a51357c3/protobuf-6.30.2.tar.gz", hash = "sha256:35c859ae076d8c56054c25b59e5e59638d86545ed6e2b6efac6be0b6ea3ba048", size = 429315, upload-time = "2025-03-26T19:12:57.394Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/52/f3/b9655a711b32c19720253f6f06326faf90580834e2e83f840472d752bc8b/protobuf-6.31.1.tar.gz", hash = "sha256:d8cac4c982f0b957a4dc73a80e2ea24fab08e679c0de9deb835f4a12d69aca9a", size = 441797, upload-time = "2025-05-28T19:25:54.947Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/be/85/cd53abe6a6cbf2e0029243d6ae5fb4335da2996f6c177bb2ce685068e43d/protobuf-6.30.2-cp310-abi3-win32.whl", hash = "sha256:b12ef7df7b9329886e66404bef5e9ce6a26b54069d7f7436a0853ccdeb91c103", size = 419148, upload-time = "2025-03-26T19:12:41.359Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/97/e9/7b9f1b259d509aef2b833c29a1f3c39185e2bf21c9c1be1cd11c22cb2149/protobuf-6.30.2-cp310-abi3-win_amd64.whl", hash = "sha256:7653c99774f73fe6b9301b87da52af0e69783a2e371e8b599b3e9cb4da4b12b9", size = 431003, upload-time = "2025-03-26T19:12:44.156Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/66/7f3b121f59097c93267e7f497f10e52ced7161b38295137a12a266b6c149/protobuf-6.30.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:0eb523c550a66a09a0c20f86dd554afbf4d32b02af34ae53d93268c1f73bc65b", size = 417579, upload-time = "2025-03-26T19:12:45.447Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/89/bbb1bff09600e662ad5b384420ad92de61cab2ed0f12ace1fd081fd4c295/protobuf-6.30.2-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:50f32cc9fd9cb09c783ebc275611b4f19dfdfb68d1ee55d2f0c7fa040df96815", size = 317319, upload-time = "2025-03-26T19:12:46.999Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/28/50/1925de813499546bc8ab3ae857e3ec84efe7d2f19b34529d0c7c3d02d11d/protobuf-6.30.2-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4f6c687ae8efae6cf6093389a596548214467778146b7245e886f35e1485315d", size = 316212, upload-time = "2025-03-26T19:12:48.458Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/a1/93c2acf4ade3c5b557d02d500b06798f4ed2c176fa03e3c34973ca92df7f/protobuf-6.30.2-py3-none-any.whl", hash = "sha256:ae86b030e69a98e08c77beab574cbcb9fff6d031d57209f574a5aea1445f4b51", size = 167062, upload-time = "2025-03-26T19:12:55.892Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/6f/6ab8e4bf962fd5570d3deaa2d5c38f0a363f57b4501047b5ebeb83ab1125/protobuf-6.31.1-cp310-abi3-win32.whl", hash = "sha256:7fa17d5a29c2e04b7d90e5e32388b8bfd0e7107cd8e616feef7ed3fa6bdab5c9", size = 423603, upload-time = "2025-05-28T19:25:41.198Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/3a/b15c4347dd4bf3a1b0ee882f384623e2063bb5cf9fa9d57990a4f7df2fb6/protobuf-6.31.1-cp310-abi3-win_amd64.whl", hash = "sha256:426f59d2964864a1a366254fa703b8632dcec0790d8862d30034d8245e1cd447", size = 435283, upload-time = "2025-05-28T19:25:44.275Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/c9/b9689a2a250264a84e66c46d8862ba788ee7a641cdca39bccf64f59284b7/protobuf-6.31.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:6f1227473dc43d44ed644425268eb7c2e488ae245d51c6866d19fe158e207402", size = 425604, upload-time = "2025-05-28T19:25:45.702Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/76/a1/7a5a94032c83375e4fe7e7f56e3976ea6ac90c5e85fac8576409e25c39c3/protobuf-6.31.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:a40fc12b84c154884d7d4c4ebd675d5b3b5283e155f324049ae396b95ddebc39", size = 322115, upload-time = "2025-05-28T19:25:47.128Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/b1/b59d405d64d31999244643d88c45c8241c58f17cc887e73bcb90602327f8/protobuf-6.31.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4ee898bf66f7a8b0bd21bce523814e6fbd8c6add948045ce958b73af7e8878c6", size = 321070, upload-time = "2025-05-28T19:25:50.036Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/af/ab3c51ab7507a7325e98ffe691d9495ee3d3aa5f589afad65ec920d39821/protobuf-6.31.1-py3-none-any.whl", hash = "sha256:720a6c7e6b77288b85063569baae8536671b39f15cc22037ec7045658d80489e", size = 168724, upload-time = "2025-05-28T19:25:53.926Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2776,7 +2793,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.32.3"
|
||||
version = "2.32.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
@@ -2784,9 +2801,9 @@ dependencies = [
|
||||
{ name = "idna" },
|
||||
{ name = "urllib3" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload-time = "2024-05-29T15:37:49.536Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload-time = "2024-05-29T15:37:47.027Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3100,32 +3117,42 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "tenant-schemas-celery"
|
||||
version = "4.0.1"
|
||||
version = "3.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "celery" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/19/f8/cf055bf171b5d83d6fe96f1840fba90d3d274be2b5c35cd21b873302b128/tenant_schemas_celery-4.0.1.tar.gz", hash = "sha256:8b8f055fcd82aa53274c09faf88653a935241518d93b86ab2d43a3df3b70c7f8", size = 18870, upload-time = "2025-04-22T18:23:51.061Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d0/fe/cfe19eb7cc3ad8e39d7df7b7c44414bf665b6ac6660c998eb498f89d16c6/tenant_schemas_celery-3.0.0.tar.gz", hash = "sha256:6be3ae1a5826f262f0f3dd343c6a85a34a1c59b89e04ae37de018f36562fed55", size = 15954, upload-time = "2024-05-19T11:16:41.837Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/a8/fd663c461550d6fedfb24e987acc1557ae5b6615ca08fc6c70dbaaa88aa5/tenant_schemas_celery-4.0.1-py3-none-any.whl", hash = "sha256:d06a3ff6956db3a95168ce2051b7bff2765f9ce0d070e14df92f07a2b60ae0a0", size = 21364, upload-time = "2025-04-22T18:23:49.899Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/db/2c/376e1e641ad08b374c75d896468a7be2e6906ce3621fd0c9f9dc09ff1963/tenant_schemas_celery-3.0.0-py3-none-any.whl", hash = "sha256:ca0f69e78ef698eb4813468231df5a0ab6a660c08e657b65f5ac92e16887eec8", size = 18108, upload-time = "2024-05-19T11:16:39.92Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "threadpoolctl"
|
||||
version = "3.6.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tornado"
|
||||
version = "6.4.2"
|
||||
version = "6.5.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/59/45/a0daf161f7d6f36c3ea5fc0c2de619746cc3dd4c76402e9db545bd920f63/tornado-6.4.2.tar.gz", hash = "sha256:92bad5b4746e9879fd7bf1eb21dce4e3fc5128d71601f80005afa39237ad620b", size = 501135, upload-time = "2024-11-22T03:06:38.036Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/51/89/c72771c81d25d53fe33e3dca61c233b665b2780f21820ba6fd2c6793c12b/tornado-6.5.1.tar.gz", hash = "sha256:84ceece391e8eb9b2b95578db65e920d2a61070260594819589609ba9bc6308c", size = 509934, upload-time = "2025-05-22T18:15:38.788Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/26/7e/71f604d8cea1b58f82ba3590290b66da1e72d840aeb37e0d5f7291bd30db/tornado-6.4.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e828cce1123e9e44ae2a50a9de3055497ab1d0aeb440c5ac23064d9e44880da1", size = 436299, upload-time = "2024-11-22T03:06:20.162Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/44/87543a3b99016d0bf54fdaab30d24bf0af2e848f1d13d34a3a5380aabe16/tornado-6.4.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:072ce12ada169c5b00b7d92a99ba089447ccc993ea2143c9ede887e0937aa803", size = 434253, upload-time = "2024-11-22T03:06:22.39Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/fb/fdf679b4ce51bcb7210801ef4f11fdac96e9885daa402861751353beea6e/tornado-6.4.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a017d239bd1bb0919f72af256a970624241f070496635784d9bf0db640d3fec", size = 437602, upload-time = "2024-11-22T03:06:24.214Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/3b/e31aeffffc22b475a64dbeb273026a21b5b566f74dee48742817626c47dc/tornado-6.4.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c36e62ce8f63409301537222faffcef7dfc5284f27eec227389f2ad11b09d946", size = 436972, upload-time = "2024-11-22T03:06:25.559Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/55/b78a464de78051a30599ceb6983b01d8f732e6f69bf37b4ed07f642ac0fc/tornado-6.4.2-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca9eb02196e789c9cb5c3c7c0f04fb447dc2adffd95265b2c7223a8a615ccbf", size = 437173, upload-time = "2024-11-22T03:06:27.584Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/5e/be4fb0d1684eb822c9a62fb18a3e44a06188f78aa466b2ad991d2ee31104/tornado-6.4.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:304463bd0772442ff4d0f5149c6f1c2135a1fae045adf070821c6cdc76980634", size = 437892, upload-time = "2024-11-22T03:06:28.933Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/33/4f91fdd94ea36e1d796147003b490fe60a0215ac5737b6f9c65e160d4fe0/tornado-6.4.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:c82c46813ba483a385ab2a99caeaedf92585a1f90defb5693351fa7e4ea0bf73", size = 437334, upload-time = "2024-11-22T03:06:30.428Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/ae/c1b22d4524b0e10da2f29a176fb2890386f7bd1f63aacf186444873a88a0/tornado-6.4.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:932d195ca9015956fa502c6b56af9eb06106140d844a335590c1ec7f5277d10c", size = 437261, upload-time = "2024-11-22T03:06:32.458Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/25/36dbd49ab6d179bcfc4c6c093a51795a4f3bed380543a8242ac3517a1751/tornado-6.4.2-cp38-abi3-win32.whl", hash = "sha256:2876cef82e6c5978fde1e0d5b1f919d756968d5b4282418f3146b79b58556482", size = 438463, upload-time = "2024-11-22T03:06:34.71Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/cc/58b1adeb1bb46228442081e746fcdbc4540905c87e8add7c277540934edb/tornado-6.4.2-cp38-abi3-win_amd64.whl", hash = "sha256:908b71bf3ff37d81073356a5fadcc660eb10c1476ee6e2725588626ce7e5ca38", size = 438907, upload-time = "2024-11-22T03:06:36.71Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/89/f4532dee6843c9e0ebc4e28d4be04c67f54f60813e4bf73d595fe7567452/tornado-6.5.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d50065ba7fd11d3bd41bcad0825227cc9a95154bad83239357094c36708001f7", size = 441948, upload-time = "2025-05-22T18:15:20.862Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/9a/557406b62cffa395d18772e0cdcf03bed2fff03b374677348eef9f6a3792/tornado-6.5.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9e9ca370f717997cb85606d074b0e5b247282cf5e2e1611568b8821afe0342d6", size = 440112, upload-time = "2025-05-22T18:15:22.591Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/55/82/7721b7319013a3cf881f4dffa4f60ceff07b31b394e459984e7a36dc99ec/tornado-6.5.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b77e9dfa7ed69754a54c89d82ef746398be82f749df69c4d3abe75c4d1ff4888", size = 443672, upload-time = "2025-05-22T18:15:24.027Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/42/d11c4376e7d101171b94e03cef0cbce43e823ed6567ceda571f54cf6e3ce/tornado-6.5.1-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:253b76040ee3bab8bcf7ba9feb136436a3787208717a1fb9f2c16b744fba7331", size = 443019, upload-time = "2025-05-22T18:15:25.735Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/f7/0c48ba992d875521ac761e6e04b0a1750f8150ae42ea26df1852d6a98942/tornado-6.5.1-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:308473f4cc5a76227157cdf904de33ac268af770b2c5f05ca6c1161d82fdd95e", size = 443252, upload-time = "2025-05-22T18:15:27.499Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/46/d8d7413d11987e316df4ad42e16023cd62666a3c0dfa1518ffa30b8df06c/tornado-6.5.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:caec6314ce8a81cf69bd89909f4b633b9f523834dc1a352021775d45e51d9401", size = 443930, upload-time = "2025-05-22T18:15:29.299Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/b2/f8049221c96a06df89bed68260e8ca94beca5ea532ffc63b1175ad31f9cc/tornado-6.5.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:13ce6e3396c24e2808774741331638ee6c2f50b114b97a55c5b442df65fd9692", size = 443351, upload-time = "2025-05-22T18:15:31.038Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/76/ff/6a0079e65b326cc222a54720a748e04a4db246870c4da54ece4577bfa702/tornado-6.5.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5cae6145f4cdf5ab24744526cc0f55a17d76f02c98f4cff9daa08ae9a217448a", size = 443328, upload-time = "2025-05-22T18:15:32.426Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/18/e3f902a1d21f14035b5bc6246a8c0f51e0eef562ace3a2cea403c1fb7021/tornado-6.5.1-cp39-abi3-win32.whl", hash = "sha256:e0a36e1bc684dca10b1aa75a31df8bdfed656831489bc1e6a6ebed05dc1ec365", size = 444396, upload-time = "2025-05-22T18:15:34.205Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/09/6526e32bf1049ee7de3bebba81572673b19a2a8541f795d887e92af1a8bc/tornado-6.5.1-cp39-abi3-win_amd64.whl", hash = "sha256:908e7d64567cecd4c2b458075589a775063453aeb1d2a1853eedb806922f568b", size = 444840, upload-time = "2025-05-22T18:15:36.1Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/55/a7/535c44c7bea4578e48281d83c615219f3ab19e6abc67625ef637c73987be/tornado-6.5.1-cp39-abi3-win_arm64.whl", hash = "sha256:02420a0eb7bf617257b9935e2b754d1b63897525d8a289c9d65690d580b4dcf7", size = 443596, upload-time = "2025-05-22T18:15:37.433Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3287,11 +3314,11 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.4.0"
|
||||
version = "2.5.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672, upload-time = "2025-04-10T15:23:39.232Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680, upload-time = "2025-04-10T15:23:37.377Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
|
||||
@@ -85,8 +85,8 @@ export class AdminOverviewPage extends AdminOverviewBase {
|
||||
render(): TemplateResult {
|
||||
const username = this.user?.user.name || this.user?.user.username;
|
||||
|
||||
return html` <ak-page-header
|
||||
header=${msg(str`Welcome, ${username || ""}.`)}
|
||||
return html`<ak-page-header
|
||||
header=${this.user ? msg(str`Welcome, ${username || ""}.`) : msg("Welcome.")}
|
||||
description=${msg("General system status")}
|
||||
?hasIcon=${false}
|
||||
>
|
||||
|
||||
@@ -361,7 +361,7 @@ export class LDAPSourceForm extends BaseSourceForm<LDAPSource> {
|
||||
<p class="pf-c-form__helper-text">${placeholderHelperText}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Addition User DN")}
|
||||
label=${msg("Additional User DN")}
|
||||
name="additionalUserDn"
|
||||
>
|
||||
<input
|
||||
@@ -374,7 +374,7 @@ export class LDAPSourceForm extends BaseSourceForm<LDAPSource> {
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Addition Group DN")}
|
||||
label=${msg("Additional Group DN")}
|
||||
name="additionalGroupDn"
|
||||
>
|
||||
<input
|
||||
|
||||
@@ -201,6 +201,10 @@ select[multiple] option:checked {
|
||||
--pf-c-input-group--BackgroundColor: transparent;
|
||||
}
|
||||
|
||||
select.pf-c-form-control {
|
||||
--pf-c-form-control__select--BackgroundUrl: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 512'%3E%3Cpath fill='%23fafafa' d='M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.pf-c-form-control {
|
||||
--pf-c-form-control--BorderTopColor: transparent !important;
|
||||
--pf-c-form-control--BorderRightColor: transparent !important;
|
||||
|
||||
@@ -374,7 +374,7 @@ ${JSON.stringify(value.new_value, null, 4)}</pre
|
||||
|
||||
renderEmailSent() {
|
||||
let body = this.event.context.body as string;
|
||||
body = body.replace("cid:logo.png", "/static/dist/assets/icons/icon_left_brand.png");
|
||||
body = body.replace("cid:logo", "/static/dist/assets/icons/icon_left_brand.png");
|
||||
return html`<div class="pf-c-card__title">${msg("Email info:")}</div>
|
||||
<div class="pf-c-card__body">${this.getEmailInfo(this.event.context)}</div>
|
||||
<ak-expand>
|
||||
|
||||
@@ -147,7 +147,7 @@ export class AKPageNavbar
|
||||
}
|
||||
|
||||
.accent-icon {
|
||||
height: 1em;
|
||||
height: 1.2em;
|
||||
width: 1em;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
@@ -157,6 +157,7 @@ export class AKPageNavbar
|
||||
}
|
||||
|
||||
&.page-description {
|
||||
padding-top: 0.3em;
|
||||
grid-area: description;
|
||||
margin-block-end: var(--pf-global--spacer--md);
|
||||
|
||||
|
||||
@@ -123,6 +123,9 @@ export class AKElement extends LitElement {
|
||||
applyUITheme(nextStyleRoot, UiThemeEnum.Dark, this.#customCSSStyleSheet);
|
||||
|
||||
this.activeTheme = UiThemeEnum.Dark;
|
||||
} else if (this.preferredColorScheme === "light") {
|
||||
applyUITheme(nextStyleRoot, UiThemeEnum.Light, this.#customCSSStyleSheet);
|
||||
this.activeTheme = UiThemeEnum.Light;
|
||||
} else if (this.preferredColorScheme === "auto") {
|
||||
createUIThemeEffect(
|
||||
(nextUITheme) => {
|
||||
|
||||
@@ -32,8 +32,8 @@ import {
|
||||
} from "./types.js";
|
||||
|
||||
function localeComparator(a: DualSelectPair, b: DualSelectPair) {
|
||||
const aSortBy = a[2];
|
||||
const bSortBy = b[2];
|
||||
const aSortBy = String(a[2] || a[0]);
|
||||
const bSortBy = String(b[2] || b[0]);
|
||||
|
||||
return aSortBy.localeCompare(bSortBy);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export type DualSelectPair<T = unknown> = [
|
||||
/**
|
||||
* A string to sort by. If not provided, the key will be used.
|
||||
*/
|
||||
sortBy: string,
|
||||
sortBy?: string,
|
||||
/**
|
||||
* A local mapping of the key to the object. This is used by some specific apps.
|
||||
*
|
||||
|
||||
@@ -17,7 +17,7 @@ import "@goauthentik/elements/table/TableSearch";
|
||||
import { SlottedTemplateResult } from "@goauthentik/elements/types";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, css, html, nothing } from "lit";
|
||||
import { CSSResult, PropertyValues, TemplateResult, css, html, nothing } from "lit";
|
||||
import { property, state } from "lit/decorators.js";
|
||||
import { classMap } from "lit/directives/class-map.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
@@ -107,6 +107,9 @@ export abstract class Table<T> extends AKElement implements TableLike {
|
||||
|
||||
private isLoading = false;
|
||||
|
||||
#pageParam = `${this.tagName.toLowerCase()}-page`;
|
||||
#searchParam = `${this.tagName.toLowerCase()}-search`;
|
||||
|
||||
searchEnabled(): boolean {
|
||||
return false;
|
||||
}
|
||||
@@ -123,7 +126,7 @@ export abstract class Table<T> extends AKElement implements TableLike {
|
||||
data?: PaginatedResponse<T>;
|
||||
|
||||
@property({ type: Number })
|
||||
page = getURLParam("tablePage", 1);
|
||||
page = getURLParam(this.#pageParam, 1);
|
||||
|
||||
/**
|
||||
* Set if your `selectedElements` use of the selection box is to enable bulk-delete,
|
||||
@@ -209,7 +212,7 @@ export abstract class Table<T> extends AKElement implements TableLike {
|
||||
await this.fetch();
|
||||
});
|
||||
if (this.searchEnabled()) {
|
||||
this.search = getURLParam("search", "");
|
||||
this.search = getURLParam(this.#searchParam, "");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -462,12 +465,23 @@ export abstract class Table<T> extends AKElement implements TableLike {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
protected willUpdate(changedProperties: PropertyValues<this>): void {
|
||||
if (changedProperties.has("page")) {
|
||||
updateURLParams({
|
||||
[this.#pageParam]: changedProperties.get("page"),
|
||||
});
|
||||
}
|
||||
if (changedProperties.has("search")) {
|
||||
updateURLParams({
|
||||
[this.#searchParam]: changedProperties.get("search"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderSearch(): TemplateResult {
|
||||
const runSearch = (value: string) => {
|
||||
this.search = value;
|
||||
updateURLParams({
|
||||
search: value,
|
||||
});
|
||||
this.page = 1;
|
||||
this.fetch();
|
||||
};
|
||||
|
||||
@@ -548,7 +562,6 @@ export abstract class Table<T> extends AKElement implements TableLike {
|
||||
/* A simple pagination display, shown at both the top and bottom of the page. */
|
||||
renderTablePagination(): TemplateResult {
|
||||
const handler = (page: number) => {
|
||||
updateURLParams({ tablePage: page });
|
||||
this.page = page;
|
||||
this.fetch();
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import { updateURLParams } from "#elements/router/RouteMatch";
|
||||
import { Table } from "#elements/table/Table";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult } from "lit";
|
||||
import { CSSResult, nothing } from "lit";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
@@ -44,7 +44,7 @@ export abstract class TablePage<T> extends Table<T> {
|
||||
? inner
|
||||
: html`<ak-empty-state icon=${this.pageIcon()} header="${msg("No objects found.")}">
|
||||
<div slot="body">
|
||||
${this.searchEnabled() ? this.renderEmptyClearSearch() : html``}
|
||||
${this.searchEnabled() ? this.renderEmptyClearSearch() : nothing}
|
||||
</div>
|
||||
<div slot="primary">${this.renderObjectCreate()}</div>
|
||||
</ak-empty-state>`}
|
||||
@@ -60,9 +60,7 @@ export abstract class TablePage<T> extends Table<T> {
|
||||
this.search = "";
|
||||
this.requestUpdate();
|
||||
this.fetch();
|
||||
updateURLParams({
|
||||
search: "",
|
||||
});
|
||||
this.page = 1;
|
||||
}}
|
||||
class="pf-c-button pf-m-link"
|
||||
>
|
||||
|
||||
@@ -11,7 +11,7 @@ import { StageHost } from "#flow/stages/base";
|
||||
import "#user/user-settings/details/stages/prompt/PromptStage";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, PropertyValues, TemplateResult, html } from "lit";
|
||||
import { CSSResult, TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
||||
|
||||
@@ -36,7 +36,7 @@ export class UserSettingsFlowExecutor
|
||||
implements StageHost
|
||||
{
|
||||
@property()
|
||||
flowSlug?: string;
|
||||
flowSlug = this.brand?.flowUserSettings;
|
||||
|
||||
private _challenge?: ChallengeTypes;
|
||||
|
||||
@@ -86,12 +86,15 @@ export class UserSettingsFlowExecutor
|
||||
});
|
||||
}
|
||||
|
||||
updated(changedProperties: PropertyValues<this>): void {
|
||||
if (changedProperties.has("brand") && this.brand) {
|
||||
firstUpdated() {
|
||||
if (this.flowSlug) {
|
||||
this.nextChallenge();
|
||||
}
|
||||
}
|
||||
|
||||
updated(): void {
|
||||
if (!this.flowSlug && this.brand?.flowUserSettings) {
|
||||
this.flowSlug = this.brand.flowUserSettings;
|
||||
|
||||
if (!this.flowSlug) return;
|
||||
|
||||
this.nextChallenge();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9106,9 +9106,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="sc7524ea24eeeb019">
|
||||
<source>Failed to preview prompt</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s783964a224796865">
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'. When selecting 'Lookup using a user attribute', this should be a user attribute, otherwise a group attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1d47b4f61ca53e8e">
|
||||
<source>Lookup using user attribute</source>
|
||||
</trans-unit>
|
||||
@@ -9246,6 +9243,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se3b26b762110bda0">
|
||||
<source>Delete authentik users and groups which were previously supplied by this source, but are now missing from it.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0a2cb398b54a6207">
|
||||
<source>Welcome.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4e1d2cb86cf5ecd0">
|
||||
<source>Field which contains members of a group. The value of this field is matched against User membership attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6478025f3e0174fa">
|
||||
<source>User membership attribute</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s344be99cf5d36407">
|
||||
<source>Attribute which matches the value of Group membership field.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -7608,9 +7608,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="sc7524ea24eeeb019">
|
||||
<source>Failed to preview prompt</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s783964a224796865">
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'. When selecting 'Lookup using a user attribute', this should be a user attribute, otherwise a group attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1d47b4f61ca53e8e">
|
||||
<source>Lookup using user attribute</source>
|
||||
</trans-unit>
|
||||
@@ -7748,6 +7745,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se3b26b762110bda0">
|
||||
<source>Delete authentik users and groups which were previously supplied by this source, but are now missing from it.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0a2cb398b54a6207">
|
||||
<source>Welcome.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4e1d2cb86cf5ecd0">
|
||||
<source>Field which contains members of a group. The value of this field is matched against User membership attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6478025f3e0174fa">
|
||||
<source>User membership attribute</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s344be99cf5d36407">
|
||||
<source>Attribute which matches the value of Group membership field.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -9167,9 +9167,6 @@ Las vinculaciones a grupos o usuarios se comparan con el usuario del evento.</ta
|
||||
<trans-unit id="sc7524ea24eeeb019">
|
||||
<source>Failed to preview prompt</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s783964a224796865">
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'. When selecting 'Lookup using a user attribute', this should be a user attribute, otherwise a group attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1d47b4f61ca53e8e">
|
||||
<source>Lookup using user attribute</source>
|
||||
</trans-unit>
|
||||
@@ -9307,6 +9304,18 @@ Las vinculaciones a grupos o usuarios se comparan con el usuario del evento.</ta
|
||||
</trans-unit>
|
||||
<trans-unit id="se3b26b762110bda0">
|
||||
<source>Delete authentik users and groups which were previously supplied by this source, but are now missing from it.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0a2cb398b54a6207">
|
||||
<source>Welcome.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4e1d2cb86cf5ecd0">
|
||||
<source>Field which contains members of a group. The value of this field is matched against User membership attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6478025f3e0174fa">
|
||||
<source>User membership attribute</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s344be99cf5d36407">
|
||||
<source>Attribute which matches the value of Group membership field.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -9690,10 +9690,6 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
|
||||
<source>Failed to preview prompt</source>
|
||||
<target>Échec de la prévisualisation de l'invite</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s783964a224796865">
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'. When selecting 'Lookup using a user attribute', this should be a user attribute, otherwise a group attribute.</source>
|
||||
<target>Champ qui contient les membres d'un groupe. Si vous utilisez le champ "memberUid", la valeur est censée contenir un nom distinctif relatif, par exemple 'memberUid=un-utilisateur' au lieu de 'memberUid=cn=un-utilisateur,ou=groups,...'. Lorsque "Recherche avec un attribut utilisateur" est sélectionné, cet attribut doit être un attribut utilisateur, sinon un attribut de groupe.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1d47b4f61ca53e8e">
|
||||
<source>Lookup using user attribute</source>
|
||||
<target>Recherche avec un attribut utilisateur</target>
|
||||
@@ -9877,6 +9873,18 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
|
||||
<trans-unit id="se3b26b762110bda0">
|
||||
<source>Delete authentik users and groups which were previously supplied by this source, but are now missing from it.</source>
|
||||
<target>Supprimer les utilisateurs et les groupes authentik qui étaient auparavant fournis par cette source, mais qui en sont maintenant absents.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0a2cb398b54a6207">
|
||||
<source>Welcome.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4e1d2cb86cf5ecd0">
|
||||
<source>Field which contains members of a group. The value of this field is matched against User membership attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6478025f3e0174fa">
|
||||
<source>User membership attribute</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s344be99cf5d36407">
|
||||
<source>Attribute which matches the value of Group membership field.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -9690,10 +9690,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>Failed to preview prompt</source>
|
||||
<target>Impossibile visualizzare l'anteprima del prompt</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s783964a224796865">
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'. When selecting 'Lookup using a user attribute', this should be a user attribute, otherwise a group attribute.</source>
|
||||
<target>Campo che contiene i membri di un gruppo. Si noti che se si utilizza il campo "memberUid", si presume che il valore contenga un nome relativo distinto. Ad esempio, "memberUid=some-user" invece di "memberUid=cn=some-user,ou=groups,...". Quando si seleziona "Cerca utilizzando un attributo utente", questo dovrebbe essere un attributo utente, altrimenti un attributo di gruppo.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1d47b4f61ca53e8e">
|
||||
<source>Lookup using user attribute</source>
|
||||
<target>Ricerca tramite attributo utente</target>
|
||||
@@ -9860,6 +9856,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se3b26b762110bda0">
|
||||
<source>Delete authentik users and groups which were previously supplied by this source, but are now missing from it.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0a2cb398b54a6207">
|
||||
<source>Welcome.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4e1d2cb86cf5ecd0">
|
||||
<source>Field which contains members of a group. The value of this field is matched against User membership attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6478025f3e0174fa">
|
||||
<source>User membership attribute</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s344be99cf5d36407">
|
||||
<source>Attribute which matches the value of Group membership field.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -9075,9 +9075,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="sc7524ea24eeeb019">
|
||||
<source>Failed to preview prompt</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s783964a224796865">
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'. When selecting 'Lookup using a user attribute', this should be a user attribute, otherwise a group attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1d47b4f61ca53e8e">
|
||||
<source>Lookup using user attribute</source>
|
||||
</trans-unit>
|
||||
@@ -9215,6 +9212,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se3b26b762110bda0">
|
||||
<source>Delete authentik users and groups which were previously supplied by this source, but are now missing from it.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0a2cb398b54a6207">
|
||||
<source>Welcome.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4e1d2cb86cf5ecd0">
|
||||
<source>Field which contains members of a group. The value of this field is matched against User membership attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6478025f3e0174fa">
|
||||
<source>User membership attribute</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s344be99cf5d36407">
|
||||
<source>Attribute which matches the value of Group membership field.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -8977,9 +8977,6 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
|
||||
<trans-unit id="sc7524ea24eeeb019">
|
||||
<source>Failed to preview prompt</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s783964a224796865">
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'. When selecting 'Lookup using a user attribute', this should be a user attribute, otherwise a group attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1d47b4f61ca53e8e">
|
||||
<source>Lookup using user attribute</source>
|
||||
</trans-unit>
|
||||
@@ -9117,6 +9114,18 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
|
||||
</trans-unit>
|
||||
<trans-unit id="se3b26b762110bda0">
|
||||
<source>Delete authentik users and groups which were previously supplied by this source, but are now missing from it.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0a2cb398b54a6207">
|
||||
<source>Welcome.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4e1d2cb86cf5ecd0">
|
||||
<source>Field which contains members of a group. The value of this field is matched against User membership attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6478025f3e0174fa">
|
||||
<source>User membership attribute</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s344be99cf5d36407">
|
||||
<source>Attribute which matches the value of Group membership field.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -9402,9 +9402,6 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
|
||||
<trans-unit id="sc7524ea24eeeb019">
|
||||
<source>Failed to preview prompt</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s783964a224796865">
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'. When selecting 'Lookup using a user attribute', this should be a user attribute, otherwise a group attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1d47b4f61ca53e8e">
|
||||
<source>Lookup using user attribute</source>
|
||||
</trans-unit>
|
||||
@@ -9542,6 +9539,18 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
|
||||
</trans-unit>
|
||||
<trans-unit id="se3b26b762110bda0">
|
||||
<source>Delete authentik users and groups which were previously supplied by this source, but are now missing from it.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0a2cb398b54a6207">
|
||||
<source>Welcome.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4e1d2cb86cf5ecd0">
|
||||
<source>Field which contains members of a group. The value of this field is matched against User membership attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6478025f3e0174fa">
|
||||
<source>User membership attribute</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s344be99cf5d36407">
|
||||
<source>Attribute which matches the value of Group membership field.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -9409,9 +9409,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="sc7524ea24eeeb019">
|
||||
<source>Failed to preview prompt</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s783964a224796865">
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'. When selecting 'Lookup using a user attribute', this should be a user attribute, otherwise a group attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1d47b4f61ca53e8e">
|
||||
<source>Lookup using user attribute</source>
|
||||
</trans-unit>
|
||||
@@ -9550,4 +9547,16 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="se3b26b762110bda0">
|
||||
<source>Delete authentik users and groups which were previously supplied by this source, but are now missing from it.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0a2cb398b54a6207">
|
||||
<source>Welcome.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4e1d2cb86cf5ecd0">
|
||||
<source>Field which contains members of a group. The value of this field is matched against User membership attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6478025f3e0174fa">
|
||||
<source>User membership attribute</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s344be99cf5d36407">
|
||||
<source>Attribute which matches the value of Group membership field.</source>
|
||||
</trans-unit>
|
||||
</body></file></xliff>
|
||||
|
||||
@@ -9494,9 +9494,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="sc7524ea24eeeb019">
|
||||
<source>Failed to preview prompt</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s783964a224796865">
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'. When selecting 'Lookup using a user attribute', this should be a user attribute, otherwise a group attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1d47b4f61ca53e8e">
|
||||
<source>Lookup using user attribute</source>
|
||||
</trans-unit>
|
||||
@@ -9634,6 +9631,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se3b26b762110bda0">
|
||||
<source>Delete authentik users and groups which were previously supplied by this source, but are now missing from it.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0a2cb398b54a6207">
|
||||
<source>Welcome.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4e1d2cb86cf5ecd0">
|
||||
<source>Field which contains members of a group. The value of this field is matched against User membership attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6478025f3e0174fa">
|
||||
<source>User membership attribute</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s344be99cf5d36407">
|
||||
<source>Attribute which matches the value of Group membership field.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -9465,9 +9465,6 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
|
||||
<trans-unit id="sc7524ea24eeeb019">
|
||||
<source>Failed to preview prompt</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s783964a224796865">
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'. When selecting 'Lookup using a user attribute', this should be a user attribute, otherwise a group attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1d47b4f61ca53e8e">
|
||||
<source>Lookup using user attribute</source>
|
||||
</trans-unit>
|
||||
@@ -9605,6 +9602,18 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
|
||||
</trans-unit>
|
||||
<trans-unit id="se3b26b762110bda0">
|
||||
<source>Delete authentik users and groups which were previously supplied by this source, but are now missing from it.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0a2cb398b54a6207">
|
||||
<source>Welcome.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4e1d2cb86cf5ecd0">
|
||||
<source>Field which contains members of a group. The value of this field is matched against User membership attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6478025f3e0174fa">
|
||||
<source>User membership attribute</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s344be99cf5d36407">
|
||||
<source>Attribute which matches the value of Group membership field.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -6215,9 +6215,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="sc7524ea24eeeb019">
|
||||
<source>Failed to preview prompt</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s783964a224796865">
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'. When selecting 'Lookup using a user attribute', this should be a user attribute, otherwise a group attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1d47b4f61ca53e8e">
|
||||
<source>Lookup using user attribute</source>
|
||||
</trans-unit>
|
||||
@@ -6356,6 +6353,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="se3b26b762110bda0">
|
||||
<source>Delete authentik users and groups which were previously supplied by this source, but are now missing from it.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0a2cb398b54a6207">
|
||||
<source>Welcome.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4e1d2cb86cf5ecd0">
|
||||
<source>Field which contains members of a group. The value of this field is matched against User membership attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6478025f3e0174fa">
|
||||
<source>User membership attribute</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s344be99cf5d36407">
|
||||
<source>Attribute which matches the value of Group membership field.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" ?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
|
||||
<?xml version="1.0"?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
|
||||
<file target-language="zh-Hans" source-language="en" original="lit-localize-inputs" datatype="plaintext">
|
||||
<body>
|
||||
<trans-unit id="s4caed5b7a7e5d89b">
|
||||
@@ -596,9 +596,9 @@
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="saa0e2675da69651b">
|
||||
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
|
||||
<target>未找到 URL "
|
||||
<x id="0" equiv-text="${this.url}"/>"。</target>
|
||||
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
|
||||
<target>未找到 URL "
|
||||
<x id="0" equiv-text="${this.url}"/>"。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s58cd9c2fe836d9c6">
|
||||
@@ -1715,8 +1715,8 @@
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sa90b7809586c35ce">
|
||||
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
|
||||
<target>输入完整 URL、相对路径,或者使用 'fa://fa-test' 来使用 Font Awesome 图标 "fa-test"。</target>
|
||||
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
|
||||
<target>输入完整 URL、相对路径,或者使用 'fa://fa-test' 来使用 Font Awesome 图标 "fa-test"。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s0410779cb47de312">
|
||||
@@ -3778,10 +3778,10 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sa95a538bfbb86111">
|
||||
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
|
||||
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
|
||||
<target>您确定要更新
|
||||
<x id="0" equiv-text="${this.objectLabel}"/>"
|
||||
<x id="1" equiv-text="${this.obj?.name}"/>" 吗?</target>
|
||||
<x id="0" equiv-text="${this.objectLabel}"/>"
|
||||
<x id="1" equiv-text="${this.obj?.name}"/>" 吗?</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sc92d7cfb6ee1fec6">
|
||||
@@ -4847,7 +4847,7 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sdf1d8edef27236f0">
|
||||
<source>A "roaming" authenticator, like a YubiKey</source>
|
||||
<source>A "roaming" authenticator, like a YubiKey</source>
|
||||
<target>像 YubiKey 这样的“漫游”身份验证器</target>
|
||||
|
||||
</trans-unit>
|
||||
@@ -5206,7 +5206,7 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s1608b2f94fa0dbd4">
|
||||
<source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
|
||||
<source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
|
||||
<target>如果设置时长大于 0,用户可以选择“保持登录”选项,这将使用户的会话延长此处设置的时间。</target>
|
||||
|
||||
</trans-unit>
|
||||
@@ -7492,7 +7492,7 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<target>成功创建用户并添加到组 <x id="0" equiv-text="${this.group.name}"/></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s824e0943a7104668">
|
||||
<source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
|
||||
<source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
|
||||
<target>此用户将会被添加到组 &quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&quot;。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s62e7f6ed7d9cb3ca">
|
||||
@@ -8778,7 +8778,7 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<target>同步组</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2d5f69929bb7221d">
|
||||
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
|
||||
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
|
||||
<target><x id="0" equiv-text="${p.name}"/>(&quot;<x id="1" equiv-text="${p.fieldKey}"/>&quot;,类型为 <x id="2" equiv-text="${p.type}"/>)</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s25bacc19d98b444e">
|
||||
@@ -9026,8 +9026,8 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<target>授权流程成功后有效的重定向 URI。还可以在此处为隐式流程指定任何来源。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4c49d27de60a532b">
|
||||
<source>To allow any redirect URI, set the mode to Regex and the value to ".*". Be aware of the possible security implications this can have.</source>
|
||||
<target>要允许任何重定向 URI,请设置模式为正则表达式,并将此值设置为 ".*"。请注意这可能带来的安全影响。</target>
|
||||
<source>To allow any redirect URI, set the mode to Regex and the value to ".*". Be aware of the possible security implications this can have.</source>
|
||||
<target>要允许任何重定向 URI,请设置模式为正则表达式,并将此值设置为 ".*"。请注意这可能带来的安全影响。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa52bf79fe1ccb13e">
|
||||
<source>Federated OIDC Sources</source>
|
||||
@@ -9691,10 +9691,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>Failed to preview prompt</source>
|
||||
<target>预览输入失败</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s783964a224796865">
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'. When selecting 'Lookup using a user attribute', this should be a user attribute, otherwise a group attribute.</source>
|
||||
<target>包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...'。当选中“使用用户属性查询”时,此配置应该为用户属性,否则为组属性。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1d47b4f61ca53e8e">
|
||||
<source>Lookup using user attribute</source>
|
||||
<target>使用用户属性查询</target>
|
||||
@@ -9784,7 +9780,7 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<target>在 authorization_code 令牌请求流程期间,如何执行身份验证</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s844baf19a6c4a9b4">
|
||||
<source>Enable "Remember me on this device"</source>
|
||||
<source>Enable "Remember me on this device"</source>
|
||||
<target>启用“在此设备上记住我”</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sfa72bca733f40692">
|
||||
@@ -9878,7 +9874,19 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="se3b26b762110bda0">
|
||||
<source>Delete authentik users and groups which were previously supplied by this source, but are now missing from it.</source>
|
||||
<target>删除之前由此源提供,但现已缺失的用户和组。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0a2cb398b54a6207">
|
||||
<source>Welcome.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4e1d2cb86cf5ecd0">
|
||||
<source>Field which contains members of a group. The value of this field is matched against User membership attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6478025f3e0174fa">
|
||||
<source>User membership attribute</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s344be99cf5d36407">
|
||||
<source>Attribute which matches the value of Group membership field.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
</xliff>
|
||||
|
||||
@@ -7308,9 +7308,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="sc7524ea24eeeb019">
|
||||
<source>Failed to preview prompt</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s783964a224796865">
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'. When selecting 'Lookup using a user attribute', this should be a user attribute, otherwise a group attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1d47b4f61ca53e8e">
|
||||
<source>Lookup using user attribute</source>
|
||||
</trans-unit>
|
||||
@@ -7448,6 +7445,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se3b26b762110bda0">
|
||||
<source>Delete authentik users and groups which were previously supplied by this source, but are now missing from it.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0a2cb398b54a6207">
|
||||
<source>Welcome.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4e1d2cb86cf5ecd0">
|
||||
<source>Field which contains members of a group. The value of this field is matched against User membership attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6478025f3e0174fa">
|
||||
<source>User membership attribute</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s344be99cf5d36407">
|
||||
<source>Attribute which matches the value of Group membership field.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -9052,9 +9052,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="sc7524ea24eeeb019">
|
||||
<source>Failed to preview prompt</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s783964a224796865">
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'. When selecting 'Lookup using a user attribute', this should be a user attribute, otherwise a group attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1d47b4f61ca53e8e">
|
||||
<source>Lookup using user attribute</source>
|
||||
</trans-unit>
|
||||
@@ -9192,6 +9189,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se3b26b762110bda0">
|
||||
<source>Delete authentik users and groups which were previously supplied by this source, but are now missing from it.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0a2cb398b54a6207">
|
||||
<source>Welcome.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4e1d2cb86cf5ecd0">
|
||||
<source>Field which contains members of a group. The value of this field is matched against User membership attribute.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6478025f3e0174fa">
|
||||
<source>User membership attribute</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s344be99cf5d36407">
|
||||
<source>Attribute which matches the value of Group membership field.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Endpoint Authenticator Google Device Trust Connector Stage
|
||||
title: Google Chrome Device Trust Authenticator Stage
|
||||
authentik_version: "2024.10"
|
||||
authentik_preview: true
|
||||
authentik_enterprise: true
|
||||
@@ -7,6 +7,12 @@ authentik_enterprise: true
|
||||
|
||||
With this stage, authentik can validate users' Chrome browsers and ensure that users' devices are compliant and up-to-date.
|
||||
|
||||
Support for the Chrome Enterprise Device Trust connector allows organizations to integrate Chrome browsers and ChromeOS devices with authentik as the Identity Provider (IdP), to strengthen their overall security posture.
|
||||
|
||||
Device Trust is particularly important in environments with many different device types that are used by a large, remote workforce that might have a BYOD (Bring Your Own Device) policy, or have large teams of of contractors, temporary workers, or volunteers.
|
||||
|
||||
With Device Trust you can enable "context-aware" access policies; for example a policy might require that a device has all security patches installed.
|
||||
|
||||
:::info
|
||||
This stage only works with Google Chrome, as it relies on the [Chrome Verified Access API](https://developers.google.com/chrome/verified-access).
|
||||
:::
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
---
|
||||
title: WebAuthn / Passkeys Authenticator setup stage
|
||||
title: WebAuthn / FIDO2 / Passkeys Authenticator setup stage
|
||||
---
|
||||
|
||||
This stage configures a WebAuthn-based Authenticator. This can either be a browser, biometrics or a Security stick like a YubiKey.
|
||||
This stage configures an authenticator stage for using WebAuthn, FIDO2, Passkeys. This stage supports:
|
||||
|
||||
- **Security Keys**: Physical devices like YubiKey, Google Titan, etc.
|
||||
- **Platform Authenticators**: Built-in authenticators like Windows Hello, Touch ID, Face ID
|
||||
- **Mobile Devices**: Using device biometrics or security keys via mobile browsers
|
||||
|
||||
### Options
|
||||
|
||||
|
||||
@@ -4,7 +4,23 @@ authentik_version: "2024.4"
|
||||
authentik_enterprise: true
|
||||
---
|
||||
|
||||
The source stage injects an [OAuth](../../../../users-sources/sources/protocols/oauth/index.mdx) or [SAML](../../../../users-sources/sources/protocols/saml/index.md) Source into the flow execution. This allows for additional user verification, or to dynamically access different sources for different user identifiers (username, email address, etc).
|
||||
The source stage embeds an [OAuth](../../../../users-sources/sources/protocols/oauth/index.mdx) or [SAML](../../../../users-sources/sources/protocols/saml/index.md) source into the flow execution. This allows for additional user verification, or to dynamically access different sources for different user identifiers (username, email address, etc).
|
||||
|
||||
### Example use case
|
||||
|
||||
This stage can be used to leverage an external OAuth/SAML identity provider.
|
||||
|
||||
For example, you can authenticate users by routing them through a custom device-health solution.
|
||||
|
||||
Another use case is to route users to authenticate with your legacy (Okta, etc) IdP and then use the returned identity and attributes within authentik as part of an authorization flow, for example as part of an IdP migration. For authentication/enrollment this is also possible with an [OAuth](../../../../users-sources/sources/protocols/oauth/index.mdx)/[SAML](../../../../users-sources/sources/protocols/saml/index.md) source by itself.
|
||||
|
||||
### Considerations
|
||||
|
||||
It is very important that the configured source's authentication and enrollment flows (when set; they can be left unselected to prevent authentication or enrollment with the source) do **not** have a [User login stage](../user_login/index.md) bound to them.
|
||||
|
||||
This is because the Source stage works by appending a [dynamic in-memory](../../../../core/terminology.md#dynamic-in-memory-stage) stage to the source's flow, so having a [User login stage](../user_login/index.md) bound will cause the source's flow to not resume the original flow it was started from, and instead directly authenticating the pending user.
|
||||
|
||||
## Source stage workflow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
@@ -29,20 +45,6 @@ sequenceDiagram
|
||||
ak->>u: Execution of the previous flow is resumed
|
||||
```
|
||||
|
||||
### Considerations
|
||||
|
||||
It is very important that the configured source's authentication and enrollment flows (when set; they can be left unselected to prevent authentication or enrollment with the source) do **not** have a [User login stage](../user_login/index.md) bound to them.
|
||||
|
||||
This is because the Source stage works by appending a [dynamic in-memory](../../../../core/terminology.md#dynamic-in-memory-stage) stage to the source's flow, so having a [User login stage](../user_login/index.md) bound will cause the source's flow to not resume the original flow it was started from, and instead directly authenticating the pending user.
|
||||
|
||||
### Example use case
|
||||
|
||||
This stage can be used to leverage an external OAuth/SAML identity provider.
|
||||
|
||||
For example, you can authenticate users by routing them through a custom device-health solution.
|
||||
|
||||
Another use case is to route users to authenticate with your legacy (Okta, etc) IdP and then use the returned identity and attributes within authentik as part of an authorization flow, for example as part of an IdP migration. For authentication/enrollment this is also possible with an [OAuth](../../../../users-sources/sources/protocols/oauth/index.mdx)/[SAML](../../../../users-sources/sources/protocols/saml/index.md) source by itself.
|
||||
|
||||
### Options
|
||||
|
||||
#### Source
|
||||
|
||||
@@ -40,7 +40,7 @@ When creating or editing this stage in the UI of the Admin interface, you can se
|
||||
|
||||
When configured, all sessions authenticated by this stage will be bound to the selected network and/or GeoIP criteria.
|
||||
|
||||
Sessions that break this binding will be terminated on use. The created [`logout`](../../../../sys-mgmt/events/index.md#logout) event will contain additional data related to what caused the binding to be broken:
|
||||
Sessions that break this binding will be terminated on use. The created [`logout`](../../../../sys-mgmt/events/event-actions#logout) event will contain additional data related to what caused the binding to be broken:
|
||||
|
||||
```json
|
||||
{
|
||||
|
||||
@@ -12,19 +12,17 @@ You can also run the outpost in a separate docker-compose project, you just have
|
||||
services:
|
||||
authentik_proxy:
|
||||
image: ghcr.io/goauthentik/proxy
|
||||
# Optionally specify which networks the container should be
|
||||
# might be needed to reach the core authentik server
|
||||
# Optionally specify the container's network, which must be able to reach the core authentik server.
|
||||
# networks:
|
||||
# - foo
|
||||
ports:
|
||||
- 9000:9000
|
||||
- 9443:9443
|
||||
environment:
|
||||
AUTHENTIK_HOST: https://your-authentik.tld
|
||||
AUTHENTIK_HOST: https://authentik.company
|
||||
AUTHENTIK_INSECURE: "false"
|
||||
AUTHENTIK_TOKEN: token-generated-by-authentik
|
||||
# Starting with 2021.9, you can optionally set this too
|
||||
# when authentik_host for internal communication doesn't match the public URL
|
||||
# Optional setting to be used when `authentik_host` for internal communication doesn't match the public URL.
|
||||
# AUTHENTIK_HOST_BROWSER: https://external-domain.tld
|
||||
```
|
||||
|
||||
@@ -34,15 +32,29 @@ services:
|
||||
services:
|
||||
authentik_ldap:
|
||||
image: ghcr.io/goauthentik/ldap
|
||||
# Optionally specify which networks the container should be
|
||||
# might be needed to reach the core authentik server
|
||||
# Optionally specify the container's network, which must be able to reach the core authentik server.
|
||||
# networks:
|
||||
# - foo
|
||||
ports:
|
||||
- 389:3389
|
||||
- 636:6636
|
||||
environment:
|
||||
AUTHENTIK_HOST: https://your-authentik.tld
|
||||
AUTHENTIK_HOST: https://authentik.company
|
||||
AUTHENTIK_INSECURE: "false"
|
||||
AUTHENTIK_TOKEN: token-generated-by-authentik
|
||||
```
|
||||
|
||||
### RAC outpost
|
||||
|
||||
```yaml
|
||||
services:
|
||||
rac_outpost:
|
||||
image: ghcr.io/goauthentik/rac
|
||||
# Optionally specify the container's network, which must be able to reach the core authentik server.
|
||||
# networks:
|
||||
# - foo
|
||||
environment:
|
||||
AUTHENTIK_HOST: https://authentik.company
|
||||
AUTHENTIK_INSECURE: "false"
|
||||
AUTHENTIK_TOKEN: token-generated-by-authentik
|
||||
```
|
||||
@@ -53,14 +65,13 @@ services:
|
||||
services:
|
||||
radius_outpost:
|
||||
image: ghcr.io/goauthentik/radius
|
||||
# Optionally specify which networks the container should be
|
||||
# might be needed to reach the core authentik server
|
||||
# Optionally specify the container's network, which must be able to reach the core authentik server.
|
||||
# networks:
|
||||
# - foo
|
||||
ports:
|
||||
- 1812:1812/udp
|
||||
environment:
|
||||
AUTHENTIK_HOST: https://your-authentik.tld
|
||||
AUTHENTIK_HOST: https://authentik.company
|
||||
AUTHENTIK_INSECURE: "false"
|
||||
AUTHENTIK_TOKEN: token-generated-by-authentik
|
||||
```
|
||||
|
||||
@@ -104,6 +104,7 @@ metadata:
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/affinity: cookie
|
||||
nginx.ingress.kubernetes.io/proxy-buffer-size: 16k
|
||||
nginx.ingress.kubernetes.io/proxy-busy-buffers-size: 32k
|
||||
nginx.ingress.kubernetes.io/proxy-buffers-number: "4"
|
||||
traefik.ingress.kubernetes.io/affinity: "true"
|
||||
labels:
|
||||
|
||||
@@ -10,7 +10,7 @@ For more information about using a Google Workspace provider, see the [Overview]
|
||||
To create a Google Workspace provider in authentik, you must have already [configured Google Workspace](./setup-gws.md) to integrate with authentik.
|
||||
|
||||
:::info
|
||||
When adding the Google Workspace provider in authentik, you must define the **Backchannel provider** using the name of the Google Workspace provider that you created in authentik. If you have also configured Google Workspace to log in using authentik following [these](../../../../integrations/services/google/), then this configuration can be done on the same app.
|
||||
When adding the Google Workspace provider in authentik, you must define the **Backchannel provider** using the name of the Google Workspace provider that you created in authentik. If you have also configured Google Workspace to log in using authentik following [these](/integrations/services/google/), then this configuration can be done on the same app.
|
||||
:::
|
||||
|
||||
### Create the Google Workspace provider in authentik
|
||||
|
||||
@@ -152,3 +152,17 @@ return {
|
||||
```
|
||||
|
||||
Afterwards, edit the _Proxy provider_ and add this new mapping. The expression is only evaluated when the user logs into the application.
|
||||
|
||||
## Host header:ak-version[2025.6.1]
|
||||
|
||||
By default, the proxy provider will use forwarded Host header received from the client. Starting with authentik 2025.6.1, it is possible to dynamically adjust the Host header with a property mapping.
|
||||
|
||||
```python
|
||||
return {
|
||||
"ak_proxy": {
|
||||
"host_header": "my-internal-host-header"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Afterwards, edit the _Proxy provider_ and add this new mapping. The expression is only evaluated when the user logs into the application.
|
||||
|
||||
@@ -36,11 +36,12 @@ The _Endpoint_ object specifies the hostname/IP of the machine to connect to, as
|
||||
|
||||
Configuration details such as credentials can be specified through _settings_, which can be specified on different levels and are all merged together when connecting:
|
||||
|
||||
1. Provider settings
|
||||
2. Endpoint settings
|
||||
3. Connection settings
|
||||
1. Default settings
|
||||
2. Provider settings
|
||||
3. Endpoint settings
|
||||
4. Provider property mapping settings
|
||||
5. Endpoint property mapping settings
|
||||
6. Connection settings
|
||||
|
||||
### Connection settings
|
||||
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
---
|
||||
title: RAC Credentials Prompt
|
||||
---
|
||||
|
||||
## About the RAC credentials prompt
|
||||
|
||||
You can configure the RAC provider to prompt users for their credentials when connecting to RAC endpoints. This is particulalry useful for establishing RDP connections to modern Windows systems that often require credentials to establish a connection.
|
||||
|
||||
After implementing this configuration, when connecting to an RAC endpoint users are prompted to enter their credentials which are then passed to the RAC endpoint. This means that static credentials do not need to be set in the RAC provider, property mapping, or endpoint.
|
||||
|
||||
This configurations requires:
|
||||
|
||||
1. Creating an authorization flow.
|
||||
2. Creating two prompts.
|
||||
3. Creating and binding a prompt stage.
|
||||
4. Updating the RAC provider.
|
||||
|
||||
## Create a new authorization flow
|
||||
|
||||
1. Log in to authentik as an administrator, and open the authentik Admin interface.
|
||||
2. Navigate to **Flows and Stages** > **Flows**, click **Create**, and enter the following required settings:
|
||||
- **Name**: Enter a descriptive name for the flow.
|
||||
- **Title**: Enter a title for the flow. This will be displayed to users when they're prompted for their credentials.
|
||||
- **Slug**: Enter a slug for the flow. This will be displayed in the flow URL.
|
||||
- **Designation**: `Authorization`
|
||||
- **Authentication**: `Require authentication`
|
||||
3. Click **Create**.
|
||||
|
||||
## Create prompts
|
||||
|
||||
1. Log in to authentik as an administrator, and open the authentik Admin interface.
|
||||
2. Navigate to **Flows and Stages** > **Prompts**, click **Create**, and enter the following required settings:
|
||||
- **Name**: Enter a descriptive name for the prompt (e.g. `username`).
|
||||
- **Field Key**: `connection_settings.username`
|
||||
- **Label**: Enter a label for the field which will be displayed above it.
|
||||
- **Type**: `Text`
|
||||
- **Required**: Toggled on.
|
||||
- **Order**: `0`
|
||||
3. Click **Create** to save the prompt.
|
||||
4. On the **Prompts** page, click **Create** again, and enter the following required settings:
|
||||
- **Name**: Enter a descriptive name for the prompt (e.g. `password`).
|
||||
- **Field Key**: `connection_settings.password`
|
||||
- **Label**: Enter a label for the field which will be displayed above it.
|
||||
- **Type**: `Password`
|
||||
- **Required**: Toggled.
|
||||
- **Order**: `1`
|
||||
5. Click **Create** to save the prompt.
|
||||
|
||||
:::note
|
||||
You can optionally add other prompt fields such as `domain` (e.g. `connection_settings.domain`), which can be useful for Windows based RDP. There is also the option of adding a `Text (read-only)` type prompt field that includes explanatory text for the user (e.g. `please enter your RDP credentials`).
|
||||
:::
|
||||
|
||||
## Create and bind a prompt stage
|
||||
|
||||
1. Log in to authentik as an administrator, and open the authentik Admin interface.
|
||||
2. Navigate to **Flows and Stages** > **Flows**.
|
||||
3. Click the name of the newly created authorization flow.
|
||||
4. Click on **Stage bindings**, click **Create and bind stage**, and enter the following required settings:
|
||||
- **Select Type**: Select `Prompt stage` as the prompt type.
|
||||
- **Create Prompt Stage**:
|
||||
- **Name**: Enter a name for the prompt stage.
|
||||
- Under **Fields**:
|
||||
- Click the **x** icon to remove all selected fields.
|
||||
- Add the two newly created prompt fields (e.g.`username` and `password`) to selected fields.
|
||||
- Under **Validation Policies**:
|
||||
- Click the **x** icon to remove all selected validation policies.
|
||||
- **Create binding**:
|
||||
- Click **Finish**.
|
||||
|
||||
## Update the RAC provider
|
||||
|
||||
1. Log in to authentik as an administrator, and open the authentik Admin interface.
|
||||
2. Navigate to **Applications** > **Providers**.
|
||||
3. Click the **Edit** icon of the RAC provider that you wish to add a credentials prompt to.
|
||||
4. Change **Authorization flow** to the newly created authorization flow.
|
||||
5. Click **Update** to save the change.
|
||||
|
||||
## Update the RAC endpoint _(sometimes required)_
|
||||
|
||||
Depending on the configuration of the RDP server that's being connected to, it is sometimes necessary to set the security type that's used for the connection. For many modern windows RDP servers, this often needs to be set to `tls`.
|
||||
|
||||
1. Log in to authentik as an administrator, and open the authentik Admin interface.
|
||||
2. Navigate to **Applications** > **Providers** and click the name of the RAC provider that you're using.
|
||||
3. Under **Endpoints**, click the **Edit** icon of the endpoint that you're using.
|
||||
4. Under **Advanced Settings** in the **Settings** box, enter `security: tls`
|
||||
5. Click **Update** to save the change.
|
||||
|
||||
:::note
|
||||
Other options for the connection security type are: `any`, `nla`, `nla-ext`, `vmconnect`, and `rdp`. For more information see the [Guacamole RDP Authentication and Security Documentation](https://guacamole.apache.org/doc/gug/configuring-guacamole.html#authentication-and-security).
|
||||
:::
|
||||
|
||||
## Configuration verification
|
||||
|
||||
Log in to authentik with a user account that has the required privileges to access the RAC application. Open the User interface, and on the **My applications** page click the RAC application. You should then be redirected to the prompt stage and prompted for a username and password. Enter the credentials for the RAC endpoint and if the credentials are valid the RDP/SSH/VNC connection should be established.
|
||||
@@ -16,7 +16,9 @@ Events in authentik that are tracked via SSF include when an MFA device is added
|
||||
|
||||
## Example use cases
|
||||
|
||||
A common use case for SSF is when an Admin wants to know if a user logs out of authentik, so that the user is then also automaticlaly logged out of all other work-focused applications.
|
||||
One important use case for SFF is to [integrate Apple Business Manager](https://integrations.goauthentik.io/device-management/apple/) or any of the Apple device management platforms with authentik, so that users can enroll their Apple devices using their authentik credentials. When a user signs in with their email address, Apple redirects them to authentik for authentication. Once authenticated, Apple enrolls the user's device and grants access to Apple services.
|
||||
|
||||
Another use case for SSF is when an Admin wants to know if a user logs out of authentik, so that the user is then also automatically logged out of all other work-focused applications.
|
||||
|
||||
Another example use case is when an application uses SSF to subscribe to authorization events because the application needs to know if a user changed their password in authentik. If a user did change their password, then the application receives a POST request to write the fact that the password was changed.
|
||||
|
||||
|
||||
@@ -94,6 +94,8 @@ Avoid phrasing that blames the user. Be subjective and polite when providing ins
|
||||
|
||||
For Ken's sake, and many others, try to not use too many commas (avoid commaitis). Use a comma when needed to separate clauses, or for "slowing the pace" or clarity. Please **do** use the Oxford comma.
|
||||
|
||||
In [lists](#lists), add a period at the end of a bulleted item if it is a complete sentence. Try not to mix incomplete and complete sentences in the same list.
|
||||
|
||||
### Capitalization
|
||||
|
||||
#### Titles and headers
|
||||
@@ -159,7 +161,7 @@ When writing out steps in a procedural topic, avoid starting with "Once...". Ins
|
||||
|
||||
- Use _italic_ for:
|
||||
|
||||
- Emphasis, but sparingly, to avoid overuse. For example, you can use italics for important terms or concepts on first mention in a section.
|
||||
- Emphasis, but sparingly, to avoid overuse. For example, you can use italics for important terms or concepts on first mention in a section. Do not use italics to indicate a variable or placeholder; instead use angle brackets as described under [Variables](#variables).
|
||||
|
||||
- Use `code formatting` for:
|
||||
|
||||
@@ -167,14 +169,28 @@ When writing out steps in a procedural topic, avoid starting with "Once...". Ins
|
||||
- File paths, file names, and directory names (e.g., `/usr/local/bin/`).
|
||||
- Inline code snippets (e.g., `.env`).
|
||||
|
||||
- When handling URLs:
|
||||
### Lists
|
||||
|
||||
- For URLs entered as values or defined in fields, enclose any variables inside angle brackets (`< >`) to clearly indicate that these are placeholders that require user input.
|
||||
Add a period at the end of a bulleted item if it is a complete sentence. Try not to mix incomplete and complete sentences in the same list.
|
||||
|
||||
For example: `https://authentik.company/application/o/<slug>/.well-known/openid-configuration`
|
||||
If there is a [colon](#following-a-colon) used in a bulleted list item, follow the capitalization rules.
|
||||
|
||||
### URLs
|
||||
|
||||
- When mentioning URLs in text or within procedural instructions, omit code formatting. For instance: "In your browser, go to https://example.com."
|
||||
|
||||
- For URLs entered as values or defined in fields, enclose any variables inside angle brackets (`< >`) and use underscores between words. See more about variables below (#variables).
|
||||
|
||||
### Variables
|
||||
|
||||
To clearly indicate terms or values that are placeholders and require user input, enclose any variables inside angle brackets (`< >`) and use underscores between words to clearly indicate that these are placeholders that require user input.
|
||||
|
||||
Examples:
|
||||
|
||||
`https://authentik.company/application/o/<slug>/.well-known/openid-configuration`
|
||||
|
||||
"Add the configuration setting: `<first_name>`."
|
||||
|
||||
### Titles and headers
|
||||
|
||||
- Titles and headers (H1, H2, H3) should follow **sentence case capitalization**, meaning only the first word is capitalized, except for proper nouns or product names.
|
||||
@@ -189,7 +205,7 @@ When writing out steps in a procedural topic, avoid starting with "Once...". Ins
|
||||
|
||||
### Examples
|
||||
|
||||
When you want to show an example (say, a code snippet), start on a new line, use bold text for the word "Example", and a semi-colon, like this:
|
||||
When you want to show an example (say, a code snippet), start on a new line, use bold text for the word "Example", and a colon, like this:
|
||||
|
||||
**Example**:
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ title: Welcome to authentik
|
||||
|
||||
## What is authentik?
|
||||
|
||||
authentik is an IdP (Identity Provider) and SSO (single sign on) that is built with security at the forefront of every piece of code, every feature, with an emphasis on flexibility and versatility.
|
||||
authentik is an IdP (Identity Provider) and SSO (Single Sign On) platform that is built with security at the forefront of every piece of code, every feature, with an emphasis on flexibility and versatility.
|
||||
|
||||
With authentik, site administrators, application developers, and security engineers have a dependable and secure solution for authentication in almost any type of environment. There are robust recovery actions available for the users and applications, including user profile and password management. You can quickly edit, deactivate, or even impersonate a user profile, and set a new password for new users or reset an existing password.
|
||||
|
||||
|
||||
@@ -70,9 +70,6 @@ To check if your config has been applied correctly, you can run the following co
|
||||
- `AUTHENTIK_POSTGRESQL__USER`: Database user
|
||||
- `AUTHENTIK_POSTGRESQL__PORT`: Database port, defaults to 5432
|
||||
- `AUTHENTIK_POSTGRESQL__PASSWORD`: Database password, defaults to the environment variable `POSTGRES_PASSWORD`
|
||||
{/* TODO: Temporarily deactivated feature, see https://github.com/goauthentik/authentik/issues/14320 */}
|
||||
{/* - `AUTHENTIK_POSTGRESQL__USE_POOL`: Use a [connection pool](https://docs.djangoproject.com/en/stable/ref/databases/#connection-pool) for PostgreSQL connections. Defaults to `false`. :ak-version[2025.4] */}
|
||||
- `AUTHENTIK_POSTGRESQL__POOL_OPTIONS`: Extra configuration to pass to the [ConnectionPool object](https://www.psycopg.org/psycopg3/docs/api/pool.html#psycopg_pool.ConnectionPool) when it is created. Must be a base64-encoded JSON dictionary. Ignored when `USE_POOL` is set to `false`. :ak-version[2025.4]
|
||||
- `AUTHENTIK_POSTGRESQL__USE_PGBOUNCER`: Adjust configuration to support connection to PgBouncer. Deprecated, see below
|
||||
- `AUTHENTIK_POSTGRESQL__USE_PGPOOL`: Adjust configuration to support connection to Pgpool. Deprecated, see below
|
||||
- `AUTHENTIK_POSTGRESQL__SSLMODE`: Strictness of ssl verification. Defaults to `"verify-ca"`
|
||||
@@ -85,7 +82,7 @@ To check if your config has been applied correctly, you can run the following co
|
||||
|
||||
The PostgreSQL settings `HOST`, `PORT`, `USER`, and `PASSWORD` support hot-reloading. Adding and removing read replicas doesn't support hot-reloading.
|
||||
|
||||
- `AUTHENTIK_POSTGRESQL__DEFAULT_SCHEMA`:ak-version[2024.12]
|
||||
- `AUTHENTIK_POSTGRESQL__DEFAULT_SCHEMA` :ak-version[2024.12]
|
||||
|
||||
The name of the schema used by default in the database. Defaults to `public`.
|
||||
|
||||
|
||||
@@ -81,20 +81,20 @@ Previously, sessions were stored by default in the cache. Now, they are stored i
|
||||
|
||||
An integration is a how authentik connects to third-party applications, directories, and other identity providers. The following integration guides were recently added.
|
||||
|
||||
- [Apple Business Manager](../../../integrations/services/apple/)
|
||||
- [FleetDM](../../../integrations/services/fleet/)
|
||||
- [Gravity](../../../integrations/services/gravity/)
|
||||
- [Homarr](../../../integrations/services/homarr/)
|
||||
- [KnocKnoc](../../../integrations/services/knocknoc)
|
||||
- [Mautic](../../../integrations/services/mautic/)
|
||||
- [Mailcow](../../../integrations/services/mailcow/)
|
||||
- [Mealie](../../../integrations/services/mealie/)
|
||||
- [OpenProject](../../../integrations/services/openproject)
|
||||
- [Sidero Omni](../../../integrations/services/omni)
|
||||
- [Tandoor](../../../integrations/services/tandoor/)
|
||||
- [Wazuh](../../../integrations/services/wazuh)
|
||||
- [XCreds](../../../integrations/services/xcreds)
|
||||
- [Zipline](../../../integrations/services/zipline/)
|
||||
- [Apple Business Manager](/integrations/services/apple/)
|
||||
- [FleetDM](/integrations/services/fleet/)
|
||||
- [Gravity](/integrations/services/gravity/)
|
||||
- [Homarr](/integrations/services/homarr/)
|
||||
- [KnocKnoc](/integrations/services/knocknoc)
|
||||
- [Mautic](/integrations/services/mautic/)
|
||||
- [Mailcow](/integrations/services/mailcow/)
|
||||
- [Mealie](/integrations/services/mealie/)
|
||||
- [OpenProject](/integrations/services/openproject)
|
||||
- [Sidero Omni](/integrations/services/omni)
|
||||
- [Tandoor](/integrations/services/tandoor/)
|
||||
- [Wazuh](/integrations/services/wazuh)
|
||||
- [XCreds](/integrations/services/xcreds)
|
||||
- [Zipline](/integrations/services/zipline/)
|
||||
|
||||
## Upgrading
|
||||
|
||||
@@ -285,6 +285,16 @@ helm upgrade authentik authentik/authentik -f values.yaml --version ^2025.4
|
||||
- root: temporarily deactivate database pool option (cherry-pick #14443) (#14479)
|
||||
- web/flows/sfe: fix global background image not being loaded (cherry-pick #14442) (#14450)
|
||||
|
||||
## Fixed in 2025.4.2
|
||||
|
||||
- core: Migrate permissions before deleting OldAuthenticatedSession (cherry-pick #14788) (#14791)
|
||||
- lifecycle: fix arguments not being passed to worker command (cherry-pick #14574) (#14620)
|
||||
- sources/scim: fix all users being added to group when no members are given (cherry-pick #14645) (#14666)
|
||||
|
||||
## Fixed in 2025.4.3
|
||||
|
||||
- security: fix CVE-2025-52553 (#15289)
|
||||
|
||||
## API Changes
|
||||
|
||||
#### What's New
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user