Compare commits
20 Commits
template-f
...
version/20
Author | SHA1 | Date | |
---|---|---|---|
9d81f0598c | |||
cbe429f3fa | |||
1cf0f57608 | |||
052da72acf | |||
9a1c76efe7 | |||
96b5bee912 | |||
09b3a1d0bd | |||
e87a17fd81 | |||
bb1bcb29cd | |||
0a5bdad972 | |||
d313225956 | |||
249dc276d4 | |||
5fb7dc4cb3 | |||
82930ee807 | |||
ac25fbab54 | |||
15cb6b18f6 | |||
fdd39b4b4c | |||
589304df4f | |||
4d920ff477 | |||
88dc616c5e |
@ -1,5 +1,5 @@
|
||||
[bumpversion]
|
||||
current_version = 2024.10.5
|
||||
current_version = 2024.12.2
|
||||
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*))?
|
||||
|
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:
|
||||
|
@ -20,8 +20,8 @@ Even if the issue is not a CVE, we still greatly appreciate your help in hardeni
|
||||
|
||||
| Version | Supported |
|
||||
| --------- | --------- |
|
||||
| 2024.8.x | ✅ |
|
||||
| 2024.10.x | ✅ |
|
||||
| 2024.12.x | ✅ |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
from os import environ
|
||||
|
||||
__version__ = "2024.10.5"
|
||||
__version__ = "2024.12.2"
|
||||
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"
|
||||
|
||||
|
||||
|
@ -138,7 +138,6 @@ def notification_cleanup(self: SystemTask):
|
||||
"""Cleanup seen notifications and notifications whose event expired."""
|
||||
notifications = Notification.objects.filter(Q(event=None) | Q(seen=True))
|
||||
amount = notifications.count()
|
||||
for notification in notifications:
|
||||
notification.delete()
|
||||
notifications.delete()
|
||||
LOGGER.debug("Expired notifications", amount=amount)
|
||||
self.set_status(TaskStatus.SUCCESSFUL, f"Expired {amount} Notifications")
|
||||
|
@ -280,9 +280,24 @@ class ConfigLoader:
|
||||
self.log("warning", "Failed to parse config as int", path=path, exc=str(exc))
|
||||
return default
|
||||
|
||||
def get_optional_int(self, path: str, default=None) -> int | None:
|
||||
"""Wrapper for get that converts value into int or None if set"""
|
||||
value = self.get(path, default)
|
||||
|
||||
try:
|
||||
return int(value)
|
||||
except (ValueError, TypeError) as exc:
|
||||
if value is None or (isinstance(value, str) and value.lower() == "null"):
|
||||
return None
|
||||
self.log("warning", "Failed to parse config as int", path=path, exc=str(exc))
|
||||
return default
|
||||
|
||||
def get_bool(self, path: str, default=False) -> bool:
|
||||
"""Wrapper for get that converts value into boolean"""
|
||||
return str(self.get(path, default)).lower() == "true"
|
||||
value = self.get(path, UNSET)
|
||||
if value is UNSET:
|
||||
return default
|
||||
return str(self.get(path)).lower() == "true"
|
||||
|
||||
def get_keys(self, path: str, sep=".") -> list[str]:
|
||||
"""List attribute keys by using yaml path"""
|
||||
@ -354,20 +369,33 @@ def django_db_config(config: ConfigLoader | None = None) -> dict:
|
||||
"sslcert": config.get("postgresql.sslcert"),
|
||||
"sslkey": config.get("postgresql.sslkey"),
|
||||
},
|
||||
"CONN_MAX_AGE": CONFIG.get_optional_int("postgresql.conn_max_age", 0),
|
||||
"CONN_HEALTH_CHECKS": CONFIG.get_bool("postgresql.conn_health_checks", False),
|
||||
"DISABLE_SERVER_SIDE_CURSORS": CONFIG.get_bool(
|
||||
"postgresql.disable_server_side_cursors", False
|
||||
),
|
||||
"TEST": {
|
||||
"NAME": config.get("postgresql.test.name"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
conn_max_age = CONFIG.get_optional_int("postgresql.conn_max_age", UNSET)
|
||||
disable_server_side_cursors = CONFIG.get_bool("postgresql.disable_server_side_cursors", UNSET)
|
||||
if config.get_bool("postgresql.use_pgpool", False):
|
||||
db["default"]["DISABLE_SERVER_SIDE_CURSORS"] = True
|
||||
if disable_server_side_cursors is not UNSET:
|
||||
db["default"]["DISABLE_SERVER_SIDE_CURSORS"] = disable_server_side_cursors
|
||||
|
||||
if config.get_bool("postgresql.use_pgbouncer", False):
|
||||
# https://docs.djangoproject.com/en/4.0/ref/databases/#transaction-pooling-server-side-cursors
|
||||
db["default"]["DISABLE_SERVER_SIDE_CURSORS"] = True
|
||||
# https://docs.djangoproject.com/en/4.0/ref/databases/#persistent-connections
|
||||
db["default"]["CONN_MAX_AGE"] = None # persistent
|
||||
if disable_server_side_cursors is not UNSET:
|
||||
db["default"]["DISABLE_SERVER_SIDE_CURSORS"] = disable_server_side_cursors
|
||||
if conn_max_age is not UNSET:
|
||||
db["default"]["CONN_MAX_AGE"] = conn_max_age
|
||||
|
||||
for replica in config.get_keys("postgresql.read_replicas"):
|
||||
_database = deepcopy(db["default"])
|
||||
|
@ -6,8 +6,6 @@ postgresql:
|
||||
user: authentik
|
||||
port: 5432
|
||||
password: "env://POSTGRES_PASSWORD"
|
||||
use_pgbouncer: false
|
||||
use_pgpool: false
|
||||
test:
|
||||
name: test_authentik
|
||||
read_replicas: {}
|
||||
|
@ -214,6 +214,9 @@ class TestConfig(TestCase):
|
||||
"PORT": "foo",
|
||||
"TEST": {"NAME": "foo"},
|
||||
"USER": "foo",
|
||||
"CONN_MAX_AGE": 0,
|
||||
"CONN_HEALTH_CHECKS": False,
|
||||
"DISABLE_SERVER_SIDE_CURSORS": False,
|
||||
}
|
||||
},
|
||||
)
|
||||
@ -251,6 +254,9 @@ class TestConfig(TestCase):
|
||||
"PORT": "foo",
|
||||
"TEST": {"NAME": "foo"},
|
||||
"USER": "foo",
|
||||
"CONN_MAX_AGE": 0,
|
||||
"CONN_HEALTH_CHECKS": False,
|
||||
"DISABLE_SERVER_SIDE_CURSORS": False,
|
||||
},
|
||||
"replica_0": {
|
||||
"ENGINE": "authentik.root.db",
|
||||
@ -266,6 +272,72 @@ class TestConfig(TestCase):
|
||||
"PORT": "foo",
|
||||
"TEST": {"NAME": "foo"},
|
||||
"USER": "foo",
|
||||
"CONN_MAX_AGE": 0,
|
||||
"CONN_HEALTH_CHECKS": False,
|
||||
"DISABLE_SERVER_SIDE_CURSORS": False,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
def test_db_read_replicas_pgbouncer(self):
|
||||
"""Test read replicas"""
|
||||
config = ConfigLoader()
|
||||
config.set("postgresql.host", "foo")
|
||||
config.set("postgresql.name", "foo")
|
||||
config.set("postgresql.user", "foo")
|
||||
config.set("postgresql.password", "foo")
|
||||
config.set("postgresql.port", "foo")
|
||||
config.set("postgresql.sslmode", "foo")
|
||||
config.set("postgresql.sslrootcert", "foo")
|
||||
config.set("postgresql.sslcert", "foo")
|
||||
config.set("postgresql.sslkey", "foo")
|
||||
config.set("postgresql.test.name", "foo")
|
||||
config.set("postgresql.use_pgbouncer", True)
|
||||
# Read replica
|
||||
config.set("postgresql.read_replicas.0.host", "bar")
|
||||
# Override conn_max_age
|
||||
config.set("postgresql.read_replicas.0.conn_max_age", 10)
|
||||
# This isn't supported
|
||||
config.set("postgresql.read_replicas.0.use_pgbouncer", False)
|
||||
conf = django_db_config(config)
|
||||
self.assertEqual(
|
||||
conf,
|
||||
{
|
||||
"default": {
|
||||
"DISABLE_SERVER_SIDE_CURSORS": True,
|
||||
"CONN_MAX_AGE": None,
|
||||
"CONN_HEALTH_CHECKS": False,
|
||||
"ENGINE": "authentik.root.db",
|
||||
"HOST": "foo",
|
||||
"NAME": "foo",
|
||||
"OPTIONS": {
|
||||
"sslcert": "foo",
|
||||
"sslkey": "foo",
|
||||
"sslmode": "foo",
|
||||
"sslrootcert": "foo",
|
||||
},
|
||||
"PASSWORD": "foo",
|
||||
"PORT": "foo",
|
||||
"TEST": {"NAME": "foo"},
|
||||
"USER": "foo",
|
||||
},
|
||||
"replica_0": {
|
||||
"DISABLE_SERVER_SIDE_CURSORS": True,
|
||||
"CONN_MAX_AGE": 10,
|
||||
"CONN_HEALTH_CHECKS": False,
|
||||
"ENGINE": "authentik.root.db",
|
||||
"HOST": "bar",
|
||||
"NAME": "foo",
|
||||
"OPTIONS": {
|
||||
"sslcert": "foo",
|
||||
"sslkey": "foo",
|
||||
"sslmode": "foo",
|
||||
"sslrootcert": "foo",
|
||||
},
|
||||
"PASSWORD": "foo",
|
||||
"PORT": "foo",
|
||||
"TEST": {"NAME": "foo"},
|
||||
"USER": "foo",
|
||||
},
|
||||
},
|
||||
)
|
||||
@ -294,6 +366,8 @@ class TestConfig(TestCase):
|
||||
{
|
||||
"default": {
|
||||
"DISABLE_SERVER_SIDE_CURSORS": True,
|
||||
"CONN_MAX_AGE": 0,
|
||||
"CONN_HEALTH_CHECKS": False,
|
||||
"ENGINE": "authentik.root.db",
|
||||
"HOST": "foo",
|
||||
"NAME": "foo",
|
||||
@ -310,6 +384,8 @@ class TestConfig(TestCase):
|
||||
},
|
||||
"replica_0": {
|
||||
"DISABLE_SERVER_SIDE_CURSORS": True,
|
||||
"CONN_MAX_AGE": 0,
|
||||
"CONN_HEALTH_CHECKS": False,
|
||||
"ENGINE": "authentik.root.db",
|
||||
"HOST": "bar",
|
||||
"NAME": "foo",
|
||||
@ -362,6 +438,9 @@ class TestConfig(TestCase):
|
||||
"PORT": "foo",
|
||||
"TEST": {"NAME": "foo"},
|
||||
"USER": "foo",
|
||||
"DISABLE_SERVER_SIDE_CURSORS": False,
|
||||
"CONN_MAX_AGE": 0,
|
||||
"CONN_HEALTH_CHECKS": False,
|
||||
},
|
||||
"replica_0": {
|
||||
"ENGINE": "authentik.root.db",
|
||||
@ -377,6 +456,9 @@ class TestConfig(TestCase):
|
||||
"PORT": "foo",
|
||||
"TEST": {"NAME": "foo"},
|
||||
"USER": "foo",
|
||||
"DISABLE_SERVER_SIDE_CURSORS": False,
|
||||
"CONN_MAX_AGE": 0,
|
||||
"CONN_HEALTH_CHECKS": False,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
@ -256,7 +256,7 @@ class AssertionProcessor:
|
||||
assertion.attrib["IssueInstant"] = self._issue_instant
|
||||
assertion.append(self.get_issuer())
|
||||
|
||||
if self.provider.signing_kp:
|
||||
if self.provider.signing_kp and self.provider.sign_assertion:
|
||||
sign_algorithm_transform = SIGN_ALGORITHM_TRANSFORM_MAP.get(
|
||||
self.provider.signature_algorithm, xmlsec.constants.TransformRsaSha1
|
||||
)
|
||||
@ -295,6 +295,18 @@ class AssertionProcessor:
|
||||
|
||||
response.append(self.get_issuer())
|
||||
|
||||
if self.provider.signing_kp and self.provider.sign_response:
|
||||
sign_algorithm_transform = SIGN_ALGORITHM_TRANSFORM_MAP.get(
|
||||
self.provider.signature_algorithm, xmlsec.constants.TransformRsaSha1
|
||||
)
|
||||
signature = xmlsec.template.create(
|
||||
response,
|
||||
xmlsec.constants.TransformExclC14N,
|
||||
sign_algorithm_transform,
|
||||
ns=xmlsec.constants.DSigNs,
|
||||
)
|
||||
response.append(signature)
|
||||
|
||||
status = SubElement(response, f"{{{NS_SAML_PROTOCOL}}}Status")
|
||||
status_code = SubElement(status, f"{{{NS_SAML_PROTOCOL}}}StatusCode")
|
||||
status_code.attrib["Value"] = "urn:oasis:names:tc:SAML:2.0:status:Success"
|
||||
|
@ -2,8 +2,10 @@
|
||||
|
||||
from base64 import b64encode
|
||||
|
||||
from defusedxml.lxml import fromstring
|
||||
from django.http.request import QueryDict
|
||||
from django.test import TestCase
|
||||
from lxml import etree # nosec
|
||||
|
||||
from authentik.blueprints.tests import apply_blueprint
|
||||
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
|
||||
@ -11,12 +13,14 @@ from authentik.crypto.models import CertificateKeyPair
|
||||
from authentik.events.models import Event, EventAction
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.lib.tests.utils import get_request
|
||||
from authentik.lib.xml import lxml_from_string
|
||||
from authentik.providers.saml.models import SAMLPropertyMapping, SAMLProvider
|
||||
from authentik.providers.saml.processors.assertion import AssertionProcessor
|
||||
from authentik.providers.saml.processors.authn_request_parser import AuthNRequestParser
|
||||
from authentik.sources.saml.exceptions import MismatchedRequestID
|
||||
from authentik.sources.saml.models import SAMLSource
|
||||
from authentik.sources.saml.processors.constants import (
|
||||
NS_MAP,
|
||||
SAML_BINDING_REDIRECT,
|
||||
SAML_NAME_ID_FORMAT_EMAIL,
|
||||
SAML_NAME_ID_FORMAT_UNSPECIFIED,
|
||||
@ -185,6 +189,19 @@ class TestAuthNRequest(TestCase):
|
||||
self.assertEqual(response.count(response_proc._assertion_id), 2)
|
||||
self.assertEqual(response.count(response_proc._response_id), 2)
|
||||
|
||||
schema = etree.XMLSchema(
|
||||
etree.parse("schemas/saml-schema-protocol-2.0.xsd", parser=etree.XMLParser()) # nosec
|
||||
)
|
||||
self.assertTrue(schema.validate(lxml_from_string(response)))
|
||||
|
||||
response_xml = fromstring(response)
|
||||
self.assertEqual(
|
||||
len(response_xml.xpath("//saml:Assertion/ds:Signature", namespaces=NS_MAP)), 1
|
||||
)
|
||||
self.assertEqual(
|
||||
len(response_xml.xpath("//samlp:Response/ds:Signature", namespaces=NS_MAP)), 1
|
||||
)
|
||||
|
||||
# Now parse the response (source)
|
||||
http_request.POST = QueryDict(mutable=True)
|
||||
http_request.POST["SAMLResponse"] = b64encode(response.encode()).decode()
|
||||
|
@ -5,6 +5,7 @@ from django.contrib.auth.models import Permission
|
||||
from django.db.models import QuerySet
|
||||
from django_filters.filters import ModelChoiceFilter
|
||||
from django_filters.filterset import FilterSet
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.fields import (
|
||||
CharField,
|
||||
@ -13,6 +14,8 @@ from rest_framework.fields import (
|
||||
ReadOnlyField,
|
||||
SerializerMethodField,
|
||||
)
|
||||
from rest_framework.filters import OrderingFilter, SearchFilter
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.viewsets import ReadOnlyModelViewSet
|
||||
|
||||
from authentik.core.api.utils import ModelSerializer, PassiveSerializer
|
||||
@ -92,7 +95,9 @@ class RBACPermissionViewSet(ReadOnlyModelViewSet):
|
||||
queryset = Permission.objects.none()
|
||||
serializer_class = PermissionSerializer
|
||||
ordering = ["name"]
|
||||
filter_backends = [DjangoFilterBackend, OrderingFilter, SearchFilter]
|
||||
filterset_class = PermissionFilter
|
||||
permission_classes = [IsAuthenticated]
|
||||
search_fields = [
|
||||
"codename",
|
||||
"content_type__model",
|
||||
|
@ -38,7 +38,9 @@ class KerberosBackend(InbuiltBackend):
|
||||
self, username: str, realm: str | None, password: str, **filters
|
||||
) -> tuple[User | None, KerberosSource | None]:
|
||||
sources = KerberosSource.objects.filter(enabled=True)
|
||||
user = User.objects.filter(usersourceconnection__source__in=sources, **filters).first()
|
||||
user = User.objects.filter(
|
||||
usersourceconnection__source__in=sources, username=username, **filters
|
||||
).first()
|
||||
|
||||
if user is not None:
|
||||
# User found, let's get its connections for the sources that are available
|
||||
@ -77,7 +79,7 @@ class KerberosBackend(InbuiltBackend):
|
||||
password, sender=user_source_connection.source
|
||||
)
|
||||
user_source_connection.user.save()
|
||||
return user, user_source_connection.source
|
||||
return user_source_connection.user, user_source_connection.source
|
||||
# Password doesn't match, onto next source
|
||||
LOGGER.debug(
|
||||
"failed to kinit, password invalid",
|
||||
|
@ -2,7 +2,7 @@
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://goauthentik.io/blueprints/schema.json",
|
||||
"type": "object",
|
||||
"title": "authentik 2024.10.5 Blueprint schema",
|
||||
"title": "authentik 2024.12.2 Blueprint schema",
|
||||
"required": [
|
||||
"version",
|
||||
"entries"
|
||||
|
@ -31,7 +31,7 @@ services:
|
||||
volumes:
|
||||
- redis:/data
|
||||
server:
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.10.5}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.12.2}
|
||||
restart: unless-stopped
|
||||
command: server
|
||||
environment:
|
||||
@ -54,7 +54,7 @@ services:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
worker:
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.10.5}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.12.2}
|
||||
restart: unless-stopped
|
||||
command: worker
|
||||
environment:
|
||||
|
@ -29,4 +29,4 @@ func UserAgent() string {
|
||||
return fmt.Sprintf("authentik@%s", FullVersion())
|
||||
}
|
||||
|
||||
const VERSION = "2024.10.5"
|
||||
const VERSION = "2024.12.2"
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"maps"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
@ -16,11 +17,22 @@ import (
|
||||
"goauthentik.io/internal/constants"
|
||||
)
|
||||
|
||||
func (ac *APIController) getWebsocketURL(akURL url.URL, outpostUUID string, query url.Values) *url.URL {
|
||||
wsUrl := &url.URL{}
|
||||
wsUrl.Scheme = strings.ReplaceAll(akURL.Scheme, "http", "ws")
|
||||
wsUrl.Host = akURL.Host
|
||||
_p, _ := url.JoinPath(akURL.Path, "ws/outpost/", outpostUUID, "/")
|
||||
wsUrl.Path = _p
|
||||
v := url.Values{}
|
||||
maps.Insert(v, maps.All(akURL.Query()))
|
||||
maps.Insert(v, maps.All(query))
|
||||
wsUrl.RawQuery = v.Encode()
|
||||
return wsUrl
|
||||
}
|
||||
|
||||
func (ac *APIController) initWS(akURL url.URL, outpostUUID string) error {
|
||||
pathTemplate := "%s://%s%sws/outpost/%s/?%s"
|
||||
query := akURL.Query()
|
||||
query.Set("instance_uuid", ac.instanceUUID.String())
|
||||
scheme := strings.ReplaceAll(akURL.Scheme, "http", "ws")
|
||||
|
||||
authHeader := fmt.Sprintf("Bearer %s", ac.token)
|
||||
|
||||
@ -37,7 +49,9 @@ func (ac *APIController) initWS(akURL url.URL, outpostUUID string) error {
|
||||
},
|
||||
}
|
||||
|
||||
ws, _, err := dialer.Dial(fmt.Sprintf(pathTemplate, scheme, akURL.Host, akURL.Path, outpostUUID, akURL.Query().Encode()), header)
|
||||
wsu := ac.getWebsocketURL(akURL, outpostUUID, query).String()
|
||||
ac.logger.WithField("url", wsu).Debug("connecting to websocket")
|
||||
ws, _, err := dialer.Dial(wsu, header)
|
||||
if err != nil {
|
||||
ac.logger.WithError(err).Warning("failed to connect websocket")
|
||||
return err
|
||||
|
42
internal/outpost/ak/api_ws_test.go
Normal file
42
internal/outpost/ak/api_ws_test.go
Normal file
@ -0,0 +1,42 @@
|
||||
package ak
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func URLMustParse(u string) *url.URL {
|
||||
ur, err := url.Parse(u)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ur
|
||||
}
|
||||
|
||||
func TestWebsocketURL(t *testing.T) {
|
||||
u := URLMustParse("http://localhost:9000?foo=bar")
|
||||
uuid := "23470845-7263-4fe3-bd79-ec1d7bf77d77"
|
||||
ac := &APIController{}
|
||||
nu := ac.getWebsocketURL(*u, uuid, url.Values{})
|
||||
assert.Equal(t, "ws://localhost:9000/ws/outpost/23470845-7263-4fe3-bd79-ec1d7bf77d77/?foo=bar", nu.String())
|
||||
}
|
||||
|
||||
func TestWebsocketURL_Query(t *testing.T) {
|
||||
u := URLMustParse("http://localhost:9000?foo=bar")
|
||||
uuid := "23470845-7263-4fe3-bd79-ec1d7bf77d77"
|
||||
ac := &APIController{}
|
||||
v := url.Values{}
|
||||
v.Set("bar", "baz")
|
||||
nu := ac.getWebsocketURL(*u, uuid, v)
|
||||
assert.Equal(t, "ws://localhost:9000/ws/outpost/23470845-7263-4fe3-bd79-ec1d7bf77d77/?bar=baz&foo=bar", nu.String())
|
||||
}
|
||||
|
||||
func TestWebsocketURL_Subpath(t *testing.T) {
|
||||
u := URLMustParse("http://localhost:9000/foo/bar/")
|
||||
uuid := "23470845-7263-4fe3-bd79-ec1d7bf77d77"
|
||||
ac := &APIController{}
|
||||
nu := ac.getWebsocketURL(*u, uuid, url.Values{})
|
||||
assert.Equal(t, "ws://localhost:9000/foo/bar/ws/outpost/23470845-7263-4fe3-bd79-ec1d7bf77d77/", nu.String())
|
||||
}
|
@ -15,7 +15,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-11-26 00:09+0000\n"
|
||||
"POT-Creation-Date: 2024-12-18 13:31+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: deluxghost, 2024\n"
|
||||
"Language-Team: Chinese Simplified (https://app.transifex.com/authentik/teams/119923/zh-Hans/)\n"
|
||||
@ -1898,6 +1898,10 @@ msgstr "Kerberos 领域"
|
||||
msgid "Custom krb5.conf to use. Uses the system one by default"
|
||||
msgstr "要使用的自定义 krb5.conf。默认使用系统自带"
|
||||
|
||||
#: authentik/sources/kerberos/models.py
|
||||
msgid "KAdmin server type"
|
||||
msgstr "KAdmin 服务器类型"
|
||||
|
||||
#: authentik/sources/kerberos/models.py
|
||||
msgid "Sync users from Kerberos into authentik"
|
||||
msgstr "从 Kerberos 同步用户到 authentik"
|
||||
@ -2858,7 +2862,7 @@ msgstr ""
|
||||
#, python-format
|
||||
msgid ""
|
||||
"\n"
|
||||
" If you did not request a password change, please ignore this Email. The link above is valid for %(expires)s.\n"
|
||||
" If you did not request a password change, please ignore this email. The link above is valid for %(expires)s.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
@ -2882,7 +2886,7 @@ msgstr ""
|
||||
#, python-format
|
||||
msgid ""
|
||||
"\n"
|
||||
"If you did not request a password change, please ignore this Email. The link above is valid for %(expires)s.\n"
|
||||
"If you did not request a password change, please ignore this email. The link above is valid for %(expires)s.\n"
|
||||
msgstr ""
|
||||
"\n"
|
||||
"如果您没有请求更改密码,请忽略此电子邮件。上面的链接在 %(expires)s 内有效。\n"
|
||||
@ -3151,6 +3155,22 @@ msgstr "输入阶段"
|
||||
msgid "Passwords don't match."
|
||||
msgstr "密码不匹配。"
|
||||
|
||||
#: authentik/stages/redirect/api.py
|
||||
msgid "Target URL should be present when mode is Static."
|
||||
msgstr "当模式为静态时,目标 URL 应存在。"
|
||||
|
||||
#: authentik/stages/redirect/api.py
|
||||
msgid "Target Flow should be present when mode is Flow."
|
||||
msgstr "当模式为流程时,目标流程应存在。"
|
||||
|
||||
#: authentik/stages/redirect/models.py
|
||||
msgid "Redirect Stage"
|
||||
msgstr "重定向阶段"
|
||||
|
||||
#: authentik/stages/redirect/models.py
|
||||
msgid "Redirect Stages"
|
||||
msgstr "重定向阶段"
|
||||
|
||||
#: authentik/stages/user_delete/models.py
|
||||
msgid "User Delete Stage"
|
||||
msgstr "用户删除阶段"
|
||||
|
@ -14,7 +14,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-11-26 00:09+0000\n"
|
||||
"POT-Creation-Date: 2024-12-18 13:31+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: deluxghost, 2024\n"
|
||||
"Language-Team: Chinese (China) (https://app.transifex.com/authentik/teams/119923/zh_CN/)\n"
|
||||
@ -1897,6 +1897,10 @@ msgstr "Kerberos 领域"
|
||||
msgid "Custom krb5.conf to use. Uses the system one by default"
|
||||
msgstr "要使用的自定义 krb5.conf。默认使用系统自带"
|
||||
|
||||
#: authentik/sources/kerberos/models.py
|
||||
msgid "KAdmin server type"
|
||||
msgstr "KAdmin 服务器类型"
|
||||
|
||||
#: authentik/sources/kerberos/models.py
|
||||
msgid "Sync users from Kerberos into authentik"
|
||||
msgstr "从 Kerberos 同步用户到 authentik"
|
||||
@ -2857,7 +2861,7 @@ msgstr ""
|
||||
#, python-format
|
||||
msgid ""
|
||||
"\n"
|
||||
" If you did not request a password change, please ignore this Email. The link above is valid for %(expires)s.\n"
|
||||
" If you did not request a password change, please ignore this email. The link above is valid for %(expires)s.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
@ -2881,7 +2885,7 @@ msgstr ""
|
||||
#, python-format
|
||||
msgid ""
|
||||
"\n"
|
||||
"If you did not request a password change, please ignore this Email. The link above is valid for %(expires)s.\n"
|
||||
"If you did not request a password change, please ignore this email. The link above is valid for %(expires)s.\n"
|
||||
msgstr ""
|
||||
"\n"
|
||||
"如果您没有请求更改密码,请忽略此电子邮件。上面的链接在 %(expires)s 内有效。\n"
|
||||
@ -3150,6 +3154,22 @@ msgstr "输入阶段"
|
||||
msgid "Passwords don't match."
|
||||
msgstr "密码不匹配。"
|
||||
|
||||
#: authentik/stages/redirect/api.py
|
||||
msgid "Target URL should be present when mode is Static."
|
||||
msgstr "当模式为静态时,目标 URL 应存在。"
|
||||
|
||||
#: authentik/stages/redirect/api.py
|
||||
msgid "Target Flow should be present when mode is Flow."
|
||||
msgstr "当模式为流程时,目标流程应存在。"
|
||||
|
||||
#: authentik/stages/redirect/models.py
|
||||
msgid "Redirect Stage"
|
||||
msgstr "重定向阶段"
|
||||
|
||||
#: authentik/stages/redirect/models.py
|
||||
msgid "Redirect Stages"
|
||||
msgstr "重定向阶段"
|
||||
|
||||
#: authentik/stages/user_delete/models.py
|
||||
msgid "User Delete Stage"
|
||||
msgstr "用户删除阶段"
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@goauthentik/authentik",
|
||||
"version": "2024.10.5",
|
||||
"version": "2024.12.2",
|
||||
"private": true
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "authentik"
|
||||
version = "2024.10.5"
|
||||
version = "2024.12.2"
|
||||
description = ""
|
||||
authors = ["authentik Team <hello@goauthentik.io>"]
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: authentik
|
||||
version: 2024.10.5
|
||||
version: 2024.12.2
|
||||
description: Making authentication simple.
|
||||
contact:
|
||||
email: hello@goauthentik.io
|
||||
|
@ -46,7 +46,7 @@ export class OutpostServiceConnectionListPage extends TablePage<ServiceConnectio
|
||||
const connections = await new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsAllList(
|
||||
await this.defaultEndpointConfig(),
|
||||
);
|
||||
Promise.all(
|
||||
await Promise.all(
|
||||
connections.results.map((connection) => {
|
||||
return new OutpostsApi(DEFAULT_CONFIG)
|
||||
.outpostsServiceConnectionsAllStateRetrieve({
|
||||
|
@ -4,7 +4,7 @@ import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types";
|
||||
import { OAuthSource, SourcesApi } from "@goauthentik/api";
|
||||
|
||||
const sourceToSelect = (source: OAuthSource) => [
|
||||
source.pk,
|
||||
source.slug,
|
||||
`${source.name} (${source.slug})`,
|
||||
source.name,
|
||||
source,
|
||||
|
@ -291,7 +291,7 @@ export function renderForm(
|
||||
|
||||
${showHttpBasic ? renderHttpBasic(provider) : nothing}
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Trusted OIDC Sources")}
|
||||
label=${msg("Federated OIDC Sources")}
|
||||
name="jwtFederationSources"
|
||||
>
|
||||
<ak-dual-select-dynamic-selected
|
||||
|
@ -131,9 +131,10 @@ export class UserListPage extends WithBrandConfig(WithCapabilitiesConfig(TablePa
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.activePath = getURLParam<string>("path", "/");
|
||||
const defaultPath = new DefaultUIConfig().defaults.userPath;
|
||||
this.activePath = getURLParam<string>("path", defaultPath);
|
||||
uiConfig().then((c) => {
|
||||
if (c.defaults.userPath !== new DefaultUIConfig().defaults.userPath) {
|
||||
if (c.defaults.userPath !== defaultPath) {
|
||||
this.activePath = c.defaults.userPath;
|
||||
}
|
||||
});
|
||||
|
@ -3,7 +3,7 @@ export const SUCCESS_CLASS = "pf-m-success";
|
||||
export const ERROR_CLASS = "pf-m-danger";
|
||||
export const PROGRESS_CLASS = "pf-m-in-progress";
|
||||
export const CURRENT_CLASS = "pf-m-current";
|
||||
export const VERSION = "2024.10.5";
|
||||
export const VERSION = "2024.12.2";
|
||||
export const TITLE_DEFAULT = "authentik";
|
||||
export const ROUTE_SEPARATOR = ";";
|
||||
|
||||
|
@ -3,6 +3,7 @@ import { AKElement } from "@goauthentik/elements/Base";
|
||||
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
|
||||
import { themeImage } from "@goauthentik/elements/utils/images";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
|
||||
@ -86,7 +87,7 @@ export class SidebarBrand extends WithBrandConfig(AKElement) {
|
||||
<div class="pf-c-brand ak-brand">
|
||||
<img
|
||||
src=${themeImage(this.brand?.brandingLogo ?? DefaultBrand.brandingLogo)}
|
||||
alt="authentik Logo"
|
||||
alt="${msg("authentik Logo")}"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
|
@ -511,7 +511,7 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||
DefaultBrand.brandingLogo,
|
||||
),
|
||||
)}"
|
||||
alt="authentik Logo"
|
||||
alt="${msg("authentik Logo")}"
|
||||
/>
|
||||
</div>
|
||||
${until(this.renderChallenge())}
|
||||
|
@ -8,6 +8,7 @@ import { DefaultBrand } from "@goauthentik/elements/sidebar/SidebarBrand";
|
||||
import { themeImage } from "@goauthentik/elements/utils/images";
|
||||
import "rapidoc";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
@ -102,7 +103,7 @@ export class APIBrowser extends Interface {
|
||||
>
|
||||
<div slot="nav-logo">
|
||||
<img
|
||||
alt="authentik Logo"
|
||||
alt="${msg("authentik Logo")}"
|
||||
class="logo"
|
||||
src="${themeImage(
|
||||
first(this.brand?.brandingLogo, DefaultBrand.brandingLogo),
|
||||
|
@ -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">
|
||||
@ -1737,8 +1737,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">
|
||||
@ -2901,8 +2901,8 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s76768bebabb7d543">
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
|
||||
<target>包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...'</target>
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
|
||||
<target>包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...'</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s026555347e589f0e">
|
||||
@ -3648,8 +3648,8 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s7b1fba26d245cb1c">
|
||||
<source>When using an external logging solution for archiving, this can be set to "minutes=5".</source>
|
||||
<target>使用外部日志记录解决方案进行存档时,可以将其设置为 "minutes=5"。</target>
|
||||
<source>When using an external logging solution for archiving, this can be set to "minutes=5".</source>
|
||||
<target>使用外部日志记录解决方案进行存档时,可以将其设置为 "minutes=5"。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s44536d20bb5c8257">
|
||||
@ -3825,10 +3825,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">
|
||||
@ -4904,7 +4904,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>
|
||||
@ -5273,7 +5273,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>
|
||||
@ -7674,7 +7674,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">
|
||||
@ -9020,7 +9020,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="sa38c5a2731be3a46">
|
||||
@ -9272,8 +9272,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="s43f899a86c6a3484">
|
||||
<source>Redirect URIs/Origins</source>
|
||||
@ -9301,67 +9301,88 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3cc2b33d2a8000d3">
|
||||
<source>KAdmin type</source>
|
||||
<target>KAdmin 类型</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s624e1c8739507529">
|
||||
<source>MIT krb5 kadmin</source>
|
||||
<target>MIT krb5 kadmin</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6d225d9e74dfff6f">
|
||||
<source>Heimdal kadmin</source>
|
||||
<target>Heimdal kadmin</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc9e494c8346b7cb5">
|
||||
<source>Other</source>
|
||||
<target>其他</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sbf6c78047e8ec8f8">
|
||||
<source>Other type of kadmin</source>
|
||||
<target>其他类型 kadmin</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb53d0b77abef2316">
|
||||
<source>To let a user directly reset their password, configure a recovery flow on the currently active brand.</source>
|
||||
<target>要让用户直接重置密码,请在当前活动的品牌上配置恢复流程。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2e5226fcf269689b">
|
||||
<source>Consent given lasts indefinitely</source>
|
||||
<target>无限期同意授权</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7eff620292ed9349">
|
||||
<source>Consent expires</source>
|
||||
<target>同意授权会过期</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1cc032bcc50b2942">
|
||||
<source>Available Policies</source>
|
||||
<target>可用策略</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3ad64193ad5f4a5e">
|
||||
<source>Selected Policies</source>
|
||||
<target>已选策略</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc487e11d5987dbb4">
|
||||
<source>Redirect the user to another flow, potentially with all gathered context</source>
|
||||
<target>将用户重定向到另一个流程,可能包含所有已收集的上下文</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sad9d5481474d4f5b">
|
||||
<source>Static</source>
|
||||
<target>静态</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="se87a96950464bc89">
|
||||
<source>Target URL</source>
|
||||
<target>目标 URL</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7f3097955b19736a">
|
||||
<source>Redirect the user to a static URL.</source>
|
||||
<target>将用户重定向到一个静态 URL。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9bdee1c5130c8240">
|
||||
<source>Target Flow</source>
|
||||
<target>目标流程</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa5d1405b8d6529c7">
|
||||
<source>Redirect the user to a Flow.</source>
|
||||
<target>将用户重定向到一个流程。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7c9db337d14d42b3">
|
||||
<source>Keep flow context</source>
|
||||
<target>保留流程上下文</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0d7dea184036a74d">
|
||||
<source>Require no authentication</source>
|
||||
<target>需要无身份验证</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s66f533986ba6182c">
|
||||
<source>Require superuser</source>
|
||||
<target>需要管理员用户</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s26c0a8789930b5fd">
|
||||
<source>Require being redirected from another flow</source>
|
||||
<target>需要重定向自另一个流程</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sbfaee8cfbf4e44e8">
|
||||
<source>Require Outpost (flow can only be executed from an outpost)</source>
|
||||
<target>需要前哨(流程只能从前哨执行)</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
</xliff>
|
@ -4967,16 +4967,6 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<source>Always require consent</source>
|
||||
<target>始终需要征得同意授权</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s8ce8bdc9cc9c8604">
|
||||
<source>Consent given last indefinitely</source>
|
||||
<target>无限期同意授权</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sb986f15fa9b17805">
|
||||
<source>Consent expires.</source>
|
||||
<target>同意授权会过期。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s6f328f2d8382d998">
|
||||
<source>Consent expires in</source>
|
||||
@ -5478,16 +5468,6 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<source>Require authentication</source>
|
||||
<target>需要身份验证</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s239c2a351cde6d39">
|
||||
<source>Require no authentication.</source>
|
||||
<target>需要无身份验证。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s98beadfeeb3acb66">
|
||||
<source>Require superuser.</source>
|
||||
<target>需要管理员用户。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sfad9279cc42c6b61">
|
||||
<source>Required authentication level for this flow.</source>
|
||||
@ -7765,10 +7745,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>Event volume</source>
|
||||
<target>事件容量</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s047a5f0211fedc72">
|
||||
<source>Require Outpost (flow can only be executed from an outpost).</source>
|
||||
<target>需要前哨(流程只能从前哨执行)。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3271da6c18c25b18">
|
||||
<source>Connection settings.</source>
|
||||
<target>连接设置。</target>
|
||||
@ -9322,6 +9298,90 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s4f8a3f7792e6b940">
|
||||
<source>JWTs signed by the selected providers can be used to authenticate to this provider.</source>
|
||||
<target>由已选提供程序签发的 JWT 可以用于此提供程序的身份验证。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3cc2b33d2a8000d3">
|
||||
<source>KAdmin type</source>
|
||||
<target>KAdmin 类型</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s624e1c8739507529">
|
||||
<source>MIT krb5 kadmin</source>
|
||||
<target>MIT krb5 kadmin</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6d225d9e74dfff6f">
|
||||
<source>Heimdal kadmin</source>
|
||||
<target>Heimdal kadmin</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc9e494c8346b7cb5">
|
||||
<source>Other</source>
|
||||
<target>其他</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sbf6c78047e8ec8f8">
|
||||
<source>Other type of kadmin</source>
|
||||
<target>其他类型 kadmin</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb53d0b77abef2316">
|
||||
<source>To let a user directly reset their password, configure a recovery flow on the currently active brand.</source>
|
||||
<target>要让用户直接重置密码,请在当前活动的品牌上配置恢复流程。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2e5226fcf269689b">
|
||||
<source>Consent given lasts indefinitely</source>
|
||||
<target>无限期同意授权</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7eff620292ed9349">
|
||||
<source>Consent expires</source>
|
||||
<target>同意授权会过期</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1cc032bcc50b2942">
|
||||
<source>Available Policies</source>
|
||||
<target>可用策略</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3ad64193ad5f4a5e">
|
||||
<source>Selected Policies</source>
|
||||
<target>已选策略</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc487e11d5987dbb4">
|
||||
<source>Redirect the user to another flow, potentially with all gathered context</source>
|
||||
<target>将用户重定向到另一个流程,可能包含所有已收集的上下文</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sad9d5481474d4f5b">
|
||||
<source>Static</source>
|
||||
<target>静态</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="se87a96950464bc89">
|
||||
<source>Target URL</source>
|
||||
<target>目标 URL</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7f3097955b19736a">
|
||||
<source>Redirect the user to a static URL.</source>
|
||||
<target>将用户重定向到一个静态 URL。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9bdee1c5130c8240">
|
||||
<source>Target Flow</source>
|
||||
<target>目标流程</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa5d1405b8d6529c7">
|
||||
<source>Redirect the user to a Flow.</source>
|
||||
<target>将用户重定向到一个流程。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7c9db337d14d42b3">
|
||||
<source>Keep flow context</source>
|
||||
<target>保留流程上下文</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0d7dea184036a74d">
|
||||
<source>Require no authentication</source>
|
||||
<target>需要无身份验证</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s66f533986ba6182c">
|
||||
<source>Require superuser</source>
|
||||
<target>需要管理员用户</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s26c0a8789930b5fd">
|
||||
<source>Require being redirected from another flow</source>
|
||||
<target>需要重定向自另一个流程</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sbfaee8cfbf4e44e8">
|
||||
<source>Require Outpost (flow can only be executed from an outpost)</source>
|
||||
<target>需要前哨(流程只能从前哨执行)</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -20,13 +20,21 @@ To add an application to authentik and have it display on users' **My applicatio
|
||||
|
||||
2. Click **Create with Wizard**. (Alternatively, use our legacy process and click **Create**. The legacy process requires that the application and its authentication provider be configured separately.)
|
||||
|
||||
3. In the **New application** wizard, define the application details, the provider type and configuration, and then click **Submit**.
|
||||
3. In the **New application** wizard, define the application details, the provider type, bindings for the application.
|
||||
|
||||
4. To manage the display of the new application on the **My applications** page, you can optionally define the bindings for a specific policy, group, or user. Note that if you do not define bindings, then all users have access to the application, For more information, refer to [authorization](#authorization).
|
||||
- **Application**: provide a name, an optional group for the type of application, the policy engine mode, and optional UI settings.
|
||||
|
||||
## Authorization
|
||||
- **Choose a Provider**: select the provider types for this application.
|
||||
|
||||
Application access can be configured using (Policy) bindings. Click on an application in the applications list, and select the _Policy / Group / User Bindings_ tab. There you can bind users/groups/policies to grant them access. When nothing is bound, everyone has access. You can use this to grant access to one or multiple users/groups, or dynamically give access using policies.
|
||||
- **Configure a Provider**: provide a name (or accept the auto-provided name), the authorization flow to use for this provider, and any additional required configurations.
|
||||
|
||||
- **Configure Bindings**: to manage the listing and access to applications on a user's **My applications** page, you can optionally create a [binding](../flows-stages/bindings/index.md) between the application and a specific policy, group, or user. Note that if you do not define any bindings, then all users have access to the application. For more information about user access, refer to our documentation about [authorization](#policy-driven-authorization) and [hiding an application](#hide-applications).
|
||||
|
||||
4. On the **Review and Submit Application** panel, review the configuration for the new application and its provider, and then click **Submit**.
|
||||
|
||||
## Policy-driven authorization
|
||||
|
||||
To use a [policy](../../customize/policies/index.md) to control which users or groups can access an application, click on an application in the applications list and then select the **Policy/Group/User Bindings** tab. There you can bind users/groups/policies to grant them access. When nothing is bound, everyone has access. Binding a policy restricts access to specific Users or Groups, or by other custom policies such as restriction to a set time-of-day or a geographic region.
|
||||
|
||||
By default, all users can access applications when no policies are bound.
|
||||
|
||||
@ -35,6 +43,49 @@ When multiple policies/groups/users are attached, you can configure the _Policy
|
||||
- Require users to pass all bindings/be member of all groups (ALL), or
|
||||
- Require users to pass either binding/be member of either group (ANY)
|
||||
|
||||
## Application Entitlements
|
||||
|
||||
<span class="badge badge--preview">Preview</span>
|
||||
<span class="badge badge--version">authentik 2024.12+</span>
|
||||
|
||||
Application entitlements can be used through authentik to manage authorization within an application (what areas of the app users or groups can access). Entitlements are scoped to a single application and can be bound to multiple users and/or groups (binding policies is not currently supported), giving them access to the entitlement. An application can either check for the name of the entitlement (via the `entitlements` scope), or via attributes stored in entitlements.
|
||||
|
||||
An authentik admin can create an entitlement [in the Admin interface](#create-an-application-entitlement) or using the [authentik API](../../developer-docs/api/api.md).
|
||||
|
||||
Because entitlements exist within an application, names of entitlements must be unique within an application. This also means that entitlements are deleted when an application is deleted.
|
||||
|
||||
### Using entitlements
|
||||
|
||||
Entitlements to which a user has access can be retrieved using the `user.app_entitlements()` function in property mappings/policies. This function needs to be passed the specific application for which to get the entitlements. For example:
|
||||
|
||||
```python
|
||||
entitlements = [entitlement.name for entitlement in request.user.app_entitlements(provider.application)]
|
||||
return {
|
||||
"entitlements": entitlements,
|
||||
}
|
||||
```
|
||||
|
||||
### Attributes
|
||||
|
||||
Each entitlement can store attributes similar to user and group attributes. These attributes can be accessed in property mappings and passed to applications via `user.app_entitlements_attributes`. For example:
|
||||
|
||||
```python
|
||||
attrs = request.user.app_entitlements(provider.application)
|
||||
return {
|
||||
"my_attr": attrs.get("my_attr")
|
||||
}
|
||||
```
|
||||
|
||||
### Create an application entitlement
|
||||
|
||||
1. Open the Admin interface and navigate to **Applications -> Applications**.
|
||||
2. Click the name of the application for which you want to create an entitlement.
|
||||
3. Click the **Application entitlements** tab at the top of the page, and then click **Create entitlement**. Provide a name for the entitlement, enter any optional **Attributes**, and then click **Create**.
|
||||
4. In the list locate the entitlement to which you want to bind a user or group, and then **click the caret (>) to expand the entitlement details.**
|
||||
5. In the expanded area, click **Bind existing Group/User**.
|
||||
6. In the **Create Binding** modal box, select either the tab for **Group** or **User**, and then in the drop-down list, select the group or user.
|
||||
7. Optionally, configure additional settings for the binding, and then click **Create** to create the binding and close the modal box.
|
||||
|
||||
## Hide applications
|
||||
|
||||
To hide an application without modifying its policy settings or removing it, you can simply set the _Launch URL_ to `blank://blank`, which will hide the application from users.
|
||||
|
33
website/docs/add-secure-apps/flows-stages/bindings/index.md
Normal file
33
website/docs/add-secure-apps/flows-stages/bindings/index.md
Normal file
@ -0,0 +1,33 @@
|
||||
---
|
||||
title: Bindings
|
||||
---
|
||||
|
||||
A binding is, simply put, a connection between two components (a flow, stage, policy, user, or group). The use of a binding adds additional functionality to one those existing components; for example, a policy binding can cause a new stage to be presented within a flow to a specific user or group.
|
||||
|
||||
:::info
|
||||
For information about creating and managing bindings, refer to [Working with bindings](./work_with_bindings.md).
|
||||
:::
|
||||
|
||||
Bindings are an important part of authentik; the majority of configuration options are set in bindings.
|
||||
|
||||
Bindings are analyzed by authentik's Flow Plan, which starts with the flow, then assesses all of the bound policies, and then runs them in order to build out the plan.
|
||||
|
||||
The two most common types of bindings in authentik are:
|
||||
|
||||
- stage bindings
|
||||
- policy bindings
|
||||
- user and group bindings
|
||||
|
||||
A _stage binding_ connects a stage to a flow. The "additional content" (i.e. the content in the stage) is now added to the flow.
|
||||
|
||||
A _policy binding_ connects a specific policy to a flow or to a stage. With the binding, the flow (or stage) will now have additional content (i.e. the policy rules).
|
||||
|
||||
You can also bind groups and users to another component (a policy, a stage, a flow, etc.). For example, you can create a binding for a specific group, and then [bind that to a stage binding](../stages/index.md#bind-users-and-groups-to-a-flows-stage-binding), with the result that everyone in that group now will see that stage (and any policies bound to that stage) as part of their flow. Or more specifically, and going one step deeper, you can also _bind a binding to a binding_.
|
||||
|
||||
Bindings are also used for [Application Entitlements](../../applications/manage_apps.md#application-entitlements), where you can bind specific users or groups to an application as a way to manage who has access to the application.
|
||||
|
||||
It's important to remember that bindings are instantiated objects themselves, and conceptually can be considered as a "connector" between two components. This is why you might read about "binding a binding", because technically, a binding is "spliced" into another binding, in order to intercept and enforce the criteria defined in the second binding.
|
||||
|
||||
:::info
|
||||
Be aware that some stages and flows do not allow user or group bindings, because in certain scenarios (authentication or enrollment), the flow plan doesn't yet know who the user or group is.
|
||||
:::
|
@ -0,0 +1,13 @@
|
||||
---
|
||||
title: Work with bindings
|
||||
---
|
||||
|
||||
As covered in the [overview](./index.md), bindings interact with many other components.
|
||||
|
||||
For instructions to create a binding, refer to the documentation for the specific components:
|
||||
|
||||
- [Bind a stage to a flow](../stages/index.md#bind-a-stage-to-a-flow)
|
||||
- [Bind a policy to a flow or stage](../../../customize/policies/working_with_policies#bind-a-policy-to-a-flow-or-stage)
|
||||
- [Bind users or groups to a specific application with an Application Entitlement](../../applications/manage_apps.md#application-entitlements)
|
||||
- [Bind a policy to a specific application when you create a new app using the Wizard](../../applications/manage_apps.md#instructions)
|
||||
- [Bind users and groups to a stage binding, to define whether or not that stage is shown](../stages/index.md#bind-users-and-groups-to-a-flows-stage-binding)
|
Binary file not shown.
After Width: | Height: | Size: 208 KiB |
@ -54,3 +54,24 @@ To bind a stage to a flow, follow these steps:
|
||||
3. In the list of flows, click the name of the flow to which you want to bind one or more stages.
|
||||
4. On the Flow page, click the **Stage Bindings** tab at the top.
|
||||
5. Here, you can decide if you want to create a new stage and bind it to the flow (**Create and bind Stage**), or if you want to select an existing stage and bind it to the flow (**Bind existing stage**).
|
||||
|
||||
## Bind users and groups to a flow's stage binding
|
||||
|
||||
You can use bindings to determine whehther or not a stage is presented to a single user or any users within a group. You do this by binding the user or group to a stage binding within a specific flow. For example, if you have a flow that contains a stage that prompts the user for multi-factor authentication, but you only want certain users to see this stage (and fulfill the MFA prompt), then you would bind the appropriate group (or single user) to the stage binding for that flow.
|
||||
|
||||
To bind a user or a group to a stage binding for a specific flow, follow these steps:
|
||||
|
||||
1. Log in as an admin to authentik, and go to the Admin interface.
|
||||
2. In the Admin interface, navigate to **Flows and Stages -> Flows**.
|
||||
3. In the list of flows, click the name of the flow to which you want to bind one or more stages.
|
||||
4. On the Flow page, click the **Stage Bindings** tab at the top.
|
||||
5. Locate the stage binding to which you want to bind a user or group, and then **click the caret (>) to expand the stage binding details.**
|
||||
|
||||

|
||||
|
||||
6. In the expanded area, click **Bind existing policy/group/user**.
|
||||
7. In the **Create Binding** modal box, select either the tab for **Group** or **User**.
|
||||
8. In the drop-down list, select the group or user.
|
||||
9. Optionally, configure additional settings for the binding, and then click **Create** to create the binding and close the modal box.
|
||||
|
||||
Learn more about [bindings](../bindings/index.md) and [working with them](../bindings/work_with_bindings.md).
|
||||
|
@ -9,7 +9,7 @@ The main settings that brands influence are flows and branding.
|
||||
|
||||
## Flows
|
||||
|
||||
You can explicitly select, in your instance's Brand settings, the default flow to use for the following configurations:
|
||||
You can explicitly select, in your instance's Brand settings, the _default flows_ to use for the current brand. To do so, log in as an administrator, open the Admin interface, and navigate to **System -> Brands**. There you can optionally configure these default flows:
|
||||
|
||||
- Authentication flow: the flow used to authenticate users. If left empty, the first applicable flow sorted by the slug is used.
|
||||
- Invalidation flow: for typical use cases, select the `default-invalidation-flow` (Logout) flow. This flow logs the user out of authentik when the application session ends (user logs out of the app).
|
||||
|
@ -8,6 +8,8 @@ authentik provides several [standard policy types](./index.md#standard-policies)
|
||||
|
||||
We also document how to use a policy to [whitelist email domains](./expression/whitelist_email.md) and to [ensure unique email addresses](./expression/unique_email.md).
|
||||
|
||||
To learn more see also [bindings](../../add-secure-apps/flows-stages/bindings/index.md) and how to use the [authentik Wizard to bind policy bindings to the new application](../../add-secure-apps/applications/manage_apps.md#add-new-applications) (for example, to configure application-specific access).
|
||||
|
||||
## Create a policy
|
||||
|
||||
To create a new policy, follow these steps:
|
||||
@ -22,7 +24,7 @@ To create a new policy, follow these steps:
|
||||
After creating the policy, you can bind it to either a [flow](../../add-secure-apps/flows-stages/flow/index.md) or to a [stage](../../add-secure-apps/flows-stages/stages/index.md).
|
||||
|
||||
:::info
|
||||
Bindings are instantiated objects themselves, and conceptually can be considered as the "connector" between the policy and the stage or flow. This is why you might read about "binding a binding", because technically, a binding is "spliced" into another binding, in order to intercept and enforce the criteria defined in the policy. You can edit bindings on a flow's **Stage Bindings** tab.
|
||||
Bindings are instantiated objects themselves, and conceptually can be considered as the "connector" between the policy and the stage or flow. This is why you might read about "binding a binding", because technically, a binding is "spliced" into another binding, in order to intercept and enforce the criteria defined in the policy. To learn more refer to our [Bindings documentation](../../add-secure-apps/flows-stages/bindings/index.md).
|
||||
:::
|
||||
|
||||
### Bind a policy to a flow
|
||||
|
@ -70,14 +70,17 @@ 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`
|
||||
- `AUTHENTIK_POSTGRESQL__USE_PGBOUNCER`: Adjust configuration to support connection to PgBouncer
|
||||
- `AUTHENTIK_POSTGRESQL__USE_PGPOOL`: Adjust configuration to support connection to Pgpool
|
||||
- `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"`
|
||||
- `AUTHENTIK_POSTGRESQL__SSLROOTCERT`: CA root for server ssl verification
|
||||
- `AUTHENTIK_POSTGRESQL__SSLCERT`: Path to x509 client certificate to authenticate to server
|
||||
- `AUTHENTIK_POSTGRESQL__SSLKEY`: Path to private key of `SSLCERT` certificate
|
||||
- `AUTHENTIK_POSTGRESQL__CONN_MAX_AGE`: Database connection lifetime. Defaults to `0` (no persistent connections). Can be set to `null` for unlimited persistent connections. See [Django's documentation](https://docs.djangoproject.com/en/stable/ref/settings/#conn-max-age) for more details.
|
||||
- `AUTHENTIK_POSTGRESQL__CONN_HEALTH_CHECK`: Existing persistent database connections will be health checked before they are reused if set to `true`. Defaults to `false`. See [Django's documentation](https://docs.djangoproject.com/en/stable/ref/settings/#conn-health-checks) for more details.
|
||||
- `AUTHENTIK_POSTGRESQL__DISABLE_SERVER_SIDE_CURSORS`: Disable server side cursors when set to `true`. Defaults to `false`. See [Django's documentation](https://docs.djangoproject.com/en/stable/ref/settings/#disable-server-side-cursors) for more details.
|
||||
|
||||
All PostgreSQL settings, apart from `USE_PGBOUNCER` and `USE_PGPOOL`, support hot-reloading. Adding and removing read replicas doesn't support hot-reloading.
|
||||
The PostgreSQL settings `HOST`, `PORT`, `USER`, and `PASSWORD` support hot-reloading. Adding and removing read replicas doesn't support hot-reloading.
|
||||
|
||||
### Read replicas
|
||||
|
||||
@ -96,8 +99,25 @@ The same PostgreSQL settings as described above are used for each read replica.
|
||||
- `AUTHENTIK_POSTGRESQL__READ_REPLICAS__0__SSLROOTCERT`
|
||||
- `AUTHENTIK_POSTGRESQL__READ_REPLICAS__0__SSLCERT`
|
||||
- `AUTHENTIK_POSTGRESQL__READ_REPLICAS__0__SSLKEY`
|
||||
- `AUTHENTIK_POSTGRESQL__READ_REPLICAS__0__CONN_MAX_AGE`
|
||||
- `AUTHENTIK_POSTGRESQL__READ_REPLICAS__0__CONN_HEALTH_CHECK`
|
||||
- `AUTHENTIK_POSTGRESQL__READ_REPLICAS__0__DISABLE_SERVER_SIDE_CURSORS`
|
||||
|
||||
Note that `USE_PGBOUNCER` and `USE_PGPOOL` are inherited from the main database configuration and are _not_ overridable on read replicas.
|
||||
### Using a PostgreSQL connection pooler (PgBouncer or PgPool)
|
||||
|
||||
When your PostgreSQL database(s) are running behind a connection pooler, like PgBouncer or PgPool, two settings need to be overridden:
|
||||
|
||||
- `AUTHENTIK_POSTGRESQL__CONN_MAX_AGE`
|
||||
|
||||
A connection pooler running in session pool mode (PgBouncer default) can be incompatible with unlimited persistent connections enabled by setting this to `null`: If the connection from the connection pooler to the database server is dropped, the connection pooler will wait for the client to disconnect before releasing the connection; however this will **never** happen as authentik is configured to keep the connection to the connection pooler forever.
|
||||
|
||||
To address this incompatibility, either configure the connection pooler to run in transaction pool mode, or update this setting to a value lower than any timeouts that may cause the connection to the database to be dropped (up to `0`).
|
||||
|
||||
- `AUTHENTIK_POSTGRESQL__DISABLE_SERVER_SIDE_CURSORS`
|
||||
|
||||
Using a connection pooler in transaction pool mode (e.g. PgPool, or PgBouncer in transaction or statement pool mode) requires disabling server-side cursors, so this setting must be set to `false`.
|
||||
|
||||
Additionally, you can set `AUTHENTIK_POSTGRESQL__CONN_HEALTH_CHECK` to perform health checks on persistent database connections before they are re-used.
|
||||
|
||||
## Redis Settings
|
||||
|
||||
|
@ -24,7 +24,7 @@ Parameters:
|
||||
Description: authentik server memory in MiB
|
||||
Type: Number
|
||||
AuthentikVersion:
|
||||
Default: 2024.10.5
|
||||
Default: 2024.12.2
|
||||
Description: authentik Docker image tag
|
||||
Type: String
|
||||
AuthentikWorkerCPU:
|
||||
|
@ -3,12 +3,6 @@ title: Release 2024.12
|
||||
slug: "/releases/2024.12"
|
||||
---
|
||||
|
||||
:::::note
|
||||
2024.12 has not been released yet! We're publishing these release notes as a preview of what's to come, and for our awesome beta testers trying out release candidates.
|
||||
|
||||
To try out the release candidate, replace your Docker image tag with the latest release candidate number, such as 2024.12.0-rc1. You can find the latest one in [the latest releases on GitHub](https://github.com/goauthentik/authentik/releases). If you don't find any, it means we haven't released one yet.
|
||||
:::::
|
||||
|
||||
## Highlights
|
||||
|
||||
- **Redirect stage** Conditionally redirect users to other flows and URLs.
|
||||
@ -24,6 +18,16 @@ To try out the release candidate, replace your Docker image tag with the latest
|
||||
|
||||
You can disable this behavior in the **Admin interface** under **System** > **Settings**.
|
||||
|
||||
- **Deprecated PostgreSQL `USE_PGBOUNCER` and `USE_PGPOOL` settings**
|
||||
|
||||
With this release, the `AUTHENTIK_POSTGRESQL__USE_PGBOUNCER` and `AUTHENTIK_POSTGRESQL__USE_PGPOOL` settings have been deprecated in favor of exposing the underlying database settings: `AUTHENTIK_POSTGRESQL__CONN_MAX_AGE` and `AUTHENTIK_POSTGRESQL__DISABLE_SERVER_SIDE_CURSORS`.
|
||||
|
||||
If you are using PgBouncer or PgPool as connection poolers and wish to maintain the same behavior as previous versions, `AUTHENTIK_POSTGRESQL__DISABLE_SERVER_SIDE_CURSORS` must be set to `true`. Moreover, if you are using PgBouncer `AUTHENTIK_POSTGRESQL__CONN_MAX_AGE` must be set to `null`.
|
||||
|
||||
The newly exposed settings allow supporting a wider set of connection pooler configurations. For details on how these settings interact with different configurations of connection poolers, please refer to the [PostgreSQL documentation](../../install-config/configuration/configuration.mdx#postgresql-settings).
|
||||
|
||||
These settings will be removed in a future version.
|
||||
|
||||
## New features
|
||||
|
||||
- **Redirect stage**
|
||||
@ -92,6 +96,7 @@ helm upgrade authentik authentik/authentik -f values.yaml --version ^2024.12
|
||||
- enterprise/rac: fix API Schema for invalidation_flow (#11907)
|
||||
- enterprise/stages/authenticator_endpoint_gdtc: don't set frame options globally (#12311)
|
||||
- enterprise: allow deletion/modification of users when in read-only mode (#12289)
|
||||
- events: notification_cleanup: avoid unnecessary loop (cherry-pick #12417) (#12418)
|
||||
- flows: better test stage's challenge responses (#12316)
|
||||
- flows: silent authz flow (#12213)
|
||||
- internal: add CSP header to files in `/media` (#12092)
|
||||
@ -112,6 +117,7 @@ helm upgrade authentik authentik/authentik -f values.yaml --version ^2024.12
|
||||
- providers/scim: accept string and int for SCIM IDs (#12093)
|
||||
- rbac: fix incorrect object_description for object-level permissions (#12029)
|
||||
- root: check remote IP for proxy protocol same as HTTP/etc (#12094)
|
||||
- root: expose CONN_MAX_AGE, CONN_HEALTH_CHECKS and DISABLE_SERVER_SIDE_CURSORS for PostgreSQL config (cherry-pick #10159) (#12419)
|
||||
- root: fix activation of locale not being scoped (#12091)
|
||||
- root: fix database ssl options not set correctly (#12180)
|
||||
- root: fix health status code (#12255)
|
||||
|
@ -57,6 +57,10 @@ When enabled, all the events caused by a user will be deleted upon the user's de
|
||||
|
||||
Globally enable/disable impersonation. Defaults to `true`.
|
||||
|
||||
### Require reason for impersonation
|
||||
|
||||
Require administrators to provide a reason for impersonating a user. Defaults to `true`.
|
||||
|
||||
### Default token duration
|
||||
|
||||
Default duration for generated tokens. Defaults to `minutes=30`.
|
||||
|
@ -105,7 +105,7 @@ If the user does not receive the email, check if the mail server parameters [are
|
||||
As an Admin, you can simply reset the password for the user.
|
||||
|
||||
1. In the Admin interface, navigate to **Directory > Users** to display all users.
|
||||
2. Either click the name of the user to display the full User details page, or click the chevron beside their name to expand the toptions.
|
||||
2. Either click the name of the user to display the full User details page, or click the chevron beside their name to expand the options.
|
||||
3. To reset the user's password, click **Reset password**, and then define the new value.
|
||||
|
||||
## Deactivate or Delete user
|
||||
@ -128,3 +128,18 @@ You may instead deactivate the account to preserve identity data.
|
||||
2. Review the changes and click **Delete**.
|
||||
|
||||
The user list refreshes and no longer displays the removed users.
|
||||
|
||||
## Impersonate a user
|
||||
|
||||
With authentik, an Admin can impersonate a user, meaning that the Admin temporarily assumes the identity of the user.
|
||||
|
||||
1. In the Admin interface, navigate to **Directory > Users** to display all users.
|
||||
2. Click the name of the user to display the full User details page.
|
||||
3. On the Overview tab, beneath **User Details**, in the **Actions** area, click **Impersonate**.
|
||||
4. At the prompt, provide a reason why you are impersonating this user, and then click **Impersonate**.
|
||||
|
||||
:::info
|
||||
An Admin can globally enable or disable impersonation in the [System Settings](../../sys-mgmt/settings.md#impersonation). By default, this option is set to true, meaning all users can be impersonated.
|
||||
|
||||
An Admin can also configure whether inputting a reason for impersonation is required in the [System Settings](../../sys-mgmt/settings.md#require-reason-for-impersonation).
|
||||
:::
|
||||
|
@ -306,6 +306,17 @@ export default {
|
||||
"add-secure-apps/flows-stages/stages/user_write",
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Bindings",
|
||||
link: {
|
||||
type: "doc",
|
||||
id: "add-secure-apps/flows-stages/bindings/index",
|
||||
},
|
||||
items: [
|
||||
"add-secure-apps/flows-stages/bindings/work_with_bindings",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
Reference in New Issue
Block a user