mirror of
https://github.com/goauthentik/authentik
synced 2026-05-06 15:12:13 +02:00
Compare commits
119 Commits
version/20
...
developer-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c11f407470 | ||
|
|
b7c6b961a1 | ||
|
|
e6adb72695 | ||
|
|
9cbdcd2cad | ||
|
|
197f4c5585 | ||
|
|
80e9865c6a | ||
|
|
c08df26c65 | ||
|
|
332a53ceff | ||
|
|
4919772d68 | ||
|
|
a978b4b60e | ||
|
|
17bd1f1574 | ||
|
|
0b4be1fdda | ||
|
|
e305c98eb8 | ||
|
|
35bd1d9907 | ||
|
|
3150885889 | ||
|
|
5fd96518d3 | ||
|
|
287647beea | ||
|
|
2c1a0ca0fc | ||
|
|
da47095ebc | ||
|
|
2ea95ba189 | ||
|
|
b277828b21 | ||
|
|
8765c92fc4 | ||
|
|
536688f23b | ||
|
|
7861f5a40e | ||
|
|
e7b43b72ab | ||
|
|
2bf9a9d4fe | ||
|
|
f6af8f3b9d | ||
|
|
c9a4eff3a8 | ||
|
|
b893305e5f | ||
|
|
b3a5cc8320 | ||
|
|
94d7a989a1 | ||
|
|
359fa5d5df | ||
|
|
11c9015a49 | ||
|
|
f135990c6b | ||
|
|
6f63a3eb15 | ||
|
|
2209fcea2a | ||
|
|
e5efb50a37 | ||
|
|
bbc02dc065 | ||
|
|
f3f81951c6 | ||
|
|
739eff66e0 | ||
|
|
48de61a926 | ||
|
|
032031f2cf | ||
|
|
4e44209af1 | ||
|
|
289555abcd | ||
|
|
943c456555 | ||
|
|
a79b914d39 | ||
|
|
7a8816abd1 | ||
|
|
93e448c3fd | ||
|
|
109c869f97 | ||
|
|
8029fdad7b | ||
|
|
d2aac457ef | ||
|
|
70ce5ccceb | ||
|
|
173c334478 | ||
|
|
6e321097a1 | ||
|
|
f3bf8097b8 | ||
|
|
b869433e4d | ||
|
|
5aef86c3d1 | ||
|
|
970ac44ff8 | ||
|
|
9145d55e6c | ||
|
|
1c36b361b2 | ||
|
|
d55e23cdb8 | ||
|
|
52673e4223 | ||
|
|
5cbcbf8d2c | ||
|
|
f29a4c1876 | ||
|
|
38fb5cd712 | ||
|
|
5b2aad586f | ||
|
|
2dd1c7b1ab | ||
|
|
57c24e5c1c | ||
|
|
76d9b3479e | ||
|
|
e9f946cdf2 | ||
|
|
167452f1ed | ||
|
|
dbfdb37e83 | ||
|
|
efdbf7aeed | ||
|
|
8e9e4de80f | ||
|
|
a63c5b1846 | ||
|
|
80b84fa8a8 | ||
|
|
4ce9795491 | ||
|
|
e50cf1c150 | ||
|
|
4178717386 | ||
|
|
20d068f767 | ||
|
|
5b7a42e6d6 | ||
|
|
1398561142 | ||
|
|
55657e149b | ||
|
|
d5d7140631 | ||
|
|
17ff12f68f | ||
|
|
9c9a6e3d66 | ||
|
|
2cd81b2e78 | ||
|
|
bad426f694 | ||
|
|
6404fba2e4 | ||
|
|
c33b9f2d3f | ||
|
|
bac6e965f4 | ||
|
|
36cb4dc750 | ||
|
|
45d9945a3a | ||
|
|
23285ad664 | ||
|
|
91ab9503fd | ||
|
|
fb7802e6af | ||
|
|
0f13a63528 | ||
|
|
36daf4b519 | ||
|
|
5cc4793b84 | ||
|
|
a6063d4af4 | ||
|
|
8f450e6e14 | ||
|
|
a1fc0605e2 | ||
|
|
c886e4ff6b | ||
|
|
f91ebc2ad5 | ||
|
|
dbe7bfe58b | ||
|
|
05d4d207d7 | ||
|
|
11efc75451 | ||
|
|
4d2d020be1 | ||
|
|
9c0905d76d | ||
|
|
3ca94b2198 | ||
|
|
dbf51fb11f | ||
|
|
ad69eb955f | ||
|
|
c867ebc014 | ||
|
|
adea1e460c | ||
|
|
846c58e617 | ||
|
|
352079fc3c | ||
|
|
6786391732 | ||
|
|
4b3d08154d | ||
|
|
130fe4cac7 |
@@ -33,17 +33,12 @@ packages/prettier-config @goauthentik/frontend
|
||||
packages/tsconfig @goauthentik/frontend
|
||||
# Web
|
||||
web/ @goauthentik/frontend
|
||||
tests/wdio/ @goauthentik/frontend
|
||||
# Locale
|
||||
locale/ @goauthentik/backend @goauthentik/frontend
|
||||
web/xliff/ @goauthentik/backend @goauthentik/frontend
|
||||
# Docs & Website
|
||||
docs/ @goauthentik/docs
|
||||
# TODO Remove after moving website to docs
|
||||
# Docs
|
||||
website/ @goauthentik/docs
|
||||
CODE_OF_CONDUCT.md @goauthentik/docs
|
||||
# Security
|
||||
SECURITY.md @goauthentik/security @goauthentik/docs
|
||||
# TODO Remove after moving website to docs
|
||||
website/security/ @goauthentik/security @goauthentik/docs
|
||||
docs/security/ @goauthentik/security @goauthentik/docs
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
# Contributing to authentik
|
||||
|
||||
Thanks for your interest in contributing! Please see our [contributing guide](https://docs.goauthentik.io/docs/developer-docs/?utm_source=github) for more information.
|
||||
|
||||
1
CONTRIBUTING.md
Symbolic link
1
CONTRIBUTING.md
Symbolic link
@@ -0,0 +1 @@
|
||||
website/docs/developer-docs/index.md
|
||||
12
Dockerfile
12
Dockerfile
@@ -26,7 +26,7 @@ RUN npm run build && \
|
||||
npm run build:sfe
|
||||
|
||||
# Stage 2: Build go proxy
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS go-builder
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25-bookworm AS go-builder
|
||||
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
@@ -76,9 +76,9 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
|
||||
/bin/sh -c "GEOIPUPDATE_LICENSE_KEY_FILE=/run/secrets/GEOIPUPDATE_LICENSE_KEY /usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
|
||||
|
||||
# Stage 4: Download uv
|
||||
FROM ghcr.io/astral-sh/uv:0.8.8 AS uv
|
||||
FROM ghcr.io/astral-sh/uv:0.8.13 AS uv
|
||||
# Stage 5: Base python image
|
||||
FROM ghcr.io/goauthentik/fips-python:3.13.6-slim-bookworm-fips AS python-base
|
||||
FROM ghcr.io/goauthentik/fips-python:3.13.7-slim-bookworm-fips AS python-base
|
||||
|
||||
ENV VENV_PATH="/ak-root/.venv" \
|
||||
PATH="/lifecycle:/ak-root/.venv/bin:$PATH" \
|
||||
@@ -119,11 +119,7 @@ RUN --mount=type=cache,id=apt-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/v
|
||||
libltdl-dev && \
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||
|
||||
ENV UV_NO_BINARY_PACKAGE="cryptography lxml python-kadmin-rs xmlsec" \
|
||||
# https://github.com/rust-lang/rustup/issues/2949
|
||||
# Fixes issues where the rust version in the build cache is older than latest
|
||||
# and rustup tries to update it, which fails
|
||||
RUSTUP_PERMIT_COPY_RENAME="true"
|
||||
ENV UV_NO_BINARY_PACKAGE="cryptography lxml python-kadmin-rs xmlsec"
|
||||
|
||||
RUN --mount=type=bind,target=pyproject.toml,src=pyproject.toml \
|
||||
--mount=type=bind,target=uv.lock,src=uv.lock \
|
||||
|
||||
27
README.md
27
README.md
@@ -15,16 +15,15 @@
|
||||
|
||||
## What is authentik?
|
||||
|
||||
authentik is an open-source Identity Provider (IdP) for modern SSO. It supports SAML, OAuth2/OIDC, LDAP, RADIUS, and more, designed for self-hosting from small labs to large production clusters.
|
||||
authentik is an open-source Identity Provider that emphasizes flexibility and versatility, with support for a wide set of protocols.
|
||||
|
||||
Our [enterprise offering](https://goauthentik.io/pricing) is available for organizations to securely replace existing IdPs such as Okta, Auth0, Entra ID, and Ping Identity for robust, large-scale identity management.
|
||||
Our [enterprise offer](https://goauthentik.io/pricing) can also be used as a self-hosted replacement for large-scale deployments of Okta/Auth0, Entra ID, Ping Identity, or other legacy IdPs for employees and B2B2C use.
|
||||
|
||||
## Installation
|
||||
|
||||
- Docker Compose: recommended for small/test setups. See the [documentation](https://docs.goauthentik.io/docs/install-config/install/docker-compose/).
|
||||
- Kubernetes (Helm Chart): recommended for larger setups. See the [documentation](https://docs.goauthentik.io/docs/install-config/install/kubernetes/) and the Helm chart [repository](https://github.com/goauthentik/helm).
|
||||
- AWS CloudFormation: deploy on AWS using our official templates. See the [documentation](https://docs.goauthentik.io/docs/install-config/install/aws/).
|
||||
- DigitalOcean Marketplace: one-click deployment via the official Marketplace app. See the [app listing](https://marketplace.digitalocean.com/apps/authentik).
|
||||
For small/test setups it is recommended to use Docker Compose; refer to the [documentation](https://goauthentik.io/docs/installation/docker-compose/?utm_source=github).
|
||||
|
||||
For bigger setups, there is a Helm Chart [here](https://github.com/goauthentik/helm). This is documented [here](https://goauthentik.io/docs/installation/kubernetes/?utm_source=github).
|
||||
|
||||
## Screenshots
|
||||
|
||||
@@ -33,20 +32,14 @@ Our [enterprise offering](https://goauthentik.io/pricing) is available for organ
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
## Development and contributions
|
||||
## Development
|
||||
|
||||
See the [Developer Documentation](https://docs.goauthentik.io/docs/developer-docs/) for information about setting up local build environments, testing your contributions, and our contribution process.
|
||||
See [Developer Documentation](https://docs.goauthentik.io/docs/developer-docs/?utm_source=github)
|
||||
|
||||
## Security
|
||||
|
||||
Please see [SECURITY.md](SECURITY.md).
|
||||
See [SECURITY.md](SECURITY.md)
|
||||
|
||||
## Adoption
|
||||
## Adoption and Contributions
|
||||
|
||||
Using authentik? We'd love to hear your story and feature your logo. Email us at [hello@goauthentik.io](mailto:hello@goauthentik.io) or open a GitHub Issue/PR!
|
||||
|
||||
## License
|
||||
|
||||
[](LICENSE)
|
||||
[](website/LICENSE)
|
||||
[](authentik/enterprise/LICENSE)
|
||||
Your organization uses authentik? We'd love to add your logo to the readme and our website! Email us @ hello@goauthentik.io or open a GitHub Issue/PR! For more information on how to contribute to authentik, please refer to our [contribution guide](https://docs.goauthentik.io/docs/developer-docs?utm_source=github).
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from functools import lru_cache
|
||||
from os import environ
|
||||
|
||||
VERSION = "2025.8.4"
|
||||
VERSION = "2025.10.0-rc1"
|
||||
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"
|
||||
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@ from authentik.blueprints.v1.oci import OCI_PREFIX
|
||||
from authentik.events.logs import capture_logs
|
||||
from authentik.events.utils import sanitize_dict
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.tasks.apps import PRIORITY_HIGH
|
||||
from authentik.tasks.models import Task
|
||||
from authentik.tasks.schedules.models import Schedule
|
||||
from authentik.tenants.models import Tenant
|
||||
@@ -112,7 +111,6 @@ class BlueprintEventHandler(FileSystemEventHandler):
|
||||
@actor(
|
||||
description=_("Find blueprints as `blueprints_find` does, but return a safe dict."),
|
||||
throws=(DatabaseError, ProgrammingError, InternalError),
|
||||
priority=PRIORITY_HIGH,
|
||||
)
|
||||
def blueprints_find_dict():
|
||||
blueprints = []
|
||||
|
||||
@@ -328,12 +328,6 @@ class SessionUserSerializer(PassiveSerializer):
|
||||
original = UserSelfSerializer(required=False)
|
||||
|
||||
|
||||
class UserPasswordSetSerializer(PassiveSerializer):
|
||||
"""Payload to set a users' password directly"""
|
||||
|
||||
password = CharField(required=True)
|
||||
|
||||
|
||||
class UsersFilter(FilterSet):
|
||||
"""Filter for users"""
|
||||
|
||||
@@ -591,7 +585,12 @@ class UserViewSet(UsedByMixin, ModelViewSet):
|
||||
|
||||
@permission_required("authentik_core.reset_user_password")
|
||||
@extend_schema(
|
||||
request=UserPasswordSetSerializer,
|
||||
request=inline_serializer(
|
||||
"UserPasswordSetSerializer",
|
||||
{
|
||||
"password": CharField(required=True),
|
||||
},
|
||||
),
|
||||
responses={
|
||||
204: OpenApiResponse(description="Successfully changed password"),
|
||||
400: OpenApiResponse(description="Bad request"),
|
||||
@@ -600,11 +599,9 @@ class UserViewSet(UsedByMixin, ModelViewSet):
|
||||
@action(detail=True, methods=["POST"], permission_classes=[])
|
||||
def set_password(self, request: Request, pk: int) -> Response:
|
||||
"""Set password for user"""
|
||||
data = UserPasswordSetSerializer(data=request.data)
|
||||
data.is_valid(raise_exception=True)
|
||||
user: User = self.get_object()
|
||||
try:
|
||||
user.set_password(data.validated_data["password"], request=request)
|
||||
user.set_password(request.data.get("password"), request=request)
|
||||
user.save()
|
||||
except (ValidationError, IntegrityError) as exc:
|
||||
LOGGER.debug("Failed to set password", exc=exc)
|
||||
|
||||
@@ -21,8 +21,6 @@ from rest_framework.serializers import (
|
||||
raise_errors_on_nested_writes,
|
||||
)
|
||||
|
||||
from authentik.rbac.permissions import assign_initial_permissions
|
||||
|
||||
|
||||
def is_dict(value: Any):
|
||||
"""Ensure a value is a dictionary, useful for JSONFields"""
|
||||
@@ -52,15 +50,6 @@ class ModelSerializer(BaseModelSerializer):
|
||||
serializer_field_mapping = BaseModelSerializer.serializer_field_mapping.copy()
|
||||
serializer_field_mapping[models.JSONField] = JSONDictField
|
||||
|
||||
def create(self, validated_data):
|
||||
instance = super().create(validated_data)
|
||||
|
||||
request = self.context.get("request")
|
||||
if request and hasattr(request, "user") and not request.user.is_anonymous:
|
||||
assign_initial_permissions(request.user, instance)
|
||||
|
||||
return instance
|
||||
|
||||
def update(self, instance: Model, validated_data):
|
||||
raise_errors_on_nested_writes("update", self, validated_data)
|
||||
info = model_meta.get_field_info(instance)
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 5.1.12 on 2025-09-25 13:39
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_core", "0050_user_last_updated_and_more"),
|
||||
("authentik_rbac", "0006_alter_role_options"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddIndex(
|
||||
model_name="group",
|
||||
index=models.Index(fields=["is_superuser"], name="authentik_c_is_supe_1e5a97_idx"),
|
||||
),
|
||||
]
|
||||
@@ -200,10 +200,7 @@ class Group(SerializerModel, AttributesMixin):
|
||||
"parent",
|
||||
),
|
||||
)
|
||||
indexes = (
|
||||
models.Index(fields=["name"]),
|
||||
models.Index(fields=["is_superuser"]),
|
||||
)
|
||||
indexes = [models.Index(fields=["name"])]
|
||||
verbose_name = _("Group")
|
||||
verbose_name_plural = _("Groups")
|
||||
permissions = [
|
||||
|
||||
@@ -102,16 +102,6 @@ class TestUsersAPI(APITestCase):
|
||||
self.admin.refresh_from_db()
|
||||
self.assertTrue(self.admin.check_password(new_pw))
|
||||
|
||||
def test_set_password_blank(self):
|
||||
"""Test Direct password set"""
|
||||
self.client.force_login(self.admin)
|
||||
response = self.client.post(
|
||||
reverse("authentik_api:user-set-password", kwargs={"pk": self.admin.pk}),
|
||||
data={"password": ""},
|
||||
)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertJSONEqual(response.content, {"password": ["This field may not be blank."]})
|
||||
|
||||
def test_recovery(self):
|
||||
"""Test user recovery link"""
|
||||
flow = create_test_flow(
|
||||
|
||||
@@ -46,5 +46,5 @@ class FlowStageBindingViewSet(UsedByMixin, ModelViewSet):
|
||||
serializer_class = FlowStageBindingSerializer
|
||||
filterset_fields = "__all__"
|
||||
search_fields = ["stage__name"]
|
||||
ordering = ["order", "pk"]
|
||||
ordering_fields = ["order", "stage__name", "target__uuid", "pk"]
|
||||
ordering = ["order"]
|
||||
ordering_fields = ["order", "stage__name"]
|
||||
|
||||
@@ -19,7 +19,7 @@ def start_debug_server(**kwargs) -> bool:
|
||||
)
|
||||
return False
|
||||
|
||||
listen: str = CONFIG.get("listen.debug_py", "127.0.0.1:9901")
|
||||
listen: str = CONFIG.get("listen.listen_debug_py", "127.0.0.1:9901")
|
||||
host, _, port = listen.rpartition(":")
|
||||
try:
|
||||
debugpy.listen((host, int(port)), **kwargs) # nosec
|
||||
|
||||
@@ -31,14 +31,14 @@ postgresql:
|
||||
# host: replica1.example.com
|
||||
|
||||
listen:
|
||||
http: 0.0.0.0:9000
|
||||
https: 0.0.0.0:9443
|
||||
ldap: 0.0.0.0:3389
|
||||
ldaps: 0.0.0.0:6636
|
||||
radius: 0.0.0.0:1812
|
||||
metrics: 0.0.0.0:9300
|
||||
debug: 0.0.0.0:9900
|
||||
debug_py: 0.0.0.0:9901
|
||||
listen_http: 0.0.0.0:9000
|
||||
listen_https: 0.0.0.0:9443
|
||||
listen_ldap: 0.0.0.0:3389
|
||||
listen_ldaps: 0.0.0.0:6636
|
||||
listen_radius: 0.0.0.0:1812
|
||||
listen_metrics: 0.0.0.0:9300
|
||||
listen_debug: 0.0.0.0:9900
|
||||
listen_debug_py: 0.0.0.0:9901
|
||||
trusted_proxy_cidrs:
|
||||
- 127.0.0.0/8
|
||||
- 10.0.0.0/8
|
||||
@@ -152,7 +152,7 @@ worker:
|
||||
processes: 1
|
||||
threads: 2
|
||||
consumer_listen_timeout: "seconds=30"
|
||||
task_max_retries: 5
|
||||
task_max_retries: 20
|
||||
task_default_time_limit: "minutes=10"
|
||||
lock_purge_interval: "minutes=1"
|
||||
task_purge_interval: "days=1"
|
||||
|
||||
@@ -43,9 +43,7 @@ def structlog_configure():
|
||||
structlog.stdlib.PositionalArgumentsFormatter(),
|
||||
structlog.processors.TimeStamper(fmt="iso", utc=False),
|
||||
structlog.processors.StackInfoRenderer(),
|
||||
structlog.processors.ExceptionRenderer(
|
||||
structlog.processors.ExceptionDictTransformer(show_locals=CONFIG.get_bool("debug"))
|
||||
),
|
||||
structlog.processors.dict_tracebacks,
|
||||
structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
|
||||
],
|
||||
logger_factory=structlog.stdlib.LoggerFactory(),
|
||||
@@ -67,14 +65,7 @@ def get_logger_config():
|
||||
"json": {
|
||||
"()": structlog.stdlib.ProcessorFormatter,
|
||||
"processor": structlog.processors.JSONRenderer(sort_keys=True),
|
||||
"foreign_pre_chain": LOG_PRE_CHAIN
|
||||
+ [
|
||||
structlog.processors.ExceptionRenderer(
|
||||
structlog.processors.ExceptionDictTransformer(
|
||||
show_locals=CONFIG.get_bool("debug")
|
||||
)
|
||||
),
|
||||
],
|
||||
"foreign_pre_chain": LOG_PRE_CHAIN + [structlog.processors.dict_tracebacks],
|
||||
},
|
||||
"console": {
|
||||
"()": structlog.stdlib.ProcessorFormatter,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from dramatiq.actor import Actor
|
||||
from dramatiq.results.errors import ResultFailure
|
||||
from drf_spectacular.utils import extend_schema
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.fields import BooleanField, CharField, ChoiceField
|
||||
@@ -111,13 +110,9 @@ class OutgoingSyncProviderStatusMixin:
|
||||
"override_dry_run": params.validated_data["override_dry_run"],
|
||||
"pk": params.validated_data["sync_object_id"],
|
||||
},
|
||||
retries=0,
|
||||
rel_obj=provider,
|
||||
)
|
||||
try:
|
||||
msg.get_result(block=True)
|
||||
except ResultFailure:
|
||||
pass
|
||||
msg.get_result(block=True)
|
||||
task: Task = msg.options["task"]
|
||||
task.refresh_from_db()
|
||||
return Response(SyncObjectResultSerializer(instance={"messages": task._messages}).data)
|
||||
|
||||
@@ -20,7 +20,6 @@ from authentik.lib.sync.outgoing.exceptions import (
|
||||
TransientSyncException,
|
||||
)
|
||||
from authentik.lib.sync.outgoing.models import OutgoingSyncProvider
|
||||
from authentik.lib.utils.errors import exception_to_dict
|
||||
from authentik.lib.utils.reflection import class_to_path, path_to_class
|
||||
from authentik.tasks.models import Task
|
||||
|
||||
@@ -165,17 +164,16 @@ class SyncTasks:
|
||||
except BadRequestSyncException as exc:
|
||||
self.logger.warning("failed to sync object", exc=exc, obj=obj)
|
||||
task.warning(
|
||||
f"Failed to sync {str(obj)} due to error: {str(exc)}",
|
||||
f"Failed to sync {obj._meta.verbose_name} {str(obj)} due to error: {str(exc)}",
|
||||
arguments=exc.args[1:],
|
||||
obj=sanitize_item(obj),
|
||||
exception=exception_to_dict(exc),
|
||||
)
|
||||
except TransientSyncException as exc:
|
||||
self.logger.warning("failed to sync object", exc=exc, user=obj)
|
||||
task.warning(
|
||||
f"Failed to sync {str(obj)} due to " f"transient error: {str(exc)}",
|
||||
f"Failed to sync {obj._meta.verbose_name} {str(obj)} due to "
|
||||
"transient error: {str(exc)}",
|
||||
obj=sanitize_item(obj),
|
||||
exception=exception_to_dict(exc),
|
||||
)
|
||||
except StopSync as exc:
|
||||
self.logger.warning("Stopping sync", exc=exc)
|
||||
|
||||
@@ -4,11 +4,9 @@ from traceback import extract_tb
|
||||
|
||||
from structlog.tracebacks import ExceptionDictTransformer
|
||||
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.utils.reflection import class_to_path
|
||||
|
||||
TRACEBACK_HEADER = "Traceback (most recent call last):"
|
||||
_exception_transformer = ExceptionDictTransformer(show_locals=CONFIG.get_bool("debug"))
|
||||
|
||||
|
||||
def exception_to_string(exc: Exception) -> str:
|
||||
@@ -25,4 +23,4 @@ def exception_to_string(exc: Exception) -> str:
|
||||
|
||||
def exception_to_dict(exc: Exception) -> dict:
|
||||
"""Format exception as a dictionary"""
|
||||
return _exception_transformer((type(exc), exc, exc.__traceback__))
|
||||
return ExceptionDictTransformer()((type(exc), exc, exc.__traceback__))
|
||||
|
||||
@@ -13,7 +13,6 @@ from urllib3.exceptions import HTTPError
|
||||
from yaml import dump_all
|
||||
|
||||
from authentik.events.logs import LogEvent, capture_logs
|
||||
from authentik.lib.utils.reflection import class_to_path
|
||||
from authentik.outposts.controllers.base import BaseClient, BaseController, ControllerException
|
||||
from authentik.outposts.controllers.k8s.base import KubernetesObjectReconciler
|
||||
from authentik.outposts.controllers.k8s.deployment import DeploymentReconciler
|
||||
@@ -106,7 +105,7 @@ class KubernetesController(BaseController):
|
||||
LogEvent(
|
||||
log_level="info",
|
||||
event=f"{reconcile_key.title()}: Disabled",
|
||||
logger=class_to_path(self.__class__),
|
||||
logger=str(type(self)),
|
||||
)
|
||||
)
|
||||
continue
|
||||
@@ -145,7 +144,7 @@ class KubernetesController(BaseController):
|
||||
LogEvent(
|
||||
log_level="info",
|
||||
event=f"{reconcile_key.title()}: Disabled",
|
||||
logger=class_to_path(self.__class__),
|
||||
logger=str(type(self)),
|
||||
)
|
||||
)
|
||||
continue
|
||||
|
||||
@@ -124,5 +124,4 @@ class PolicyBindingViewSet(UsedByMixin, ModelViewSet):
|
||||
serializer_class = PolicyBindingSerializer
|
||||
search_fields = ["policy__name"]
|
||||
filterset_class = PolicyBindingFilter
|
||||
ordering = ["order", "pk"]
|
||||
ordering_fields = ["order", "target__uuid", "pk"]
|
||||
ordering = ["target", "order"]
|
||||
|
||||
@@ -26,6 +26,7 @@ HIST_POLICIES_EXECUTION_TIME = Histogram(
|
||||
"binding_order",
|
||||
"binding_target_type",
|
||||
"binding_target_name",
|
||||
"object_pk",
|
||||
"object_type",
|
||||
"mode",
|
||||
],
|
||||
|
||||
@@ -86,6 +86,7 @@ class PolicyEngine:
|
||||
binding_order=binding.order,
|
||||
binding_target_type=binding.target_type,
|
||||
binding_target_name=binding.target_name,
|
||||
object_pk=str(self.request.obj.pk),
|
||||
object_type=class_to_path(self.request.obj.__class__),
|
||||
mode="cache_retrieve",
|
||||
).time():
|
||||
|
||||
@@ -131,6 +131,7 @@ class PolicyProcess(PROCESS_CLASS):
|
||||
binding_order=self.binding.order,
|
||||
binding_target_type=self.binding.target_type,
|
||||
binding_target_name=self.binding.target_name,
|
||||
object_pk=str(self.request.obj.pk) if self.request.obj else "",
|
||||
object_type=class_to_path(self.request.obj.__class__) if self.request.obj else "",
|
||||
mode="execute_process",
|
||||
).time(),
|
||||
|
||||
@@ -8,12 +8,7 @@ from jwt import decode
|
||||
|
||||
from authentik.blueprints.tests import apply_blueprint
|
||||
from authentik.core.models import Application, Group, Token, TokenIntents, UserTypes
|
||||
from authentik.core.tests.utils import (
|
||||
create_test_admin_user,
|
||||
create_test_cert,
|
||||
create_test_flow,
|
||||
create_test_user,
|
||||
)
|
||||
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
|
||||
from authentik.policies.models import PolicyBinding
|
||||
from authentik.providers.oauth2.constants import (
|
||||
GRANT_TYPE_CLIENT_CREDENTIALS,
|
||||
@@ -187,47 +182,6 @@ class TestTokenClientCredentialsUserNamePassword(OAuthTestCase):
|
||||
self.assertEqual(jwt["given_name"], self.user.name)
|
||||
self.assertEqual(jwt["preferred_username"], self.user.username)
|
||||
|
||||
def test_successful_two_tokens(self):
|
||||
"""test successful when two app passwords with the same key exist"""
|
||||
Token.objects.create(
|
||||
identifier="sa-token-two",
|
||||
user=create_test_user(),
|
||||
intent=TokenIntents.INTENT_APP_PASSWORD,
|
||||
expiring=False,
|
||||
key=self.token.key,
|
||||
)
|
||||
|
||||
response = self.client.post(
|
||||
reverse("authentik_providers_oauth2:token"),
|
||||
{
|
||||
"grant_type": GRANT_TYPE_CLIENT_CREDENTIALS,
|
||||
"scope": f"{SCOPE_OPENID} {SCOPE_OPENID_EMAIL} {SCOPE_OPENID_PROFILE}",
|
||||
"client_id": self.provider.client_id,
|
||||
"username": "sa",
|
||||
"password": self.token.key,
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
body = loads(response.content.decode())
|
||||
self.assertEqual(body["token_type"], TOKEN_TYPE)
|
||||
_, alg = self.provider.jwt_key
|
||||
jwt = decode(
|
||||
body["access_token"],
|
||||
key=self.provider.signing_key.public_key,
|
||||
algorithms=[alg],
|
||||
audience=self.provider.client_id,
|
||||
)
|
||||
self.assertEqual(jwt["given_name"], self.user.name)
|
||||
self.assertEqual(jwt["preferred_username"], self.user.username)
|
||||
jwt = decode(
|
||||
body["id_token"],
|
||||
key=self.provider.signing_key.public_key,
|
||||
algorithms=[alg],
|
||||
audience=self.provider.client_id,
|
||||
)
|
||||
self.assertEqual(jwt["given_name"], self.user.name)
|
||||
self.assertEqual(jwt["preferred_username"], self.user.username)
|
||||
|
||||
def test_successful_password(self):
|
||||
"""test successful (password grant)"""
|
||||
response = self.client.post(
|
||||
|
||||
@@ -4,18 +4,17 @@ import re
|
||||
import uuid
|
||||
from base64 import b64decode
|
||||
from binascii import Error
|
||||
from time import time
|
||||
from typing import Any
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from django.http import HttpRequest, HttpResponse, JsonResponse
|
||||
from django.http.response import HttpResponseRedirect
|
||||
from django.utils.cache import patch_vary_headers
|
||||
from django.utils.timezone import now
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.core.middleware import CTX_AUTH_VIA, KEY_USER
|
||||
from authentik.events.models import Event, EventAction
|
||||
from authentik.lib.utils.time import timedelta_from_string
|
||||
from authentik.providers.oauth2.errors import BearerTokenError
|
||||
from authentik.providers.oauth2.id_token import hash_session_key
|
||||
from authentik.providers.oauth2.models import AccessToken, OAuth2Provider
|
||||
@@ -230,13 +229,11 @@ def create_logout_token(
|
||||
|
||||
LOGGER.debug("Creating logout token", provider=provider, sub=sub)
|
||||
|
||||
_now = now()
|
||||
# Create the logout token payload
|
||||
payload = {
|
||||
"iss": str(iss),
|
||||
"aud": provider.client_id,
|
||||
"iat": int(_now.timestamp()),
|
||||
"exp": int((_now + timedelta_from_string(provider.access_token_validity)).timestamp()),
|
||||
"iat": int(time()),
|
||||
"jti": str(uuid.uuid4()),
|
||||
"events": {
|
||||
"http://schemas.openid.net/event/backchannel-logout": {},
|
||||
|
||||
@@ -340,7 +340,7 @@ class TokenParams:
|
||||
if not user:
|
||||
raise TokenError("invalid_grant")
|
||||
token: Token = Token.filter_not_expired(
|
||||
key=password, intent=TokenIntents.INTENT_APP_PASSWORD, user=user
|
||||
key=password, intent=TokenIntents.INTENT_APP_PASSWORD
|
||||
).first()
|
||||
if not token or token.user.uid != user.uid:
|
||||
raise TokenError("invalid_grant")
|
||||
|
||||
@@ -13,7 +13,7 @@ def migrate_sessions(apps, schema_editor):
|
||||
for token in ConnectionToken.objects.using(db_alias).all():
|
||||
token.session = (
|
||||
AuthenticatedSession.objects.using(db_alias)
|
||||
.filter(session__session_key=token.old_session.session_key)
|
||||
.filter(session_key=token.old_session.session_key)
|
||||
.first()
|
||||
)
|
||||
if token.session:
|
||||
|
||||
@@ -27,8 +27,3 @@ class SCIMRequestException(TransientSyncException):
|
||||
except ValidationError:
|
||||
pass
|
||||
return self._message
|
||||
|
||||
def __str__(self):
|
||||
if self._response:
|
||||
return self._response.text
|
||||
return super().__str__()
|
||||
|
||||
@@ -72,8 +72,7 @@ class SCIMUserClient(SCIMClient[User, SCIMProviderUser, SCIMUserSchema]):
|
||||
if not self._config.filter.supported:
|
||||
raise exc
|
||||
users = self._request(
|
||||
"GET",
|
||||
f"/Users?{urlencode({'filter': f'userName eq \"{scim_user.userName}\"'})}",
|
||||
"GET", f"/Users?{urlencode({'filter': f'userName eq {scim_user.userName}'})}"
|
||||
)
|
||||
users_res = users.get("Resources", [])
|
||||
if len(users_res) < 1:
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
"""common RBAC serializers"""
|
||||
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.db.models import Q, QuerySet
|
||||
from django.db.transaction import atomic
|
||||
from django_filters.filters import CharFilter, ChoiceFilter
|
||||
@@ -18,7 +17,7 @@ from rest_framework.viewsets import GenericViewSet
|
||||
|
||||
from authentik.core.api.groups import GroupMemberSerializer
|
||||
from authentik.core.api.utils import ModelSerializer
|
||||
from authentik.core.models import Group, User, UserTypes
|
||||
from authentik.core.models import User, UserTypes
|
||||
from authentik.policies.event_matcher.models import model_choices
|
||||
from authentik.rbac.api.rbac import PermissionAssignResultSerializer, PermissionAssignSerializer
|
||||
from authentik.rbac.decorators import permission_required
|
||||
@@ -55,56 +54,26 @@ class UserAssignedPermissionFilter(FilterSet):
|
||||
model = ChoiceFilter(choices=model_choices(), method="filter_model", required=True)
|
||||
object_pk = CharFilter(method="filter_object_pk")
|
||||
|
||||
def filter_queryset(self, queryset):
|
||||
queryset = super().filter_queryset(queryset)
|
||||
data = self.form.cleaned_data
|
||||
model: str = data["model"]
|
||||
object_pk: str | None = data.get("object_pk", None)
|
||||
app, _, model = model.partition(".")
|
||||
|
||||
superuser_pks = (
|
||||
Group.objects.filter(is_superuser=True).values_list("users", flat=True).distinct()
|
||||
)
|
||||
|
||||
permissions = Permission.objects.filter(
|
||||
content_type__app_label=app,
|
||||
content_type__model=model,
|
||||
)
|
||||
|
||||
user_pks_with_model_permission = (
|
||||
permissions.order_by().values_list("user", flat=True).distinct()
|
||||
)
|
||||
user_pks_with_object_permission = []
|
||||
if object_pk:
|
||||
user_pks_with_object_permission = (
|
||||
UserObjectPermission.objects.filter(
|
||||
permission__in=permissions,
|
||||
object_pk=object_pk,
|
||||
)
|
||||
.order_by()
|
||||
.values_list("user", flat=True)
|
||||
.distinct()
|
||||
)
|
||||
|
||||
return queryset.filter(
|
||||
Q(pk__in=superuser_pks)
|
||||
| Q(pk__in=user_pks_with_model_permission)
|
||||
| Q(pk__in=user_pks_with_object_permission)
|
||||
)
|
||||
|
||||
def filter_model(self, queryset: QuerySet, name, value: str) -> QuerySet:
|
||||
"""Filter by object type"""
|
||||
# Actual filtering is handled by the above method where both `model` and `object_pk` are
|
||||
# available. Don't do anything here, this method is only left here to avoid overriding too
|
||||
# much of filter_queryset.
|
||||
return queryset
|
||||
app, _, model = value.partition(".")
|
||||
return queryset.filter(
|
||||
Q(
|
||||
user_permissions__content_type__app_label=app,
|
||||
user_permissions__content_type__model=model,
|
||||
)
|
||||
| Q(
|
||||
userobjectpermission__permission__content_type__app_label=app,
|
||||
userobjectpermission__permission__content_type__model=model,
|
||||
)
|
||||
| Q(ak_groups__is_superuser=True)
|
||||
).distinct()
|
||||
|
||||
def filter_object_pk(self, queryset: QuerySet, name, value: str) -> QuerySet:
|
||||
"""Filter by object primary key"""
|
||||
# Actual filtering is handled by the above method where both `model` and `object_pk` are
|
||||
# available. Don't do anything here, this method is only left here to avoid overriding too
|
||||
# much of filter_queryset.
|
||||
return queryset
|
||||
return queryset.filter(
|
||||
Q(userobjectpermission__object_pk=value) | Q(ak_groups__is_superuser=True),
|
||||
).distinct()
|
||||
|
||||
|
||||
class UserAssignedPermissionViewSet(ListModelMixin, GenericViewSet):
|
||||
@@ -114,7 +83,7 @@ class UserAssignedPermissionViewSet(ListModelMixin, GenericViewSet):
|
||||
ordering = ["username"]
|
||||
# The filtering is done in the filterset,
|
||||
# which has a required filter that does the heavy lifting
|
||||
queryset = User.objects.all().prefetch_related("userobjectpermission_set")
|
||||
queryset = User.objects.all()
|
||||
filterset_class = UserAssignedPermissionFilter
|
||||
|
||||
@permission_required("authentik_core.assign_user_permissions")
|
||||
|
||||
69
authentik/rbac/middleware.py
Normal file
69
authentik/rbac/middleware.py
Normal file
@@ -0,0 +1,69 @@
|
||||
"""InitialPermissions middleware"""
|
||||
|
||||
from collections.abc import Callable
|
||||
from contextvars import ContextVar
|
||||
from functools import partial
|
||||
|
||||
from django.db.models import Model
|
||||
from django.db.models.signals import post_save
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
|
||||
from authentik.core.models import User
|
||||
from authentik.rbac.permissions import assign_initial_permissions
|
||||
|
||||
_CTX_REQUEST = ContextVar[HttpRequest | None]("authentik_initial_permissions_request", default=None)
|
||||
|
||||
|
||||
class InitialPermissionsMiddleware:
|
||||
"""Register a handler for duration of request-response that assigns InitialPermissions"""
|
||||
|
||||
get_response: Callable[[HttpRequest], HttpResponse]
|
||||
|
||||
def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]):
|
||||
self.get_response = get_response
|
||||
|
||||
def get_uid(self, request_id: str) -> str:
|
||||
return f"InitialPermissionMiddleware-{request_id}"
|
||||
|
||||
def connect(self, request: HttpRequest):
|
||||
if not hasattr(request, "request_id"):
|
||||
return
|
||||
post_save.connect(
|
||||
partial(self.post_save_handler, request=request),
|
||||
dispatch_uid=self.get_uid(request.request_id),
|
||||
weak=False,
|
||||
)
|
||||
|
||||
def disconnect(self, request: HttpRequest):
|
||||
if not hasattr(request, "request_id"):
|
||||
return
|
||||
post_save.disconnect(dispatch_uid=self.get_uid(request.request_id))
|
||||
|
||||
def __call__(self, request: HttpRequest) -> HttpResponse:
|
||||
_CTX_REQUEST.set(request)
|
||||
self.connect(request)
|
||||
|
||||
response = self.get_response(request)
|
||||
|
||||
self.disconnect(request)
|
||||
_CTX_REQUEST.set(None)
|
||||
return response
|
||||
|
||||
def process_exception(self, request: HttpRequest, exception: Exception):
|
||||
self.disconnect(request)
|
||||
|
||||
def post_save_handler(
|
||||
self,
|
||||
request: HttpRequest,
|
||||
instance: Model,
|
||||
created: bool,
|
||||
**_,
|
||||
):
|
||||
if not created:
|
||||
return
|
||||
if request.request_id != _CTX_REQUEST.get().request_id:
|
||||
return
|
||||
user: User = request.user
|
||||
if not user or user.is_anonymous:
|
||||
return
|
||||
assign_initial_permissions(user, instance)
|
||||
@@ -1,24 +0,0 @@
|
||||
# Generated by Django 5.1.11 on 2025-08-29 14:42
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_rbac", "0005_initialpermissions"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name="role",
|
||||
options={
|
||||
"permissions": [
|
||||
("assign_role_permissions", "Can assign permissions to roles"),
|
||||
("unassign_role_permissions", "Can unassign permissions from roles"),
|
||||
],
|
||||
"verbose_name": "Role",
|
||||
"verbose_name_plural": "Roles",
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -71,8 +71,8 @@ class Role(SerializerModel):
|
||||
verbose_name = _("Role")
|
||||
verbose_name_plural = _("Roles")
|
||||
permissions = [
|
||||
("assign_role_permissions", _("Can assign permissions to roles")),
|
||||
("unassign_role_permissions", _("Can unassign permissions from roles")),
|
||||
("assign_role_permissions", _("Can assign permissions to users")),
|
||||
("unassign_role_permissions", _("Can unassign permissions from users")),
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -5,9 +5,12 @@ from django.db.models import Model
|
||||
from guardian.shortcuts import assign_perm
|
||||
from rest_framework.permissions import BasePermission, DjangoObjectPermissions
|
||||
from rest_framework.request import Request
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.rbac.models import InitialPermissions, InitialPermissionsMode
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
class ObjectPermissions(DjangoObjectPermissions):
|
||||
"""RBAC Permissions"""
|
||||
@@ -71,4 +74,10 @@ def assign_initial_permissions(user, instance: Model):
|
||||
if initial_permissions.mode == InitialPermissionsMode.USER
|
||||
else initial_permissions.role.group
|
||||
)
|
||||
LOGGER.debug(
|
||||
"Adding initial permission",
|
||||
initial_permission=permission,
|
||||
subject=assign_to,
|
||||
object=instance,
|
||||
)
|
||||
assign_perm(permission, assign_to, instance)
|
||||
|
||||
@@ -265,6 +265,7 @@ MIDDLEWARE = [
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
"authentik.core.middleware.ImpersonateMiddleware",
|
||||
"authentik.rbac.middleware.InitialPermissionsMiddleware",
|
||||
]
|
||||
MIDDLEWARE_LAST = [
|
||||
"django_prometheus.middleware.PrometheusAfterMiddleware",
|
||||
@@ -412,10 +413,7 @@ DRAMATIQ = {
|
||||
("dramatiq.middleware.pipelines.Pipelines", {}),
|
||||
(
|
||||
"dramatiq.middleware.retries.Retries",
|
||||
{
|
||||
"max_retries": CONFIG.get_int("worker.task_max_retries") if not TEST else 0,
|
||||
"max_backoff": 60 * 60 * 1000, # 1 hour
|
||||
},
|
||||
{"max_retries": CONFIG.get_int("worker.task_max_retries") if not TEST else 0},
|
||||
),
|
||||
("dramatiq.results.middleware.Results", {"store_results": True}),
|
||||
("django_dramatiq_postgres.middleware.CurrentTask", {}),
|
||||
|
||||
@@ -5,7 +5,6 @@ from typing import Any
|
||||
|
||||
from django.db.models import Q
|
||||
from ldap3 import SUBTREE
|
||||
from ldap3.utils.conv import escape_filter_chars
|
||||
|
||||
from authentik.core.models import Group, User
|
||||
from authentik.sources.ldap.models import LDAP_DISTINGUISHED_NAME, LDAP_UNIQUENESS, LDAPSource
|
||||
@@ -53,8 +52,7 @@ class MembershipLDAPSynchronizer(BaseLDAPSynchronizer):
|
||||
for group in page_data:
|
||||
if self._source.lookup_groups_from_user:
|
||||
group_dn = group.get("dn", {})
|
||||
escaped_dn = escape_filter_chars(group_dn)
|
||||
group_filter = f"({self._source.group_membership_field}={escaped_dn})"
|
||||
group_filter = f"({self._source.group_membership_field}={group_dn})"
|
||||
group_members = self._source.connection().extend.standard.paged_search(
|
||||
search_base=self.base_dn_users,
|
||||
search_filter=group_filter,
|
||||
|
||||
@@ -4,8 +4,6 @@ from unittest.mock import MagicMock, patch
|
||||
|
||||
from django.db.models import Q
|
||||
from django.test import TestCase
|
||||
from ldap3.core.exceptions import LDAPInvalidFilterError
|
||||
from ldap3.utils.conv import escape_filter_chars
|
||||
|
||||
from authentik.blueprints.tests import apply_blueprint
|
||||
from authentik.core.models import Group, User
|
||||
@@ -521,89 +519,3 @@ class LDAPSyncTests(TestCase):
|
||||
|
||||
self.assertFalse(User.objects.filter(username__startswith="not-in-the-source").exists())
|
||||
self.assertFalse(Group.objects.filter(name__startswith="not-in-the-source").exists())
|
||||
|
||||
def test_membership_sync_special_chars_in_group_dn(self):
|
||||
"""Test membership synchronization with special characters in group DN"""
|
||||
self.source.object_uniqueness_field = "uid"
|
||||
self.source.group_object_filter = "(objectClass=groupOfNames)"
|
||||
self.source.lookup_groups_from_user = True
|
||||
self.source.group_membership_field = "memberOf"
|
||||
|
||||
# Mock connection with group DN containing special characters
|
||||
mock_conn = MagicMock()
|
||||
|
||||
# Simulate group with special characters in DN: parentheses, backslashes, asterisks
|
||||
special_group_dn = "cn=test(group),ou=groups,dc=example,dc=com"
|
||||
backslash_group_dn = "cn=test\\group,ou=groups,dc=example,dc=com"
|
||||
asterisk_group_dn = "cn=test*group,ou=groups,dc=example,dc=com"
|
||||
|
||||
# Mock the paged_search method that would be called with the filter
|
||||
mock_standard = MagicMock()
|
||||
mock_conn.extend.standard = mock_standard
|
||||
|
||||
# Test case 1: Group DN with parentheses
|
||||
with patch("authentik.sources.ldap.models.LDAPSource.connection", return_value=mock_conn):
|
||||
membership_sync = MembershipLDAPSynchronizer(self.source, Task())
|
||||
|
||||
# Simulate group data with special characters in DN
|
||||
page_data = [{"dn": special_group_dn}]
|
||||
|
||||
# This should not raise LDAPInvalidFilterError anymore
|
||||
try:
|
||||
membership_sync.sync(page_data)
|
||||
# Verify that the filter was properly escaped
|
||||
# The call should have been made with escaped characters
|
||||
mock_standard.paged_search.assert_called()
|
||||
call_args = mock_standard.paged_search.call_args
|
||||
search_filter = call_args[1]["search_filter"]
|
||||
# The parentheses should be escaped as \28 and \29
|
||||
self.assertIn("\\28", search_filter) # Escaped (
|
||||
self.assertIn("\\29", search_filter) # Escaped )
|
||||
except LDAPInvalidFilterError:
|
||||
self.fail("LDAPInvalidFilterError should not be raised with escaped filter")
|
||||
|
||||
# Test case 2: Group DN with backslashes
|
||||
with patch("authentik.sources.ldap.models.LDAPSource.connection", return_value=mock_conn):
|
||||
membership_sync = MembershipLDAPSynchronizer(self.source, Task())
|
||||
page_data = [{"dn": backslash_group_dn}]
|
||||
|
||||
try:
|
||||
membership_sync.sync(page_data)
|
||||
call_args = mock_standard.paged_search.call_args
|
||||
search_filter = call_args[1]["search_filter"]
|
||||
# The backslash should be escaped as \5c
|
||||
self.assertIn("\\5c", search_filter) # Escaped \
|
||||
except LDAPInvalidFilterError:
|
||||
self.fail("LDAPInvalidFilterError should not be raised with escaped filter")
|
||||
|
||||
# Test case 3: Group DN with asterisks
|
||||
with patch("authentik.sources.ldap.models.LDAPSource.connection", return_value=mock_conn):
|
||||
membership_sync = MembershipLDAPSynchronizer(self.source, Task())
|
||||
page_data = [{"dn": asterisk_group_dn}]
|
||||
|
||||
try:
|
||||
membership_sync.sync(page_data)
|
||||
call_args = mock_standard.paged_search.call_args
|
||||
search_filter = call_args[1]["search_filter"]
|
||||
# The asterisk should be escaped as \2a
|
||||
self.assertIn("\\2a", search_filter) # Escaped *
|
||||
except LDAPInvalidFilterError:
|
||||
self.fail("LDAPInvalidFilterError should not be raised with escaped filter")
|
||||
|
||||
def test_escape_filter_chars_function(self):
|
||||
"""Test the escape_filter_chars function directly"""
|
||||
|
||||
# Test various special characters that need escaping
|
||||
test_cases = [
|
||||
("test(group)", "test\\28group\\29"), # parentheses
|
||||
("test\\group", "test\\5cgroup"), # backslash
|
||||
("test*group", "test\\2agroup"), # asterisk
|
||||
("test(*)group", "test\\28\\2a\\29group"), # multiple special chars
|
||||
("normalgroup", "normalgroup"), # no special chars
|
||||
("", ""), # empty string
|
||||
]
|
||||
|
||||
for input_str, expected in test_cases:
|
||||
with self.subTest(input_str=input_str):
|
||||
result = escape_filter_chars(input_str)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
@@ -96,11 +96,7 @@ class EntraIDType(SourceType):
|
||||
}
|
||||
|
||||
def get_base_group_properties(self, source, group_id, **kwargs):
|
||||
raw_groups = kwargs["info"]["raw_groups"]
|
||||
if group_id in raw_groups:
|
||||
name = raw_groups[group_id]["displayName"]
|
||||
else:
|
||||
name = group_id
|
||||
raw_group = kwargs["info"]["raw_groups"][group_id]
|
||||
return {
|
||||
"name": name,
|
||||
"name": raw_group["displayName"],
|
||||
}
|
||||
|
||||
@@ -198,7 +198,10 @@ class AuthenticatorDuoStageViewSet(UsedByMixin, ModelViewSet):
|
||||
return {"error": "", "count": created}
|
||||
except RuntimeError as exc:
|
||||
LOGGER.warning("failed to get users from duo", exc=exc)
|
||||
return {"error": str(exc), "count": created}
|
||||
return {
|
||||
"error": "An internal error occurred while importing devices.",
|
||||
"count": created,
|
||||
}
|
||||
|
||||
|
||||
class DuoDeviceSerializer(ModelSerializer):
|
||||
|
||||
@@ -168,6 +168,8 @@ class AuthenticatorDuoStageTests(FlowTestCase):
|
||||
client_secret=generate_id(),
|
||||
api_hostname=generate_id(),
|
||||
)
|
||||
|
||||
# Test missing admin credentials
|
||||
response = self.client.post(
|
||||
reverse(
|
||||
"authentik_api:authenticatorduostage-import-devices-automatic",
|
||||
@@ -178,6 +180,31 @@ class AuthenticatorDuoStageTests(FlowTestCase):
|
||||
)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
# Test internal error handling
|
||||
stage.admin_integration_key = generate_id()
|
||||
stage.admin_secret_key = generate_id()
|
||||
stage.save()
|
||||
with patch(
|
||||
"duo_client.admin.Admin.get_users_iterator",
|
||||
MagicMock(side_effect=RuntimeError("Duo API error")),
|
||||
):
|
||||
response = self.client.post(
|
||||
reverse(
|
||||
"authentik_api:authenticatorduostage-import-devices-automatic",
|
||||
kwargs={
|
||||
"pk": str(stage.pk),
|
||||
},
|
||||
),
|
||||
)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertJSONEqual(
|
||||
response.content,
|
||||
{
|
||||
"error": "An internal error occurred while importing devices.",
|
||||
"count": 0,
|
||||
},
|
||||
)
|
||||
|
||||
def test_api_import_automatic(self):
|
||||
"""test `import_devices_automatic`"""
|
||||
self.client.force_login(self.user)
|
||||
|
||||
@@ -142,11 +142,6 @@ class AuthenticatorEmailStageView(ChallengeStageView):
|
||||
user = self.get_pending_user()
|
||||
|
||||
stage: AuthenticatorEmailStage = self.executor.current_stage
|
||||
# For the moment we only allow one email device per user
|
||||
if EmailDevice.objects.filter(Q(user=user), stage=stage.pk).exists():
|
||||
return self.executor.stage_invalid(
|
||||
_("The user already has an email address registered for MFA.")
|
||||
)
|
||||
if SESSION_KEY_EMAIL_DEVICE not in self.request.session:
|
||||
device = EmailDevice(user=user, confirmed=False, stage=stage, name="Email Device")
|
||||
valid_secs: int = timedelta_from_string(stage.token_expiry).total_seconds()
|
||||
|
||||
@@ -108,17 +108,6 @@ class TestAuthenticatorEmailStage(FlowTestCase):
|
||||
)
|
||||
def test_stage_submit(self):
|
||||
"""Test stage email submission"""
|
||||
# test fail because of existing device
|
||||
response = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
)
|
||||
self.assertStageResponse(
|
||||
response,
|
||||
self.flow,
|
||||
self.user,
|
||||
component="ak-stage-access-denied",
|
||||
)
|
||||
self.device.delete()
|
||||
# Initialize the flow
|
||||
response = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
@@ -243,7 +232,6 @@ class TestAuthenticatorEmailStage(FlowTestCase):
|
||||
def test_challenge_generation(self):
|
||||
"""Test challenge generation"""
|
||||
# Test with masked email
|
||||
self.device.delete()
|
||||
response = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
)
|
||||
|
||||
@@ -148,10 +148,7 @@ class IdentificationChallengeResponse(ChallengeResponse):
|
||||
captcha_token = attrs.get("captcha_token", None)
|
||||
if not captcha_token:
|
||||
self.stage.logger.warning("Token not set for captcha attempt")
|
||||
try:
|
||||
verify_captcha_token(captcha_stage, captcha_token, client_ip)
|
||||
except ValidationError:
|
||||
raise ValidationError(_("Failed to authenticate.")) from None
|
||||
verify_captcha_token(captcha_stage, captcha_token, client_ip)
|
||||
|
||||
# Password check
|
||||
if not current_stage.password_stage:
|
||||
|
||||
@@ -194,7 +194,7 @@ class TestIdentificationStage(FlowTestCase):
|
||||
password_fields=False,
|
||||
primary_action="Log in",
|
||||
response_errors={
|
||||
"non_field_errors": [{"code": "invalid", "string": "Failed to authenticate."}]
|
||||
"non_field_errors": [{"code": "invalid", "string": "Invalid captcha response"}]
|
||||
},
|
||||
sources=[
|
||||
{
|
||||
@@ -247,7 +247,7 @@ class TestIdentificationStage(FlowTestCase):
|
||||
"non_field_errors": [
|
||||
{
|
||||
"code": "invalid",
|
||||
"string": "Failed to authenticate.",
|
||||
"string": "Invalid captcha response. Retrying may solve this issue.",
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -2,8 +2,6 @@ from authentik.blueprints.apps import ManagedAppConfig
|
||||
from authentik.lib.utils.time import fqdn_rand
|
||||
from authentik.tasks.schedules.common import ScheduleSpec
|
||||
|
||||
PRIORITY_HIGH = 1000
|
||||
|
||||
|
||||
class AuthentikTasksConfig(ManagedAppConfig):
|
||||
name = "authentik.tasks"
|
||||
|
||||
@@ -11,7 +11,7 @@ def worker_healthcheck():
|
||||
import authentik.tasks.setup # noqa
|
||||
from authentik.tasks.middleware import WorkerHealthcheckMiddleware
|
||||
|
||||
host, _, port = CONFIG.get("listen.http").rpartition(":")
|
||||
host, _, port = CONFIG.get("listen.listen_http").rpartition(":")
|
||||
|
||||
try:
|
||||
port = int(port)
|
||||
@@ -33,7 +33,7 @@ def worker_metrics():
|
||||
import authentik.tasks.setup # noqa
|
||||
from authentik.tasks.middleware import MetricsMiddleware
|
||||
|
||||
addr, _, port = CONFIG.get("listen.metrics").rpartition(":")
|
||||
addr, _, port = CONFIG.get("listen.listen_metrics").rpartition(":")
|
||||
|
||||
try:
|
||||
port = int(port)
|
||||
|
||||
@@ -14,21 +14,18 @@ from django_redis import get_redis_connection
|
||||
from dramatiq.broker import Broker
|
||||
from dramatiq.message import Message
|
||||
from dramatiq.middleware import Middleware
|
||||
from psycopg.errors import Error
|
||||
from redis.exceptions import RedisError
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik import authentik_full_version
|
||||
from authentik.events.models import Event, EventAction
|
||||
from authentik.lib.sentry import should_ignore_exception
|
||||
from authentik.lib.utils.reflection import class_to_path
|
||||
from authentik.tasks.models import Task, TaskStatus, WorkerStatus
|
||||
from authentik.tenants.models import Tenant
|
||||
from authentik.tenants.utils import get_current_tenant
|
||||
|
||||
LOGGER = get_logger()
|
||||
HEALTHCHECK_LOGGER = get_logger("authentik.worker").bind()
|
||||
DB_ERRORS = (OperationalError, Error, RedisError)
|
||||
|
||||
|
||||
class TenantMiddleware(Middleware):
|
||||
@@ -62,7 +59,7 @@ class MessagesMiddleware(Middleware):
|
||||
if task_created:
|
||||
task._messages.append(
|
||||
Task._make_message(
|
||||
class_to_path(type(self)),
|
||||
str(type(self)),
|
||||
TaskStatus.INFO,
|
||||
"Task has been queued",
|
||||
delay=delay,
|
||||
@@ -72,7 +69,7 @@ class MessagesMiddleware(Middleware):
|
||||
task._previous_messages.extend(task._messages)
|
||||
task._messages = [
|
||||
Task._make_message(
|
||||
class_to_path(type(self)),
|
||||
str(type(self)),
|
||||
TaskStatus.INFO,
|
||||
"Task will be retried",
|
||||
delay=delay,
|
||||
@@ -82,7 +79,7 @@ class MessagesMiddleware(Middleware):
|
||||
|
||||
def before_process_message(self, broker: Broker, message: Message):
|
||||
task: Task = message.options["task"]
|
||||
task.log(class_to_path(type(self)), TaskStatus.INFO, "Task is being processed")
|
||||
task.log(str(type(self)), TaskStatus.INFO, "Task is being processed")
|
||||
|
||||
def after_process_message(
|
||||
self,
|
||||
@@ -94,16 +91,12 @@ class MessagesMiddleware(Middleware):
|
||||
):
|
||||
task: Task = message.options["task"]
|
||||
if exception is None:
|
||||
task.log(
|
||||
class_to_path(type(self)),
|
||||
TaskStatus.INFO,
|
||||
"Task finished processing without errors",
|
||||
)
|
||||
task.log(str(type(self)), TaskStatus.INFO, "Task finished processing without errors")
|
||||
return
|
||||
if should_ignore_exception(exception):
|
||||
return
|
||||
task.log(
|
||||
class_to_path(type(self)),
|
||||
str(type(self)),
|
||||
TaskStatus.ERROR,
|
||||
exception,
|
||||
)
|
||||
@@ -120,7 +113,7 @@ class MessagesMiddleware(Middleware):
|
||||
|
||||
def after_skip_message(self, broker: Broker, message: Message):
|
||||
task: Task = message.options["task"]
|
||||
task.log(class_to_path(type(self)), TaskStatus.INFO, "Task has been skipped")
|
||||
task.log(str(type(self)), TaskStatus.INFO, "Task has been skipped")
|
||||
|
||||
|
||||
class LoggingMiddleware(Middleware):
|
||||
@@ -182,7 +175,7 @@ class _healthcheck_handler(BaseHTTPRequestHandler):
|
||||
redis_conn = get_redis_connection()
|
||||
redis_conn.ping()
|
||||
self.send_response(200)
|
||||
except DB_ERRORS: # pragma: no cover
|
||||
except (OperationalError, RedisError): # pragma: no cover
|
||||
self.send_response(503)
|
||||
self.send_header("Content-Type", "text/plain; charset=utf-8")
|
||||
self.send_header("Content-Length", "0")
|
||||
@@ -223,14 +216,6 @@ class WorkerStatusMiddleware(Middleware):
|
||||
hostname=socket.gethostname(),
|
||||
version=authentik_full_version(),
|
||||
)
|
||||
while True:
|
||||
try:
|
||||
WorkerStatusMiddleware.keep(status)
|
||||
except DB_ERRORS: # pragma: no cover
|
||||
sleep(10)
|
||||
pass
|
||||
|
||||
def keep(status: WorkerStatus):
|
||||
lock_id = f"goauthentik.io/worker/status/{status.pk}"
|
||||
with pglock.advisory(lock_id, side_effect=pglock.Raise):
|
||||
while True:
|
||||
|
||||
@@ -107,6 +107,7 @@ class ScheduleViewSet(
|
||||
"rel_obj_content_type__app_label",
|
||||
"rel_obj_content_type__model",
|
||||
"rel_obj_id",
|
||||
"description",
|
||||
)
|
||||
filterset_class = ScheduleFilter
|
||||
ordering = (
|
||||
|
||||
0
authentik/tenants/management/commands/__init__.py
Normal file
0
authentik/tenants/management/commands/__init__.py
Normal file
10
authentik/tenants/management/commands/createsuperuser.py
Normal file
10
authentik/tenants/management/commands/createsuperuser.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "ak createsuperuser should not be used. Instead, use ak create_admin_group"
|
||||
|
||||
def handle(self, *args, **options): # noqa: ANN001, D401
|
||||
raise RuntimeError(
|
||||
"ak createsuperuser should not be used. Instead, use ak create_admin_group"
|
||||
)
|
||||
@@ -2,7 +2,7 @@
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://goauthentik.io/blueprints/schema.json",
|
||||
"type": "object",
|
||||
"title": "authentik 2025.8.4 Blueprint schema",
|
||||
"title": "authentik 2025.10.0-rc1 Blueprint schema",
|
||||
"required": [
|
||||
"version",
|
||||
"entries"
|
||||
|
||||
@@ -133,6 +133,6 @@ func checkWorker() int {
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("successfully checked health")
|
||||
log.Debug("successfully checked health")
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ services:
|
||||
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
|
||||
AUTHENTIK_REDIS__HOST: redis
|
||||
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.8.4}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.10.0-rc1}
|
||||
ports:
|
||||
- ${COMPOSE_PORT_HTTP:-9000}:9000
|
||||
- ${COMPOSE_PORT_HTTPS:-9443}:9443
|
||||
@@ -72,7 +72,7 @@ services:
|
||||
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
|
||||
AUTHENTIK_REDIS__HOST: redis
|
||||
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.8.4}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.10.0-rc1}
|
||||
restart: unless-stopped
|
||||
user: root
|
||||
volumes:
|
||||
|
||||
8
go.mod
8
go.mod
@@ -6,7 +6,7 @@ require (
|
||||
beryju.io/ldap v0.1.0
|
||||
github.com/avast/retry-go/v4 v4.6.1
|
||||
github.com/coreos/go-oidc/v3 v3.15.0
|
||||
github.com/getsentry/sentry-go v0.35.0
|
||||
github.com/getsentry/sentry-go v0.35.1
|
||||
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
|
||||
github.com/go-ldap/ldap/v3 v3.4.11
|
||||
github.com/go-openapi/runtime v0.28.0
|
||||
@@ -23,13 +23,13 @@ require (
|
||||
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
|
||||
github.com/pires/go-proxyproto v0.8.1
|
||||
github.com/prometheus/client_golang v1.23.0
|
||||
github.com/redis/go-redis/v9 v9.11.0
|
||||
github.com/redis/go-redis/v9 v9.12.1
|
||||
github.com/sethvargo/go-envconfig v1.3.0
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/stretchr/testify v1.11.0
|
||||
github.com/wwt/guac v1.3.2
|
||||
goauthentik.io/api/v3 v3.2025064.8
|
||||
goauthentik.io/api/v3 v3.2025100.2
|
||||
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
||||
golang.org/x/oauth2 v0.30.0
|
||||
golang.org/x/sync v0.16.0
|
||||
|
||||
16
go.sum
16
go.sum
@@ -26,8 +26,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
|
||||
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/getsentry/sentry-go v0.35.0 h1:+FJNlnjJsZMG3g0/rmmP7GiKjQoUF5EXfEtBwtPtkzY=
|
||||
github.com/getsentry/sentry-go v0.35.0/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE=
|
||||
github.com/getsentry/sentry-go v0.35.1 h1:iopow6UVLE2aXu46xKVIs8Z9D/YZkJrHkgozrxa+tOQ=
|
||||
github.com/getsentry/sentry-go v0.35.1/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||
@@ -148,8 +148,8 @@ github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2
|
||||
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
|
||||
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||
github.com/redis/go-redis/v9 v9.11.0 h1:E3S08Gl/nJNn5vkxd2i78wZxWAPNZgUNTp8WIJUAiIs=
|
||||
github.com/redis/go-redis/v9 v9.11.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
|
||||
github.com/redis/go-redis/v9 v9.12.1 h1:k5iquqv27aBtnTm2tIkROUDp8JBXhXZIVu1InSgvovg=
|
||||
github.com/redis/go-redis/v9 v9.12.1/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
@@ -169,8 +169,8 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8=
|
||||
github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/wwt/guac v1.3.2 h1:sH6OFGa/1tBs7ieWBVlZe7t6F5JAOWBry/tqQL/Vup4=
|
||||
github.com/wwt/guac v1.3.2/go.mod h1:eKm+NrnK7A88l4UBEcYNpZQGMpZRryYKoz4D/0/n1C0=
|
||||
go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80=
|
||||
@@ -185,8 +185,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.2025064.8 h1:wgegkPUtGSrOR7+Rnd0cxLVU0cEea87BatjESa6BJv0=
|
||||
goauthentik.io/api/v3 v3.2025064.8/go.mod h1:82lqAz4jxzl6Cg0YDbhNtvvTG2rm6605ZhdJFnbbsl8=
|
||||
goauthentik.io/api/v3 v3.2025100.2 h1:OF8qEpn6PzZFlB16RzL51RSIyFOY234gAWfd8/kjzhc=
|
||||
goauthentik.io/api/v3 v3.2025100.2/go.mod h1:82lqAz4jxzl6Cg0YDbhNtvvTG2rm6605ZhdJFnbbsl8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||
|
||||
@@ -37,13 +37,13 @@ type RedisConfig struct {
|
||||
}
|
||||
|
||||
type ListenConfig struct {
|
||||
HTTP string `yaml:"http" env:"HTTP, overwrite"`
|
||||
HTTPS string `yaml:"https" env:"HTTPS, overwrite"`
|
||||
LDAP string `yaml:"ldap" env:"LDAP, overwrite"`
|
||||
LDAPS string `yaml:"ldaps" env:"LDAPS, overwrite"`
|
||||
Radius string `yaml:"radius" env:"RADIUS, overwrite"`
|
||||
Metrics string `yaml:"metrics" env:"METRICS, overwrite"`
|
||||
Debug string `yaml:"debug" env:"DEBUG, overwrite"`
|
||||
HTTP string `yaml:"listen_http" env:"HTTP, overwrite"`
|
||||
HTTPS string `yaml:"listen_https" env:"HTTPS, overwrite"`
|
||||
LDAP string `yaml:"listen_ldap" env:"LDAP, overwrite"`
|
||||
LDAPS string `yaml:"listen_ldaps" env:"LDAPS, overwrite"`
|
||||
Radius string `yaml:"listen_radius" env:"RADIUS, overwrite"`
|
||||
Metrics string `yaml:"listen_metrics" env:"METRICS, overwrite"`
|
||||
Debug string `yaml:"listen_debug" env:"DEBUG, overwrite"`
|
||||
TrustedProxyCIDRs []string `yaml:"trusted_proxy_cidrs" env:"TRUSTED_PROXY_CIDRS, overwrite"`
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
2025.8.4
|
||||
2025.10.0-rc1
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"beryju.io/ldap"
|
||||
|
||||
@@ -51,13 +50,10 @@ func (pi *ProviderInstance) UserEntry(u api.User) *ldap.Entry {
|
||||
constants.OCPosixAccount,
|
||||
constants.OCAKUser,
|
||||
},
|
||||
"uidNumber": {pi.GetUserUidNumber(u)},
|
||||
"gidNumber": {pi.GetUserGidNumber(u)},
|
||||
"homeDirectory": {fmt.Sprintf("/home/%s", u.Username)},
|
||||
"sn": {u.Name},
|
||||
"pwdChangedTime": {u.PasswordChangeDate.In(time.UTC).Format("20060102150405Z")},
|
||||
"createTimestamp": {u.DateJoined.In(time.UTC).Format("20060102150405Z")},
|
||||
"modifyTimestamp": {u.LastUpdated.In(time.UTC).Format("20060102150405Z")},
|
||||
"uidNumber": {pi.GetUserUidNumber(u)},
|
||||
"gidNumber": {pi.GetUserGidNumber(u)},
|
||||
"homeDirectory": {fmt.Sprintf("/home/%s", u.Username)},
|
||||
"sn": {u.Name},
|
||||
})
|
||||
return &ldap.Entry{DN: dn, Attributes: attrs}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,6 @@ func (ds *DirectSearcher) SearchSubschema(req *search.Request) (ldap.ServerSearc
|
||||
"( 1.2.840.113556.1.4.44 NAME 'homeDirectory' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
|
||||
"( 1.2.840.113556.1.4.750 NAME 'groupType' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )",
|
||||
"( 1.2.840.113556.1.4.782 NAME 'objectCategory' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' SINGLE-VALUE )",
|
||||
"( 1.3.6.1.4.1.42.2.27.8.1.16 NAME 'pwdChangedTime' SYNTAX '1.3.6.1.4.1.1466.115.121.1.24' SINGLE-VALUE NO-USER-MODIFICATION )",
|
||||
"( 1.3.6.1.1.1.1.0 NAME 'uidNumber' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )",
|
||||
"( 1.3.6.1.1.1.1.1 NAME 'gidNumber' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )",
|
||||
"( 1.3.6.1.1.1.1.12 NAME 'memberUid' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )",
|
||||
|
||||
@@ -57,7 +57,7 @@ func (ms *MemorySearcher) fetch() {
|
||||
Logger: ms.log,
|
||||
})
|
||||
ms.users = users
|
||||
groups, _ := ak.Paginator(ms.si.GetAPIClient().CoreApi.CoreGroupsList(context.TODO()).IncludeUsers(true).IncludeChildren(true), ak.PaginatorOptions{
|
||||
groups, _ := ak.Paginator(ms.si.GetAPIClient().CoreApi.CoreGroupsList(context.TODO()).IncludeUsers(true), ak.PaginatorOptions{
|
||||
PageSize: 100,
|
||||
Logger: ms.log,
|
||||
})
|
||||
|
||||
91
internal/utils/web/http_compress.go
Normal file
91
internal/utils/web/http_compress.go
Normal file
@@ -0,0 +1,91 @@
|
||||
// https://github.com/gorilla/handlers/issues/259#issuecomment-2671695039
|
||||
package web
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/handlers"
|
||||
)
|
||||
|
||||
// compressHandler is an HTTP handler that adds the Content-Encoding header
|
||||
// back to responses when removed by the http.FileServer.
|
||||
//
|
||||
// handlers.CompressHandler(newCompressHandler(http.FileServer(...)))
|
||||
type compressHandler struct {
|
||||
// handler is an HTTP handler, usually an http.FileServer.
|
||||
handler http.Handler
|
||||
}
|
||||
|
||||
var _ http.Handler = &compressHandler{}
|
||||
|
||||
func NewCompressHandler(handler http.Handler) http.Handler {
|
||||
h := &compressHandler{
|
||||
handler: handler,
|
||||
}
|
||||
return handlers.CompressHandler(h)
|
||||
}
|
||||
|
||||
func (h *compressHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// The wrapped response writer saves the incoming content encoding so
|
||||
// it can be restored when writing the response headers.
|
||||
cw := &compressedResponseWriter{
|
||||
encoding: w.Header().Get("Content-Encoding"),
|
||||
fixed: false,
|
||||
responseWriter: w,
|
||||
}
|
||||
h.handler.ServeHTTP(cw, r)
|
||||
}
|
||||
|
||||
// compressedResponseWriter is an http.ResponseWriter that ensures that a
|
||||
// previously-set Content-Encoding header is in place before writing the
|
||||
// response.
|
||||
type compressedResponseWriter struct {
|
||||
encoding string
|
||||
fixed bool
|
||||
responseWriter http.ResponseWriter
|
||||
}
|
||||
|
||||
var _ http.ResponseWriter = &compressedResponseWriter{}
|
||||
|
||||
func (w *compressedResponseWriter) Header() http.Header {
|
||||
return w.responseWriter.Header()
|
||||
}
|
||||
|
||||
func (w *compressedResponseWriter) fixContentEncoding() {
|
||||
if w.fixed {
|
||||
return
|
||||
}
|
||||
w.fixed = true
|
||||
// The Go 1.23 http.FileServer() removes headers like Content-Encoding
|
||||
// from error responses. This breaks gzip and deflate encoding.
|
||||
// https://github.com/gorilla/handlers/issues/259
|
||||
// https://github.com/golang/go/issues/66343
|
||||
if w.encoding == "gzip" || w.encoding == "deflate" {
|
||||
if w.Header().Get("Content-Encoding") == "" {
|
||||
w.Header().Set("Content-Encoding", w.encoding)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *compressedResponseWriter) Write(data []byte) (int, error) {
|
||||
w.fixContentEncoding()
|
||||
return w.responseWriter.Write(data)
|
||||
}
|
||||
|
||||
func (w *compressedResponseWriter) WriteHeader(statusCode int) {
|
||||
w.fixContentEncoding()
|
||||
w.responseWriter.WriteHeader(statusCode)
|
||||
}
|
||||
|
||||
func (w *compressedResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
if hj, ok := w.responseWriter.(http.Hijacker); ok {
|
||||
return hj.Hijack()
|
||||
}
|
||||
return nil, nil, http.ErrNotSupported
|
||||
}
|
||||
|
||||
// Ensure our compressedResponseWriter implements the necessary interfaces.
|
||||
var _ http.ResponseWriter = &compressedResponseWriter{}
|
||||
var _ http.Hijacker = &compressedResponseWriter{}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
@@ -15,6 +16,7 @@ import (
|
||||
"goauthentik.io/internal/config"
|
||||
"goauthentik.io/internal/utils/sentry"
|
||||
"goauthentik.io/internal/utils/web"
|
||||
staticWeb "goauthentik.io/web"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -88,19 +90,81 @@ func (ws *WebServer) configureProxy() {
|
||||
}
|
||||
|
||||
func (ws *WebServer) proxyErrorHandler(rw http.ResponseWriter, req *http.Request, err error) {
|
||||
if !errors.Is(err, ErrAuthentikStarting) {
|
||||
ws.log.WithError(err).Warning("failed to proxy to backend")
|
||||
accept := req.Header.Get("Accept")
|
||||
|
||||
header := rw.Header()
|
||||
|
||||
if errors.Is(err, ErrAuthentikStarting) {
|
||||
header.Set("Retry-After", "5")
|
||||
|
||||
if strings.Contains(accept, "application/json") {
|
||||
header.Set("Content-Type", "application/json")
|
||||
|
||||
err = json.NewEncoder(rw).Encode(map[string]string{
|
||||
"error": "authentik starting",
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
ws.log.WithError(err).Warning("failed to write error message")
|
||||
return
|
||||
}
|
||||
} else if strings.Contains(accept, "text/html") {
|
||||
header.Set("Content-Type", "text/html")
|
||||
rw.WriteHeader(http.StatusServiceUnavailable)
|
||||
|
||||
loadingSplashFile, err := staticWeb.StaticDir.Open("standalone/loading/startup.html")
|
||||
|
||||
if err != nil {
|
||||
ws.log.WithError(err).Warning("failed to open startup splash screen")
|
||||
return
|
||||
}
|
||||
|
||||
loadingSplashHTML, err := io.ReadAll(loadingSplashFile)
|
||||
|
||||
if err != nil {
|
||||
ws.log.WithError(err).Warning("failed to read startup splash screen")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = rw.Write(loadingSplashHTML)
|
||||
|
||||
if err != nil {
|
||||
ws.log.WithError(err).Warning("failed to write startup splash screen")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
header.Set("Content-Type", "text/plain")
|
||||
rw.WriteHeader(http.StatusServiceUnavailable)
|
||||
|
||||
// Fallback to just a status message
|
||||
_, err = rw.Write([]byte("authentik starting"))
|
||||
|
||||
if err != nil {
|
||||
ws.log.WithError(err).Warning("failed to write initializing HTML")
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
rw.WriteHeader(http.StatusBadGateway)
|
||||
|
||||
ws.log.WithError(err).Warning("failed to proxy to backend")
|
||||
|
||||
em := fmt.Sprintf("failed to connect to authentik backend: %v", err)
|
||||
// return json if the client asks for json
|
||||
if req.Header.Get("Accept") == "application/json" {
|
||||
|
||||
if strings.Contains(accept, "application/json") {
|
||||
header.Set("Content-Type", "application/json")
|
||||
rw.WriteHeader(http.StatusBadGateway)
|
||||
|
||||
err = json.NewEncoder(rw).Encode(map[string]string{
|
||||
"error": em,
|
||||
})
|
||||
} else {
|
||||
header.Set("Content-Type", "text/plain")
|
||||
rw.WriteHeader(http.StatusBadGateway)
|
||||
|
||||
_, err = rw.Write([]byte(em))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
ws.log.WithError(err).Warning("failed to write error message")
|
||||
}
|
||||
|
||||
@@ -17,9 +17,7 @@ func (ws *WebServer) configureStatic() {
|
||||
// Setup routers
|
||||
staticRouter := ws.loggingRouter.NewRoute().Subrouter()
|
||||
staticRouter.Use(ws.staticHeaderMiddleware)
|
||||
indexLessRouter := staticRouter.NewRoute().Subrouter()
|
||||
// Specifically disable index
|
||||
indexLessRouter.Use(web.DisableIndex)
|
||||
staticRouter.Use(web.DisableIndex)
|
||||
|
||||
distFs := http.FileServer(http.Dir("./web/dist"))
|
||||
|
||||
@@ -31,18 +29,18 @@ func (ws *WebServer) configureStatic() {
|
||||
return h
|
||||
}
|
||||
|
||||
indexLessRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/static/dist/").Handler(pathStripper(
|
||||
staticRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/static/dist/").Handler(pathStripper(
|
||||
distFs,
|
||||
"static/dist/",
|
||||
config.Get().Web.Path,
|
||||
))
|
||||
indexLessRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/static/authentik/").Handler(pathStripper(
|
||||
staticRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/static/authentik/").Handler(pathStripper(
|
||||
http.FileServer(http.Dir("./web/authentik")),
|
||||
"static/authentik/",
|
||||
config.Get().Web.Path,
|
||||
))
|
||||
|
||||
indexLessRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/if/flow/{flow_slug}/assets").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
staticRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/if/flow/{flow_slug}/assets").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
|
||||
pathStripper(
|
||||
@@ -51,9 +49,9 @@ func (ws *WebServer) configureStatic() {
|
||||
config.Get().Web.Path,
|
||||
).ServeHTTP(rw, r)
|
||||
})
|
||||
indexLessRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/if/admin/assets").Handler(http.StripPrefix(fmt.Sprintf("%sif/admin", config.Get().Web.Path), distFs))
|
||||
indexLessRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/if/user/assets").Handler(http.StripPrefix(fmt.Sprintf("%sif/user", config.Get().Web.Path), distFs))
|
||||
indexLessRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/if/rac/{app_slug}/assets").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
staticRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/if/admin/assets").Handler(http.StripPrefix(fmt.Sprintf("%sif/admin", config.Get().Web.Path), distFs))
|
||||
staticRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/if/user/assets").Handler(http.StripPrefix(fmt.Sprintf("%sif/user", config.Get().Web.Path), distFs))
|
||||
staticRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/if/rac/{app_slug}/assets").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
|
||||
pathStripper(
|
||||
@@ -66,7 +64,7 @@ func (ws *WebServer) configureStatic() {
|
||||
// Media files, if backend is file
|
||||
if config.Get().Storage.Media.Backend == "file" {
|
||||
fsMedia := http.FileServer(http.Dir(config.Get().Storage.Media.File.Path))
|
||||
indexLessRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/media/").Handler(pathStripper(
|
||||
staticRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/media/").Handler(pathStripper(
|
||||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox")
|
||||
fsMedia.ServeHTTP(w, r)
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/securecookie"
|
||||
"github.com/pires/go-proxyproto"
|
||||
@@ -60,7 +59,7 @@ func NewWebServer() *WebServer {
|
||||
l := log.WithField("logger", "authentik.router")
|
||||
mainHandler := mux.NewRouter()
|
||||
mainHandler.Use(web.ProxyHeaders())
|
||||
mainHandler.Use(handlers.CompressHandler)
|
||||
mainHandler.Use(web.NewCompressHandler)
|
||||
loggingHandler := mainHandler.NewRoute().Subrouter()
|
||||
loggingHandler.Use(web.NewLoggingHandler(l, nil))
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Stage 1: Build
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS builder
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25-bookworm AS builder
|
||||
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
|
||||
@@ -2,10 +2,6 @@
|
||||
set -e -o pipefail
|
||||
MODE_FILE="${TMPDIR}/authentik-mode"
|
||||
|
||||
if [[ -z "${PROMETHEUS_MULTIPROC_DIR}" ]]; then
|
||||
export PROMETHEUS_MULTIPROC_DIR="${TMPDIR:-/tmp}/authentik_prometheus_tmp"
|
||||
fi
|
||||
|
||||
function log {
|
||||
printf '{"event": "%s", "level": "info", "logger": "bootstrap"}\n' "$@" >/dev/stderr
|
||||
}
|
||||
@@ -35,7 +31,7 @@ function check_if_root {
|
||||
GROUP="authentik:${GROUP_NAME}"
|
||||
fi
|
||||
# Fix permissions of certs and media
|
||||
chown -R authentik:authentik /media /certs "${PROMETHEUS_MULTIPROC_DIR}"
|
||||
chown -R authentik:authentik /media /certs
|
||||
chmod ug+rwx /media
|
||||
chmod ug+rx /certs
|
||||
exec chpst -u authentik:$GROUP env HOME=/authentik $1
|
||||
@@ -72,6 +68,9 @@ function prepare_debug {
|
||||
chown authentik:authentik /unittest.xml
|
||||
}
|
||||
|
||||
if [[ -z "${PROMETHEUS_MULTIPROC_DIR}" ]]; then
|
||||
export PROMETHEUS_MULTIPROC_DIR="${TMPDIR:-/tmp}"
|
||||
fi
|
||||
mkdir -p "${PROMETHEUS_MULTIPROC_DIR}"
|
||||
|
||||
if [[ "$(python -m authentik.lib.config debugger 2>/dev/null)" == "True" ]]; then
|
||||
|
||||
8
lifecycle/aws/package-lock.json
generated
8
lifecycle/aws/package-lock.json
generated
@@ -9,7 +9,7 @@
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"aws-cdk": "^2.1024.0",
|
||||
"aws-cdk": "^2.1026.0",
|
||||
"cross-env": "^10.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -24,9 +24,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/aws-cdk": {
|
||||
"version": "2.1024.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1024.0.tgz",
|
||||
"integrity": "sha512-hY0iVT2gPX/QOQXL7RSP2sqIRI/4BYU27vSmbhZxLEj//c3pkMkd9QpIHj7gOhyWC2gf6n5JuYPw27Dgw8FEdA==",
|
||||
"version": "2.1026.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1026.0.tgz",
|
||||
"integrity": "sha512-JdXR20s9gMHY3niweK5/D9tILLG8u2FOyJjWgSaNZGJ+pq9u0sBFxufXPO4VxJzDitGFOIW5VvQThXP+Y2VrVA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"node": ">=20"
|
||||
},
|
||||
"devDependencies": {
|
||||
"aws-cdk": "^2.1024.0",
|
||||
"aws-cdk": "^2.1026.0",
|
||||
"cross-env": "^10.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ Parameters:
|
||||
Description: authentik Docker image
|
||||
AuthentikVersion:
|
||||
Type: String
|
||||
Default: 2025.8.4
|
||||
Default: 2025.10.0-rc1
|
||||
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-08-11 00:12+0000\n"
|
||||
"POT-Creation-Date: 2025-08-18 00:11+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"
|
||||
@@ -790,6 +790,12 @@ msgstr ""
|
||||
msgid "Email"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py
|
||||
msgid ""
|
||||
"Only send notification once, for example when sending a webhook into a chat "
|
||||
"channel."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py
|
||||
msgid ""
|
||||
"Customize the body of the request. Mapping should return data that is JSON-"
|
||||
@@ -802,12 +808,6 @@ msgid ""
|
||||
"of key-value pairs"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py
|
||||
msgid ""
|
||||
"Only send notification once, for example when sending a webhook into a chat "
|
||||
"channel."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py
|
||||
msgid "Severity"
|
||||
msgstr ""
|
||||
@@ -2905,10 +2905,6 @@ msgstr ""
|
||||
msgid "Duo Devices"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/authenticator_email/models.py
|
||||
msgid "Email OTP"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/authenticator_email/models.py
|
||||
#: authentik/stages/email/models.py
|
||||
msgid ""
|
||||
@@ -3269,6 +3265,14 @@ msgstr ""
|
||||
msgid "Account Confirmation"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Email OTP"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/models.py
|
||||
msgid "Event Notification"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/models.py
|
||||
msgid ""
|
||||
"The time window used to count recent account recovery attempts. If the "
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@goauthentik/authentik",
|
||||
"version": "2025.8.4",
|
||||
"version": "2025.10.0-rc1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@goauthentik/authentik",
|
||||
"version": "2025.8.4",
|
||||
"version": "2025.10.0-rc1",
|
||||
"dependencies": {
|
||||
"@eslint/js": "^9.31.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.38.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@goauthentik/authentik",
|
||||
"version": "2025.8.4",
|
||||
"version": "2025.10.0-rc1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
||||
@@ -385,9 +385,7 @@ class _PostgresConsumer(Consumer):
|
||||
|
||||
processing = len(self.in_processing)
|
||||
if processing >= self.prefetch:
|
||||
# If we have too many messages already processing, wait and don't consume a message
|
||||
# straight away, other workers will be faster.
|
||||
# After waiting consume a message regardless.
|
||||
# Wait and don't consume the message, other worker will be faster
|
||||
self.misses, backoff_ms = compute_backoff(self.misses, max_backoff=1000)
|
||||
self.logger.debug(
|
||||
"Too many messages in processing, Sleeping",
|
||||
@@ -395,8 +393,7 @@ class _PostgresConsumer(Consumer):
|
||||
backoff_ms=backoff_ms,
|
||||
)
|
||||
time.sleep(backoff_ms / 1000)
|
||||
else:
|
||||
self.misses = 0
|
||||
return None
|
||||
|
||||
if not self.notifies:
|
||||
self.notifies += self._poll_for_notify()
|
||||
@@ -426,7 +423,6 @@ class _PostgresConsumer(Consumer):
|
||||
self._auto_purge()
|
||||
self._scheduler()
|
||||
|
||||
self.misses = 0
|
||||
return None
|
||||
|
||||
def _purge_locks(self):
|
||||
|
||||
15
packages/docusaurus-config/package-lock.json
generated
15
packages/docusaurus-config/package-lock.json
generated
@@ -10,8 +10,7 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"deepmerge-ts": "^7.1.5",
|
||||
"prism-react-renderer": "^2.4.1",
|
||||
"react-dom": ">=18"
|
||||
"prism-react-renderer": "^2.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/theme-common": "^3.8.1",
|
||||
@@ -35,7 +34,8 @@
|
||||
"@docusaurus/theme-common": "^3.8.1",
|
||||
"@docusaurus/theme-search-algolia": "^3.8.1",
|
||||
"@docusaurus/types": "^3.8.0",
|
||||
"react": ">=18"
|
||||
"react": ">=18",
|
||||
"react-dom": ">=18"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@docusaurus/theme-search-algolia": {
|
||||
@@ -43,6 +43,9 @@
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -4643,9 +4646,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "19.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz",
|
||||
"integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==",
|
||||
"version": "19.1.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.10.tgz",
|
||||
"integrity": "sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
226
packages/esbuild-plugin-live-reload/package-lock.json
generated
226
packages/esbuild-plugin-live-reload/package-lock.json
generated
@@ -132,9 +132,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz",
|
||||
"integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz",
|
||||
"integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -149,9 +149,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz",
|
||||
"integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz",
|
||||
"integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -166,9 +166,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz",
|
||||
"integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz",
|
||||
"integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -183,9 +183,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz",
|
||||
"integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz",
|
||||
"integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -200,9 +200,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz",
|
||||
"integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz",
|
||||
"integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -217,9 +217,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz",
|
||||
"integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz",
|
||||
"integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -234,9 +234,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz",
|
||||
"integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz",
|
||||
"integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -251,9 +251,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz",
|
||||
"integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz",
|
||||
"integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -268,9 +268,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz",
|
||||
"integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz",
|
||||
"integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -285,9 +285,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz",
|
||||
"integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz",
|
||||
"integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -302,9 +302,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz",
|
||||
"integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz",
|
||||
"integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -319,9 +319,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz",
|
||||
"integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz",
|
||||
"integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
@@ -336,9 +336,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz",
|
||||
"integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz",
|
||||
"integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
@@ -353,9 +353,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz",
|
||||
"integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz",
|
||||
"integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -370,9 +370,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz",
|
||||
"integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz",
|
||||
"integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -387,9 +387,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz",
|
||||
"integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz",
|
||||
"integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -404,9 +404,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz",
|
||||
"integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz",
|
||||
"integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -421,9 +421,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz",
|
||||
"integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz",
|
||||
"integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -438,9 +438,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz",
|
||||
"integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz",
|
||||
"integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -455,9 +455,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz",
|
||||
"integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz",
|
||||
"integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -472,9 +472,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz",
|
||||
"integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz",
|
||||
"integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -489,9 +489,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openharmony-arm64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz",
|
||||
"integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz",
|
||||
"integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -506,9 +506,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz",
|
||||
"integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz",
|
||||
"integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -523,9 +523,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz",
|
||||
"integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz",
|
||||
"integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -540,9 +540,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz",
|
||||
"integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz",
|
||||
"integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -557,9 +557,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz",
|
||||
"integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz",
|
||||
"integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -904,9 +904,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz",
|
||||
"integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==",
|
||||
"version": "24.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz",
|
||||
"integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -1269,9 +1269,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz",
|
||||
"integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==",
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz",
|
||||
"integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
@@ -1282,32 +1282,32 @@
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.25.8",
|
||||
"@esbuild/android-arm": "0.25.8",
|
||||
"@esbuild/android-arm64": "0.25.8",
|
||||
"@esbuild/android-x64": "0.25.8",
|
||||
"@esbuild/darwin-arm64": "0.25.8",
|
||||
"@esbuild/darwin-x64": "0.25.8",
|
||||
"@esbuild/freebsd-arm64": "0.25.8",
|
||||
"@esbuild/freebsd-x64": "0.25.8",
|
||||
"@esbuild/linux-arm": "0.25.8",
|
||||
"@esbuild/linux-arm64": "0.25.8",
|
||||
"@esbuild/linux-ia32": "0.25.8",
|
||||
"@esbuild/linux-loong64": "0.25.8",
|
||||
"@esbuild/linux-mips64el": "0.25.8",
|
||||
"@esbuild/linux-ppc64": "0.25.8",
|
||||
"@esbuild/linux-riscv64": "0.25.8",
|
||||
"@esbuild/linux-s390x": "0.25.8",
|
||||
"@esbuild/linux-x64": "0.25.8",
|
||||
"@esbuild/netbsd-arm64": "0.25.8",
|
||||
"@esbuild/netbsd-x64": "0.25.8",
|
||||
"@esbuild/openbsd-arm64": "0.25.8",
|
||||
"@esbuild/openbsd-x64": "0.25.8",
|
||||
"@esbuild/openharmony-arm64": "0.25.8",
|
||||
"@esbuild/sunos-x64": "0.25.8",
|
||||
"@esbuild/win32-arm64": "0.25.8",
|
||||
"@esbuild/win32-ia32": "0.25.8",
|
||||
"@esbuild/win32-x64": "0.25.8"
|
||||
"@esbuild/aix-ppc64": "0.25.9",
|
||||
"@esbuild/android-arm": "0.25.9",
|
||||
"@esbuild/android-arm64": "0.25.9",
|
||||
"@esbuild/android-x64": "0.25.9",
|
||||
"@esbuild/darwin-arm64": "0.25.9",
|
||||
"@esbuild/darwin-x64": "0.25.9",
|
||||
"@esbuild/freebsd-arm64": "0.25.9",
|
||||
"@esbuild/freebsd-x64": "0.25.9",
|
||||
"@esbuild/linux-arm": "0.25.9",
|
||||
"@esbuild/linux-arm64": "0.25.9",
|
||||
"@esbuild/linux-ia32": "0.25.9",
|
||||
"@esbuild/linux-loong64": "0.25.9",
|
||||
"@esbuild/linux-mips64el": "0.25.9",
|
||||
"@esbuild/linux-ppc64": "0.25.9",
|
||||
"@esbuild/linux-riscv64": "0.25.9",
|
||||
"@esbuild/linux-s390x": "0.25.9",
|
||||
"@esbuild/linux-x64": "0.25.9",
|
||||
"@esbuild/netbsd-arm64": "0.25.9",
|
||||
"@esbuild/netbsd-x64": "0.25.9",
|
||||
"@esbuild/openbsd-arm64": "0.25.9",
|
||||
"@esbuild/openbsd-x64": "0.25.9",
|
||||
"@esbuild/openharmony-arm64": "0.25.9",
|
||||
"@esbuild/sunos-x64": "0.25.9",
|
||||
"@esbuild/win32-arm64": "0.25.9",
|
||||
"@esbuild/win32-ia32": "0.25.9",
|
||||
"@esbuild/win32-x64": "0.25.9"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-string-regexp": {
|
||||
@@ -2728,9 +2728,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typedoc-plugin-markdown": {
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.8.0.tgz",
|
||||
"integrity": "sha512-BQqXnT9PETe6WEFf8bcsvvGEGQHbwTo/BFyY+RUIsSB05Y0Wn56iF+fK1PY2OKJJIhV4kp4dp7osaP9Bm5a0Zw==",
|
||||
"version": "4.8.1",
|
||||
"resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.8.1.tgz",
|
||||
"integrity": "sha512-ug7fc4j0SiJxSwBGLncpSo8tLvrT9VONvPUQqQDTKPxCoFQBADLli832RGPtj6sfSVJebNSrHZQRUdEryYH/7g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
||||
122
packages/eslint-config/package-lock.json
generated
122
packages/eslint-config/package-lock.json
generated
@@ -502,17 +502,17 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.39.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.39.0.tgz",
|
||||
"integrity": "sha512-bhEz6OZeUR+O/6yx9Jk6ohX6H9JSFTaiY0v9/PuKT3oGK0rn0jNplLmyFUGV+a9gfYnVNwGDwS/UkLIuXNb2Rw==",
|
||||
"version": "8.40.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.40.0.tgz",
|
||||
"integrity": "sha512-w/EboPlBwnmOBtRbiOvzjD+wdiZdgFeo17lkltrtn7X37vagKKWJABvyfsJXTlHe6XBzugmYgd4A4nW+k8Mixw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.39.0",
|
||||
"@typescript-eslint/type-utils": "8.39.0",
|
||||
"@typescript-eslint/utils": "8.39.0",
|
||||
"@typescript-eslint/visitor-keys": "8.39.0",
|
||||
"@typescript-eslint/scope-manager": "8.40.0",
|
||||
"@typescript-eslint/type-utils": "8.40.0",
|
||||
"@typescript-eslint/utils": "8.40.0",
|
||||
"@typescript-eslint/visitor-keys": "8.40.0",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^7.0.0",
|
||||
"natural-compare": "^1.4.0",
|
||||
@@ -526,7 +526,7 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^8.39.0",
|
||||
"@typescript-eslint/parser": "^8.40.0",
|
||||
"eslint": "^8.57.0 || ^9.0.0",
|
||||
"typescript": ">=4.8.4 <6.0.0"
|
||||
}
|
||||
@@ -542,16 +542,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.39.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.39.0.tgz",
|
||||
"integrity": "sha512-g3WpVQHngx0aLXn6kfIYCZxM6rRJlWzEkVpqEFLT3SgEDsp9cpCbxxgwnE504q4H+ruSDh/VGS6nqZIDynP+vg==",
|
||||
"version": "8.40.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.40.0.tgz",
|
||||
"integrity": "sha512-jCNyAuXx8dr5KJMkecGmZ8KI61KBUhkCob+SD+C+I5+Y1FWI2Y3QmY4/cxMCC5WAsZqoEtEETVhUiUMIGCf6Bw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.39.0",
|
||||
"@typescript-eslint/types": "8.39.0",
|
||||
"@typescript-eslint/typescript-estree": "8.39.0",
|
||||
"@typescript-eslint/visitor-keys": "8.39.0",
|
||||
"@typescript-eslint/scope-manager": "8.40.0",
|
||||
"@typescript-eslint/types": "8.40.0",
|
||||
"@typescript-eslint/typescript-estree": "8.40.0",
|
||||
"@typescript-eslint/visitor-keys": "8.40.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@@ -567,14 +567,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.39.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.0.tgz",
|
||||
"integrity": "sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==",
|
||||
"version": "8.40.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.40.0.tgz",
|
||||
"integrity": "sha512-/A89vz7Wf5DEXsGVvcGdYKbVM9F7DyFXj52lNYUDS1L9yJfqjW/fIp5PgMuEJL/KeqVTe2QSbXAGUZljDUpArw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.39.0",
|
||||
"@typescript-eslint/types": "^8.39.0",
|
||||
"@typescript-eslint/tsconfig-utils": "^8.40.0",
|
||||
"@typescript-eslint/types": "^8.40.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@@ -589,14 +589,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.39.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.0.tgz",
|
||||
"integrity": "sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==",
|
||||
"version": "8.40.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.40.0.tgz",
|
||||
"integrity": "sha512-y9ObStCcdCiZKzwqsE8CcpyuVMwRouJbbSrNuThDpv16dFAj429IkM6LNb1dZ2m7hK5fHyzNcErZf7CEeKXR4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.39.0",
|
||||
"@typescript-eslint/visitor-keys": "8.39.0"
|
||||
"@typescript-eslint/types": "8.40.0",
|
||||
"@typescript-eslint/visitor-keys": "8.40.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -607,9 +607,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.39.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.0.tgz",
|
||||
"integrity": "sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==",
|
||||
"version": "8.40.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.40.0.tgz",
|
||||
"integrity": "sha512-jtMytmUaG9d/9kqSl/W3E3xaWESo4hFDxAIHGVW/WKKtQhesnRIJSAJO6XckluuJ6KDB5woD1EiqknriCtAmcw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -624,15 +624,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.39.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.39.0.tgz",
|
||||
"integrity": "sha512-6B3z0c1DXVT2vYA9+z9axjtc09rqKUPRmijD5m9iv8iQpHBRYRMBcgxSiKTZKm6FwWw1/cI4v6em35OsKCiN5Q==",
|
||||
"version": "8.40.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.40.0.tgz",
|
||||
"integrity": "sha512-eE60cK4KzAc6ZrzlJnflXdrMqOBaugeukWICO2rB0KNvwdIMaEaYiywwHMzA1qFpTxrLhN9Lp4E/00EgWcD3Ow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.39.0",
|
||||
"@typescript-eslint/typescript-estree": "8.39.0",
|
||||
"@typescript-eslint/utils": "8.39.0",
|
||||
"@typescript-eslint/types": "8.40.0",
|
||||
"@typescript-eslint/typescript-estree": "8.40.0",
|
||||
"@typescript-eslint/utils": "8.40.0",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^2.1.0"
|
||||
},
|
||||
@@ -649,9 +649,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.39.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.0.tgz",
|
||||
"integrity": "sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==",
|
||||
"version": "8.40.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.40.0.tgz",
|
||||
"integrity": "sha512-ETdbFlgbAmXHyFPwqUIYrfc12ArvpBhEVgGAxVYSwli26dn8Ko+lIo4Su9vI9ykTZdJn+vJprs/0eZU0YMAEQg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -663,16 +663,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.39.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.0.tgz",
|
||||
"integrity": "sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==",
|
||||
"version": "8.40.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.40.0.tgz",
|
||||
"integrity": "sha512-k1z9+GJReVVOkc1WfVKs1vBrR5MIKKbdAjDTPvIK3L8De6KbFfPFt6BKpdkdk7rZS2GtC/m6yI5MYX+UsuvVYQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.39.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.39.0",
|
||||
"@typescript-eslint/types": "8.39.0",
|
||||
"@typescript-eslint/visitor-keys": "8.39.0",
|
||||
"@typescript-eslint/project-service": "8.40.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.40.0",
|
||||
"@typescript-eslint/types": "8.40.0",
|
||||
"@typescript-eslint/visitor-keys": "8.40.0",
|
||||
"debug": "^4.3.4",
|
||||
"fast-glob": "^3.3.2",
|
||||
"is-glob": "^4.0.3",
|
||||
@@ -731,16 +731,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.39.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.0.tgz",
|
||||
"integrity": "sha512-4GVSvNA0Vx1Ktwvf4sFE+exxJ3QGUorQG1/A5mRfRNZtkBT2xrA/BCO2H0eALx/PnvCS6/vmYwRdDA41EoffkQ==",
|
||||
"version": "8.40.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.40.0.tgz",
|
||||
"integrity": "sha512-Cgzi2MXSZyAUOY+BFwGs17s7ad/7L+gKt6Y8rAVVWS+7o6wrjeFN4nVfTpbE25MNcxyJ+iYUXflbs2xR9h4UBg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.7.0",
|
||||
"@typescript-eslint/scope-manager": "8.39.0",
|
||||
"@typescript-eslint/types": "8.39.0",
|
||||
"@typescript-eslint/typescript-estree": "8.39.0"
|
||||
"@typescript-eslint/scope-manager": "8.40.0",
|
||||
"@typescript-eslint/types": "8.40.0",
|
||||
"@typescript-eslint/typescript-estree": "8.40.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -755,13 +755,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.39.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.0.tgz",
|
||||
"integrity": "sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==",
|
||||
"version": "8.40.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.40.0.tgz",
|
||||
"integrity": "sha512-8CZ47QwalyRjsypfwnbI3hKy5gJDPmrkLjkgMxhi0+DZZ2QNx2naS6/hWoVYUHU7LU2zleF68V9miaVZvhFfTA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.39.0",
|
||||
"@typescript-eslint/types": "8.40.0",
|
||||
"eslint-visitor-keys": "^4.2.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -4709,16 +4709,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-eslint": {
|
||||
"version": "8.39.0",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.39.0.tgz",
|
||||
"integrity": "sha512-lH8FvtdtzcHJCkMOKnN73LIn6SLTpoojgJqDAxPm1jCR14eWSGPX8ul/gggBdPMk/d5+u9V854vTYQ8T5jF/1Q==",
|
||||
"version": "8.40.0",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.40.0.tgz",
|
||||
"integrity": "sha512-Xvd2l+ZmFDPEt4oj1QEXzA4A2uUK6opvKu3eGN9aGjB8au02lIVcLyi375w94hHyejTOmzIU77L8ol2sRg9n7Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "8.39.0",
|
||||
"@typescript-eslint/parser": "8.39.0",
|
||||
"@typescript-eslint/typescript-estree": "8.39.0",
|
||||
"@typescript-eslint/utils": "8.39.0"
|
||||
"@typescript-eslint/eslint-plugin": "8.40.0",
|
||||
"@typescript-eslint/parser": "8.40.0",
|
||||
"@typescript-eslint/typescript-estree": "8.40.0",
|
||||
"@typescript-eslint/utils": "8.40.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
|
||||
6
packages/prettier-config/package-lock.json
generated
6
packages/prettier-config/package-lock.json
generated
@@ -385,9 +385,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz",
|
||||
"integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==",
|
||||
"version": "24.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz",
|
||||
"integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
@@ -17,7 +17,7 @@ COPY web .
|
||||
RUN npm run build-proxy
|
||||
|
||||
# Stage 2: Build
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS builder
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25-bookworm AS builder
|
||||
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
[project]
|
||||
name = "authentik"
|
||||
version = "2025.8.4"
|
||||
version = "2025.10.0-rc1"
|
||||
description = ""
|
||||
authors = [{ name = "authentik Team", email = "hello@goauthentik.io" }]
|
||||
requires-python = "==3.13.*"
|
||||
dependencies = [
|
||||
"argon2-cffi==25.1.0",
|
||||
"channels==4.3.0",
|
||||
"channels==4.3.1",
|
||||
"channels-redis==4.3.0",
|
||||
"cryptography==45.0.5",
|
||||
"dacite==1.9.2",
|
||||
"deepmerge==2.0",
|
||||
"defusedxml==0.7.1",
|
||||
"django==5.1.12",
|
||||
"django==5.1.11",
|
||||
"django-countries==7.6.1",
|
||||
"django-cte==2.0.0",
|
||||
"django-dramatiq-postgres",
|
||||
@@ -79,7 +79,7 @@ dev = [
|
||||
"aws-cdk-lib==2.188.0",
|
||||
"bandit==1.8.3",
|
||||
"black==25.1.0",
|
||||
"channels[daphne]==4.3.0",
|
||||
"channels[daphne]==4.3.1",
|
||||
"codespell==2.4.1",
|
||||
"colorama==0.4.6",
|
||||
"constructs==10.4.2",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Stage 1: Build
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS builder
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25-bookworm AS builder
|
||||
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Stage 1: Build
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS builder
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25-bookworm AS builder
|
||||
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: authentik
|
||||
version: 2025.8.4
|
||||
version: 2025.10.0-rc1
|
||||
description: Making authentication simple.
|
||||
contact:
|
||||
email: hello@goauthentik.io
|
||||
@@ -61879,7 +61879,6 @@ components:
|
||||
- object_pk
|
||||
UserPasswordSetRequest:
|
||||
type: object
|
||||
description: Payload to set a users' password directly
|
||||
properties:
|
||||
password:
|
||||
type: string
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
services:
|
||||
chrome:
|
||||
platform: linux/x86_64
|
||||
image: docker.io/selenium/standalone-chrome:138.0
|
||||
image: docker.io/selenium/standalone-chrome:139.0
|
||||
volumes:
|
||||
- /dev/shm:/dev/shm
|
||||
network_mode: host
|
||||
restart: always
|
||||
mailpit:
|
||||
image: docker.io/axllent/mailpit:v1.27.4
|
||||
image: docker.io/axllent/mailpit:v1.27.6
|
||||
ports:
|
||||
- 1025:1025
|
||||
- 8025:8025
|
||||
|
||||
@@ -241,9 +241,6 @@ class TestProviderLDAP(SeleniumTestCase):
|
||||
"homeDirectory": f"/home/{o_user.username}",
|
||||
"ak-active": True,
|
||||
"ak-superuser": False,
|
||||
"pwdChangedTime": o_user.password_change_date.replace(microsecond=0),
|
||||
"createTimestamp": o_user.date_joined.replace(microsecond=0),
|
||||
"modifyTimestamp": o_user.last_updated.replace(microsecond=0),
|
||||
},
|
||||
"type": "searchResEntry",
|
||||
},
|
||||
@@ -272,9 +269,6 @@ class TestProviderLDAP(SeleniumTestCase):
|
||||
"homeDirectory": f"/home/{embedded_account.username}",
|
||||
"ak-active": True,
|
||||
"ak-superuser": False,
|
||||
"pwdChangedTime": embedded_account.password_change_date.replace(microsecond=0),
|
||||
"createTimestamp": embedded_account.date_joined.replace(microsecond=0),
|
||||
"modifyTimestamp": embedded_account.last_updated.replace(microsecond=0),
|
||||
},
|
||||
"type": "searchResEntry",
|
||||
},
|
||||
@@ -307,9 +301,6 @@ class TestProviderLDAP(SeleniumTestCase):
|
||||
"ak-active": True,
|
||||
"ak-superuser": True,
|
||||
"extraAttribute": ["bar"],
|
||||
"pwdChangedTime": self.user.password_change_date.replace(microsecond=0),
|
||||
"createTimestamp": self.user.date_joined.replace(microsecond=0),
|
||||
"modifyTimestamp": self.user.last_updated.replace(microsecond=0),
|
||||
},
|
||||
"type": "searchResEntry",
|
||||
},
|
||||
@@ -387,9 +378,6 @@ class TestProviderLDAP(SeleniumTestCase):
|
||||
"homeDirectory": f"/home/{user.username}",
|
||||
"ak-active": True,
|
||||
"ak-superuser": False,
|
||||
"pwdChangedTime": user.password_change_date.replace(microsecond=0),
|
||||
"createTimestamp": user.date_joined.replace(microsecond=0),
|
||||
"modifyTimestamp": user.last_updated.replace(microsecond=0),
|
||||
},
|
||||
"type": "searchResEntry",
|
||||
},
|
||||
|
||||
@@ -10,6 +10,7 @@ from docker.types.healthcheck import Healthcheck
|
||||
|
||||
from authentik.core.tests.utils import create_test_flow
|
||||
from authentik.crypto.models import CertificateKeyPair
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.outposts.models import (
|
||||
DockerServiceConnection,
|
||||
Outpost,
|
||||
@@ -88,6 +89,7 @@ class TestProxyDocker(DockerTestCase, ChannelsLiveServerTestCase):
|
||||
pass
|
||||
|
||||
@pytest.mark.timeout(120)
|
||||
@CONFIG.patch("outposts.container_image_base", "ghcr.io/goauthentik/dev-proxy:gh-main")
|
||||
def test_docker_controller(self):
|
||||
"""test that deployment requires update"""
|
||||
controller = DockerController(self.outpost, self.service_connection)
|
||||
|
||||
34
uv.lock
generated
34
uv.lock
generated
@@ -1,5 +1,5 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
revision = 2
|
||||
requires-python = "==3.13.*"
|
||||
|
||||
[manifest]
|
||||
@@ -159,7 +159,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "authentik"
|
||||
version = "2025.8.4"
|
||||
version = "2025.10.0rc1"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "argon2-cffi" },
|
||||
@@ -260,13 +260,13 @@ dev = [
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "argon2-cffi", specifier = "==25.1.0" },
|
||||
{ name = "channels", specifier = "==4.3.0" },
|
||||
{ name = "channels", specifier = "==4.3.1" },
|
||||
{ name = "channels-redis", specifier = "==4.3.0" },
|
||||
{ name = "cryptography", specifier = "==45.0.5" },
|
||||
{ name = "dacite", specifier = "==1.9.2" },
|
||||
{ name = "deepmerge", specifier = "==2.0" },
|
||||
{ name = "defusedxml", specifier = "==0.7.1" },
|
||||
{ name = "django", specifier = "==5.1.12" },
|
||||
{ name = "django", specifier = "==5.1.11" },
|
||||
{ name = "django-countries", specifier = "==7.6.1" },
|
||||
{ name = "django-cte", specifier = "==2.0.0" },
|
||||
{ name = "django-dramatiq-postgres", editable = "packages/django-dramatiq-postgres" },
|
||||
@@ -333,7 +333,7 @@ dev = [
|
||||
{ name = "aws-cdk-lib", specifier = "==2.188.0" },
|
||||
{ name = "bandit", specifier = "==1.8.3" },
|
||||
{ name = "black", specifier = "==25.1.0" },
|
||||
{ name = "channels", extras = ["daphne"], specifier = "==4.3.0" },
|
||||
{ name = "channels", extras = ["daphne"], specifier = "==4.3.1" },
|
||||
{ name = "codespell", specifier = "==2.4.1" },
|
||||
{ name = "colorama", specifier = "==0.4.6" },
|
||||
{ name = "constructs", specifier = "==10.4.2" },
|
||||
@@ -652,15 +652,15 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "channels"
|
||||
version = "4.3.0"
|
||||
version = "4.3.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "asgiref" },
|
||||
{ name = "django" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/04/6768c7a887f9c593c4d49f99130c8aec4ea06e750bc17c306b689f6caf3b/channels-4.3.0.tar.gz", hash = "sha256:7db32c61dcd88eada1647e6c6f6ad2eb724b75d4852eeff26ad1c51ccd1a37f7", size = 26816, upload-time = "2025-07-28T13:52:50.334Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/12/a0/46450fcf9e56af18a6b0440ba49db6635419bb7bc84142c35f4143b1a66c/channels-4.3.1.tar.gz", hash = "sha256:97413ffd674542db08e16a9ef09cd86ec0113e5f8125fbd33cf0854adcf27cdb", size = 26896, upload-time = "2025-08-01T13:25:19.952Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/59/0866202ee593e1b0dab0b472ebb8169e1b2b7886ad3008d193da2bbe10cb/channels-4.3.0-py3-none-any.whl", hash = "sha256:0497f3affb95e621b37d6bae1b6a5d9e8e1e1221007a2566f280091cf30ffcce", size = 31238, upload-time = "2025-07-28T13:52:49.117Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/1c/eae1c2a8c195760376e7f65d0bdcc3e966695d29cfbe5c54841ce5c71408/channels-4.3.1-py3-none-any.whl", hash = "sha256:b091d4b26f91d807de3e84aead7ba785314f27eaf5bac31dd51b1c956b883859", size = 31286, upload-time = "2025-08-01T13:25:18.845Z" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
@@ -899,16 +899,16 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "django"
|
||||
version = "5.1.12"
|
||||
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/f0/99/a951d93a27a5bc59fb96edbcdbc03fb9bfac51177f1bc0110888de85af3f/django-5.1.12.tar.gz", hash = "sha256:8a8991b1ec052ef6a44fefd1ef336ab8daa221287bcb91a4a17d5e1abec5bbcc", size = 10737777, upload-time = "2025-09-03T13:09:45.855Z" }
|
||||
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/1e/1c/a9520c8263e980b0b9933c9b5ce8f22c9ddf007b062e4eb428b557ff0932/django-5.1.12-py3-none-any.whl", hash = "sha256:9eb695636cea3601b65690f1596993c042206729afb320ca0960b55f8ed4477b", size = 8277454, upload-time = "2025-09-03T13:09:30.997Z" },
|
||||
{ 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]]
|
||||
@@ -1499,15 +1499,15 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "4.3.0"
|
||||
version = "4.2.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "hpack" },
|
||||
{ name = "hyperframe" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1d/17/afa56379f94ad0fe8defd37d6eb3f89a25404ffc71d4d848893d270325fc/h2-4.3.0.tar.gz", hash = "sha256:6c59efe4323fa18b47a632221a1888bd7fde6249819beda254aeca909f221bf1", size = 2152026, upload-time = "2025-08-23T18:12:19.778Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1b/38/d7f80fd13e6582fb8e0df8c9a653dcc02b03ca34f4d72f34869298c5baf8/h2-4.2.0.tar.gz", hash = "sha256:c8a52129695e88b1a0578d8d2cc6842bbd79128ac685463b887ee278126ad01f", size = 2150682, upload-time = "2025-02-02T07:43:51.815Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/69/b2/119f6e6dcbd96f9069ce9a2665e0146588dc9f88f29549711853645e736a/h2-4.3.0-py3-none-any.whl", hash = "sha256:c438f029a25f7945c69e0ccf0fb951dc3f73a5f6412981daee861431b70e2bdd", size = 61779, upload-time = "2025-08-23T18:12:17.779Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/9e/984486f2d0a0bd2b024bf4bc1c62688fcafa9e61991f041fb0e2def4a982/h2-4.2.0-py3-none-any.whl", hash = "sha256:479a53ad425bb29af087f3458a61d30780bc818e4ebcf01f0b536ba916462ed0", size = 60957, upload-time = "2025-02-01T11:02:26.481Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2736,11 +2736,11 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "redis"
|
||||
version = "6.2.0"
|
||||
version = "6.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ea/9a/0551e01ba52b944f97480721656578c8a7c46b51b99d66814f85fe3a4f3e/redis-6.2.0.tar.gz", hash = "sha256:e821f129b75dde6cb99dd35e5c76e8c49512a5a0d8dfdc560b2fbd44b85ca977", size = 4639129, upload-time = "2025-05-28T05:01:18.91Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/21/cd/030274634a1a052b708756016283ea3d84e91ae45f74d7f5dcf55d753a0f/redis-6.3.0.tar.gz", hash = "sha256:3000dbe532babfb0999cdab7b3e5744bcb23e51923febcfaeb52c8cfb29632ef", size = 4647275, upload-time = "2025-08-05T08:12:31.648Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/13/67/e60968d3b0e077495a8fee89cf3f2373db98e528288a48f1ee44967f6e8c/redis-6.2.0-py3-none-any.whl", hash = "sha256:c8ddf316ee0aab65f04a11229e94a64b2618451dab7a67cb2f77eb799d872d5e", size = 278659, upload-time = "2025-05-28T05:01:16.955Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/a7/2fe45801534a187543fc45d28b3844d84559c1589255bc2ece30d92dc205/redis-6.3.0-py3-none-any.whl", hash = "sha256:92f079d656ded871535e099080f70fab8e75273c0236797126ac60242d638e9b", size = 280018, upload-time = "2025-08-05T08:12:30.093Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
1309
web/package-lock.json
generated
1309
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@goauthentik/web",
|
||||
"version": "2025.8.4",
|
||||
"version": "2025.10.0-rc1",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
@@ -111,20 +111,20 @@
|
||||
"@open-wc/lit-helpers": "^0.7.0",
|
||||
"@openlayers-elements/core": "^0.4.0",
|
||||
"@openlayers-elements/maps": "^0.4.0",
|
||||
"@patternfly/elements": "^4.1.0",
|
||||
"@patternfly/elements": "^4.2.0",
|
||||
"@patternfly/patternfly": "^4.224.2",
|
||||
"@sentry/browser": "^10.3.0",
|
||||
"@sentry/browser": "^10.5.0",
|
||||
"@spotlightjs/spotlight": "^3.0.2",
|
||||
"@storybook/addon-docs": "^9.1.1",
|
||||
"@storybook/addon-links": "^9.1.1",
|
||||
"@storybook/web-components": "^9.1.1",
|
||||
"@storybook/web-components-vite": "^9.1.1",
|
||||
"@storybook/addon-docs": "^9.1.2",
|
||||
"@storybook/addon-links": "^9.1.2",
|
||||
"@storybook/web-components": "^9.1.2",
|
||||
"@storybook/web-components-vite": "^9.1.2",
|
||||
"@types/codemirror": "^5.60.16",
|
||||
"@types/grecaptcha": "^3.0.9",
|
||||
"@types/guacamole-common-js": "^1.5.3",
|
||||
"@types/guacamole-common-js": "^1.5.4",
|
||||
"@types/mocha": "^10.0.10",
|
||||
"@types/node": "^24.2.1",
|
||||
"@types/react": "^19.1.8",
|
||||
"@types/node": "^24.3.0",
|
||||
"@types/react": "^19.1.10",
|
||||
"@types/react-dom": "^19.1.7",
|
||||
"@typescript-eslint/eslint-plugin": "^8.38.0",
|
||||
"@typescript-eslint/parser": "^8.38.0",
|
||||
@@ -135,12 +135,12 @@
|
||||
"chartjs-adapter-date-fns": "^3.0.0",
|
||||
"codemirror": "^6.0.2",
|
||||
"construct-style-sheets-polyfill": "^3.1.0",
|
||||
"core-js": "^3.44.0",
|
||||
"core-js": "^3.45.1",
|
||||
"country-flag-icons": "^1.5.19",
|
||||
"date-fns": "^4.1.0",
|
||||
"deepmerge-ts": "^7.1.5",
|
||||
"dompurify": "^3.2.6",
|
||||
"esbuild": "^0.25.8",
|
||||
"esbuild": "^0.25.9",
|
||||
"esbuild-plugin-copy": "^2.1.1",
|
||||
"esbuild-plugin-polyfill-node": "^0.3.0",
|
||||
"esbuild-plugins-node-modules-polyfill": "^1.7.1",
|
||||
@@ -156,7 +156,7 @@
|
||||
"lit": "^3.3.1",
|
||||
"lit-analyzer": "^2.0.3",
|
||||
"md-front-matter": "^1.0.4",
|
||||
"mermaid": "^11.9.0",
|
||||
"mermaid": "^11.10.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.6.2",
|
||||
"pseudolocale": "^2.1.0",
|
||||
@@ -177,7 +177,7 @@
|
||||
"ts-pattern": "^5.8.0",
|
||||
"turnstile-types": "^1.2.3",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.39.0",
|
||||
"typescript-eslint": "^8.40.0",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"webcomponent-qr-code": "^1.3.0",
|
||||
"wireit": "^0.14.12",
|
||||
@@ -187,14 +187,14 @@
|
||||
"@esbuild/darwin-arm64": "^0.25.4",
|
||||
"@esbuild/linux-arm64": "^0.25.4",
|
||||
"@esbuild/linux-x64": "^0.25.4",
|
||||
"@rollup/rollup-darwin-arm64": "^4.46.2",
|
||||
"@rollup/rollup-linux-arm64-gnu": "^4.46.2",
|
||||
"@rollup/rollup-linux-x64-gnu": "^4.46.2",
|
||||
"@wdio/browser-runner": "^9.19.0",
|
||||
"@wdio/cli": "^9.19.0",
|
||||
"@wdio/spec-reporter": "^9.19.0",
|
||||
"@rollup/rollup-darwin-arm64": "^4.46.3",
|
||||
"@rollup/rollup-linux-arm64-gnu": "^4.46.3",
|
||||
"@rollup/rollup-linux-x64-gnu": "^4.46.3",
|
||||
"@wdio/browser-runner": "^9.19.1",
|
||||
"@wdio/cli": "^9.19.1",
|
||||
"@wdio/spec-reporter": "^9.19.1",
|
||||
"@web/test-runner": "^0.20.2",
|
||||
"chromedriver": "^139.0.0"
|
||||
"chromedriver": "^139.0.1"
|
||||
},
|
||||
"wireit": {
|
||||
"build": {
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
"dependencies": {
|
||||
"@goauthentik/prettier-config": "^3.1.0",
|
||||
"@goauthentik/tsconfig": "^1.0.4",
|
||||
"@types/node": "^24.2.1",
|
||||
"@types/node": "^24.3.0",
|
||||
"prettier": "^3.6.2",
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"watch": "rollup -w -c rollup.config.mjs --bundleConfigAsCjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@goauthentik/api": "^2024.6.0-1719577139",
|
||||
"@goauthentik/api": "^2025.10.0-rc1-1755254677",
|
||||
"@goauthentik/core": "^1.0.0",
|
||||
"@rollup/plugin-commonjs": "^28.0.6",
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
@@ -23,7 +23,7 @@
|
||||
"formdata-polyfill": "^4.0.10",
|
||||
"jquery": "^3.7.1",
|
||||
"prettier": "^3.5.3",
|
||||
"rollup": "^4.46.2",
|
||||
"rollup": "^4.46.3",
|
||||
"rollup-plugin-copy": "^3.5.0",
|
||||
"weakmap-polyfill": "^2.0.4"
|
||||
},
|
||||
|
||||
@@ -48,6 +48,11 @@ const BASE_ESBUILD_OPTIONS = {
|
||||
plugins: [
|
||||
copy({
|
||||
assets: [
|
||||
{
|
||||
from: path.join(path.dirname(EntryPoint.StandaloneLoading.in), "startup", "**"),
|
||||
to: path.dirname(EntryPoint.StandaloneLoading.out),
|
||||
},
|
||||
|
||||
{
|
||||
from: path.join(patternflyPath, "patternfly.min.css"),
|
||||
to: ".",
|
||||
|
||||
@@ -51,8 +51,6 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
|
||||
}
|
||||
|
||||
async send(settingsRequest: SettingsRequest): Promise<Settings> {
|
||||
settingsRequest.flags ??= this.settings.flags;
|
||||
|
||||
const result = await new AdminApi(DEFAULT_CONFIG).adminSettingsUpdate({
|
||||
settingsRequest,
|
||||
});
|
||||
@@ -254,7 +252,7 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
|
||||
<ak-form-element-horizontal label=${msg("Flags")} name="flags" required>
|
||||
<ak-codemirror
|
||||
mode=${CodeMirrorMode.YAML}
|
||||
value="${YAML.stringify(settings.flags ?? {})}"
|
||||
value="${YAML.stringify(settings?.flags ?? {})}"
|
||||
>
|
||||
</ak-codemirror>
|
||||
<p class="pf-c-form__helper-text">
|
||||
|
||||
@@ -5,6 +5,10 @@ import { groupBy } from "#common/utils";
|
||||
|
||||
import { AKElement } from "#elements/Base";
|
||||
|
||||
import { AKLabel } from "#components/ak-label";
|
||||
|
||||
import { IDGenerator } from "#packages/core/id";
|
||||
|
||||
import { Provider, ProvidersAllListRequest, ProvidersApi } from "@goauthentik/api";
|
||||
|
||||
import { html, nothing } from "lit";
|
||||
@@ -38,11 +42,13 @@ export class AkProviderInput extends AKElement {
|
||||
return this;
|
||||
}
|
||||
|
||||
//#region Properties
|
||||
|
||||
@property({ type: String })
|
||||
name!: string;
|
||||
|
||||
@property({ type: String })
|
||||
label = "";
|
||||
label?: string;
|
||||
|
||||
@property({ type: Number })
|
||||
value?: number;
|
||||
@@ -60,14 +66,26 @@ export class AkProviderInput extends AKElement {
|
||||
super();
|
||||
this.selected = this.selected.bind(this);
|
||||
}
|
||||
/**
|
||||
* A unique ID to associate with the input and label.
|
||||
* @property
|
||||
*/
|
||||
@property({ type: String, reflect: false })
|
||||
public fieldID?: string = IDGenerator.elementID().toString();
|
||||
|
||||
selected(item: Provider) {
|
||||
return this.value !== undefined && this.value === item.pk;
|
||||
}
|
||||
//#endregion
|
||||
|
||||
render() {
|
||||
return html` <ak-form-element-horizontal label=${this.label} name=${this.name}>
|
||||
return html` <ak-form-element-horizontal name=${this.name}>
|
||||
<div slot="label" class="pf-c-form__group-label">
|
||||
${AKLabel({ htmlFor: this.fieldID, required: this.required }, this.label)}
|
||||
</div>
|
||||
|
||||
<ak-search-select
|
||||
.fieldID=${this.fieldID}
|
||||
.selected=${this.selected}
|
||||
.fetchObjects=${fetch}
|
||||
.renderElement=${renderElement}
|
||||
|
||||
@@ -44,6 +44,7 @@ export class BrandForm extends ModelForm<Brand, string> {
|
||||
}
|
||||
|
||||
async send(data: Brand): Promise<Brand> {
|
||||
data.attributes ??= {};
|
||||
if (this.instance?.brandUuid) {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreBrandsUpdate({
|
||||
brandUuid: this.instance.brandUuid,
|
||||
|
||||
@@ -53,6 +53,7 @@ export class GroupForm extends ModelForm<Group, string> {
|
||||
}
|
||||
|
||||
async send(data: Group): Promise<Group> {
|
||||
data.attributes ??= {};
|
||||
if (this.instance?.pk) {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreGroupsPartialUpdate({
|
||||
groupUuid: this.instance.pk,
|
||||
@@ -145,7 +146,7 @@ export class GroupForm extends ModelForm<Group, string> {
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Attributes")} required name="attributes">
|
||||
<ak-form-element-horizontal label=${msg("Attributes")} name="attributes">
|
||||
<ak-codemirror
|
||||
mode=${CodeMirrorMode.YAML}
|
||||
value="${YAML.stringify(this.instance?.attributes ?? {})}"
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { APIError } from "#common/errors/network";
|
||||
import { MessageLevel } from "#common/messages";
|
||||
|
||||
import { ModelForm } from "#elements/forms/ModelForm";
|
||||
import { APIMessage } from "#elements/messages/Message";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
|
||||
@@ -8,4 +12,14 @@ export abstract class BaseProviderForm<T> extends ModelForm<T, number> {
|
||||
? msg("Successfully updated provider.")
|
||||
: msg("Successfully created provider.");
|
||||
}
|
||||
|
||||
protected override formatAPIErrorMessage(error: APIError): APIMessage {
|
||||
return {
|
||||
level: MessageLevel.error,
|
||||
...super.formatAPIErrorMessage(error),
|
||||
message: this.instance
|
||||
? msg("An error occurred while updating the provider.")
|
||||
: msg("An error occurred while creating the provider."),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user