Compare commits
1 Commits
core/fix-g
...
docusaurus
Author | SHA1 | Date | |
---|---|---|---|
19f706e7aa |
3
.github/workflows/ci-main.yml
vendored
3
.github/workflows/ci-main.yml
vendored
@ -200,7 +200,7 @@ jobs:
|
|||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: web/dist
|
path: web/dist
|
||||||
key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'web/src/**', 'web/packages/sfe/src/**') }}-b
|
key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'web/src/**') }}
|
||||||
- name: prepare web ui
|
- name: prepare web ui
|
||||||
if: steps.cache-web.outputs.cache-hit != 'true'
|
if: steps.cache-web.outputs.cache-hit != 'true'
|
||||||
working-directory: web
|
working-directory: web
|
||||||
@ -208,7 +208,6 @@ jobs:
|
|||||||
npm ci
|
npm ci
|
||||||
make -C .. gen-client-ts
|
make -C .. gen-client-ts
|
||||||
npm run build
|
npm run build
|
||||||
npm run build:sfe
|
|
||||||
- name: run e2e
|
- name: run e2e
|
||||||
run: |
|
run: |
|
||||||
uv run coverage run manage.py test ${{ matrix.job.glob }}
|
uv run coverage run manage.py test ${{ matrix.job.glob }}
|
||||||
|
@ -40,8 +40,7 @@ COPY ./web /work/web/
|
|||||||
COPY ./website /work/website/
|
COPY ./website /work/website/
|
||||||
COPY ./gen-ts-api /work/web/node_modules/@goauthentik/api
|
COPY ./gen-ts-api /work/web/node_modules/@goauthentik/api
|
||||||
|
|
||||||
RUN npm run build && \
|
RUN npm run build
|
||||||
npm run build:sfe
|
|
||||||
|
|
||||||
# Stage 3: Build go proxy
|
# Stage 3: Build go proxy
|
||||||
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS go-builder
|
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS go-builder
|
||||||
@ -94,7 +93,7 @@ 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"
|
/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 5: Download uv
|
# Stage 5: Download uv
|
||||||
FROM ghcr.io/astral-sh/uv:0.7.3 AS uv
|
FROM ghcr.io/astral-sh/uv:0.7.2 AS uv
|
||||||
# Stage 6: Base python image
|
# Stage 6: Base python image
|
||||||
FROM ghcr.io/goauthentik/fips-python:3.13.3-slim-bookworm-fips AS python-base
|
FROM ghcr.io/goauthentik/fips-python:3.13.3-slim-bookworm-fips AS python-base
|
||||||
|
|
||||||
|
@ -42,4 +42,4 @@ See [SECURITY.md](SECURITY.md)
|
|||||||
|
|
||||||
## Adoption and Contributions
|
## Adoption and Contributions
|
||||||
|
|
||||||
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).
|
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 [CONTRIBUTING.md file](./CONTRIBUTING.md).
|
||||||
|
@ -5,10 +5,10 @@ from typing import Any
|
|||||||
from django.db.models import F, Q
|
from django.db.models import F, Q
|
||||||
from django.db.models import Value as V
|
from django.db.models import Value as V
|
||||||
from django.http.request import HttpRequest
|
from django.http.request import HttpRequest
|
||||||
|
from sentry_sdk import get_current_span
|
||||||
|
|
||||||
from authentik import get_full_version
|
from authentik import get_full_version
|
||||||
from authentik.brands.models import Brand
|
from authentik.brands.models import Brand
|
||||||
from authentik.lib.sentry import get_http_meta
|
|
||||||
from authentik.tenants.models import Tenant
|
from authentik.tenants.models import Tenant
|
||||||
|
|
||||||
_q_default = Q(default=True)
|
_q_default = Q(default=True)
|
||||||
@ -32,9 +32,13 @@ def context_processor(request: HttpRequest) -> dict[str, Any]:
|
|||||||
"""Context Processor that injects brand object into every template"""
|
"""Context Processor that injects brand object into every template"""
|
||||||
brand = getattr(request, "brand", DEFAULT_BRAND)
|
brand = getattr(request, "brand", DEFAULT_BRAND)
|
||||||
tenant = getattr(request, "tenant", Tenant())
|
tenant = getattr(request, "tenant", Tenant())
|
||||||
|
trace = ""
|
||||||
|
span = get_current_span()
|
||||||
|
if span:
|
||||||
|
trace = span.to_traceparent()
|
||||||
return {
|
return {
|
||||||
"brand": brand,
|
"brand": brand,
|
||||||
"footer_links": tenant.footer_links,
|
"footer_links": tenant.footer_links,
|
||||||
"html_meta": {**get_http_meta()},
|
"sentry_trace": trace,
|
||||||
"version": get_full_version(),
|
"version": get_full_version(),
|
||||||
}
|
}
|
||||||
|
@ -16,12 +16,10 @@ from drf_spectacular.utils import (
|
|||||||
from guardian.shortcuts import get_objects_for_user
|
from guardian.shortcuts import get_objects_for_user
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.fields import CharField, IntegerField, SerializerMethodField
|
from rest_framework.fields import CharField, IntegerField, SerializerMethodField
|
||||||
from rest_framework.permissions import SAFE_METHODS, BasePermission
|
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.serializers import ListSerializer, ValidationError
|
from rest_framework.serializers import ListSerializer, ValidationError
|
||||||
from rest_framework.validators import UniqueValidator
|
from rest_framework.validators import UniqueValidator
|
||||||
from rest_framework.views import View
|
|
||||||
from rest_framework.viewsets import ModelViewSet
|
from rest_framework.viewsets import ModelViewSet
|
||||||
|
|
||||||
from authentik.core.api.used_by import UsedByMixin
|
from authentik.core.api.used_by import UsedByMixin
|
||||||
@ -87,6 +85,34 @@ class GroupSerializer(ModelSerializer):
|
|||||||
raise ValidationError(_("Cannot set group as parent of itself."))
|
raise ValidationError(_("Cannot set group as parent of itself."))
|
||||||
return parent
|
return parent
|
||||||
|
|
||||||
|
def validate_is_superuser(self, superuser: bool):
|
||||||
|
"""Ensure that the user creating this group has permissions to set the superuser flag"""
|
||||||
|
request: Request = self.context.get("request", None)
|
||||||
|
if not request:
|
||||||
|
return superuser
|
||||||
|
# If we're updating an instance, and the state hasn't changed, we don't need to check perms
|
||||||
|
if self.instance and superuser == self.instance.is_superuser:
|
||||||
|
return superuser
|
||||||
|
user: User = request.user
|
||||||
|
perm = (
|
||||||
|
"authentik_core.enable_group_superuser"
|
||||||
|
if superuser
|
||||||
|
else "authentik_core.disable_group_superuser"
|
||||||
|
)
|
||||||
|
has_perm = user.has_perm(perm)
|
||||||
|
if self.instance and not has_perm:
|
||||||
|
has_perm = user.has_perm(perm, self.instance)
|
||||||
|
if not has_perm:
|
||||||
|
raise ValidationError(
|
||||||
|
_(
|
||||||
|
(
|
||||||
|
"User does not have permission to set "
|
||||||
|
"superuser status to {superuser_status}."
|
||||||
|
).format_map({"superuser_status": superuser})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return superuser
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Group
|
model = Group
|
||||||
fields = [
|
fields = [
|
||||||
@ -154,36 +180,6 @@ class GroupFilter(FilterSet):
|
|||||||
fields = ["name", "is_superuser", "members_by_pk", "attributes", "members_by_username"]
|
fields = ["name", "is_superuser", "members_by_pk", "attributes", "members_by_username"]
|
||||||
|
|
||||||
|
|
||||||
class SuperuserSetter(BasePermission):
|
|
||||||
"""Check for enable_group_superuser or disable_group_superuser permissions"""
|
|
||||||
|
|
||||||
message = _("User does not have permission to set the given superuser status.")
|
|
||||||
enable_perm = "authentik_core.enable_group_superuser"
|
|
||||||
disable_perm = "authentik_core.disable_group_superuser"
|
|
||||||
|
|
||||||
def has_permission(self, request: Request, view: View):
|
|
||||||
if request.method != "POST":
|
|
||||||
return True
|
|
||||||
|
|
||||||
is_superuser = request.data.get("is_superuser", False)
|
|
||||||
if not is_superuser:
|
|
||||||
return True
|
|
||||||
|
|
||||||
return request.user.has_perm(self.enable_perm)
|
|
||||||
|
|
||||||
def has_object_permission(self, request: Request, view: View, object: Group):
|
|
||||||
if request.method in SAFE_METHODS:
|
|
||||||
return True
|
|
||||||
|
|
||||||
new_value = request.data.get("is_superuser")
|
|
||||||
old_value = object.is_superuser
|
|
||||||
if new_value is None or new_value == old_value:
|
|
||||||
return True
|
|
||||||
|
|
||||||
perm = self.enable_perm if new_value else self.disable_perm
|
|
||||||
return request.user.has_perm(perm) or request.user.has_perm(perm, object)
|
|
||||||
|
|
||||||
|
|
||||||
class GroupViewSet(UsedByMixin, ModelViewSet):
|
class GroupViewSet(UsedByMixin, ModelViewSet):
|
||||||
"""Group Viewset"""
|
"""Group Viewset"""
|
||||||
|
|
||||||
@ -196,7 +192,6 @@ class GroupViewSet(UsedByMixin, ModelViewSet):
|
|||||||
serializer_class = GroupSerializer
|
serializer_class = GroupSerializer
|
||||||
search_fields = ["name", "is_superuser"]
|
search_fields = ["name", "is_superuser"]
|
||||||
filterset_class = GroupFilter
|
filterset_class = GroupFilter
|
||||||
permission_classes = [SuperuserSetter]
|
|
||||||
ordering = ["name"]
|
ordering = ["name"]
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.contrib.auth.management import create_permissions
|
from django.contrib.auth.management import create_permissions
|
||||||
from django.core.management import call_command
|
|
||||||
from django.core.management.base import BaseCommand, no_translations
|
from django.core.management.base import BaseCommand, no_translations
|
||||||
from guardian.management import create_anonymous_user
|
from guardian.management import create_anonymous_user
|
||||||
|
|
||||||
@ -17,10 +16,6 @@ class Command(BaseCommand):
|
|||||||
"""Check permissions for all apps"""
|
"""Check permissions for all apps"""
|
||||||
for tenant in Tenant.objects.filter(ready=True):
|
for tenant in Tenant.objects.filter(ready=True):
|
||||||
with tenant:
|
with tenant:
|
||||||
# See https://code.djangoproject.com/ticket/28417
|
|
||||||
# Remove potential lingering old permissions
|
|
||||||
call_command("remove_stale_contenttypes", "--no-input")
|
|
||||||
|
|
||||||
for app in apps.get_app_configs():
|
for app in apps.get_app_configs():
|
||||||
self.stdout.write(f"Checking app {app.name} ({app.label})\n")
|
self.stdout.write(f"Checking app {app.name} ({app.label})\n")
|
||||||
create_permissions(app, verbosity=0)
|
create_permissions(app, verbosity=0)
|
||||||
|
@ -31,10 +31,7 @@ class PickleSerializer:
|
|||||||
|
|
||||||
def loads(self, data):
|
def loads(self, data):
|
||||||
"""Unpickle data to be loaded from redis"""
|
"""Unpickle data to be loaded from redis"""
|
||||||
try:
|
return pickle.loads(data) # nosec
|
||||||
return pickle.loads(data) # nosec
|
|
||||||
except Exception:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
|
|
||||||
def _migrate_session(
|
def _migrate_session(
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
# Generated by Django 5.1.9 on 2025-05-14 11:15
|
|
||||||
|
|
||||||
from django.apps.registry import Apps
|
|
||||||
from django.db import migrations
|
|
||||||
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
|
||||||
|
|
||||||
|
|
||||||
def remove_old_authenticated_session_content_type(
|
|
||||||
apps: Apps, schema_editor: BaseDatabaseSchemaEditor
|
|
||||||
):
|
|
||||||
db_alias = schema_editor.connection.alias
|
|
||||||
ContentType = apps.get_model("contenttypes", "ContentType")
|
|
||||||
|
|
||||||
ContentType.objects.using(db_alias).filter(model="oldauthenticatedsession").delete()
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("authentik_core", "0047_delete_oldauthenticatedsession"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RunPython(
|
|
||||||
code=remove_old_authenticated_session_content_type,
|
|
||||||
),
|
|
||||||
]
|
|
@ -21,9 +21,7 @@
|
|||||||
<script src="{% versioned_script 'dist/standalone/loading/index-%v.js' %}" type="module"></script>
|
<script src="{% versioned_script 'dist/standalone/loading/index-%v.js' %}" type="module"></script>
|
||||||
{% block head %}
|
{% block head %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% for key, value in html_meta.items %}
|
<meta name="sentry-trace" content="{{ sentry_trace }}" />
|
||||||
<meta name="{{key}}" content="{{ value }}" />
|
|
||||||
{% endfor %}
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
@ -118,25 +118,12 @@ class TestGroupsAPI(APITestCase):
|
|||||||
reverse("authentik_api:group-list"),
|
reverse("authentik_api:group-list"),
|
||||||
data={"name": generate_id(), "is_superuser": True},
|
data={"name": generate_id(), "is_superuser": True},
|
||||||
)
|
)
|
||||||
self.assertEqual(res.status_code, 403)
|
self.assertEqual(res.status_code, 400)
|
||||||
self.assertJSONEqual(
|
self.assertJSONEqual(
|
||||||
res.content,
|
res.content,
|
||||||
{"detail": "User does not have permission to set the given superuser status."},
|
{"is_superuser": ["User does not have permission to set superuser status to True."]},
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_superuser_update_object_perm(self):
|
|
||||||
"""Test updating a superuser group with object permission"""
|
|
||||||
group = Group.objects.create(name=generate_id(), is_superuser=False)
|
|
||||||
assign_perm("view_group", self.login_user, group)
|
|
||||||
assign_perm("change_group", self.login_user, group)
|
|
||||||
assign_perm("enable_group_superuser", self.login_user, group)
|
|
||||||
self.client.force_login(self.login_user)
|
|
||||||
res = self.client.patch(
|
|
||||||
reverse("authentik_api:group-detail", kwargs={"pk": group.pk}),
|
|
||||||
data={"is_superuser": True},
|
|
||||||
)
|
|
||||||
self.assertEqual(res.status_code, 200)
|
|
||||||
|
|
||||||
def test_superuser_update_no_perm(self):
|
def test_superuser_update_no_perm(self):
|
||||||
"""Test updating a superuser group without permission"""
|
"""Test updating a superuser group without permission"""
|
||||||
group = Group.objects.create(name=generate_id(), is_superuser=True)
|
group = Group.objects.create(name=generate_id(), is_superuser=True)
|
||||||
@ -147,10 +134,10 @@ class TestGroupsAPI(APITestCase):
|
|||||||
reverse("authentik_api:group-detail", kwargs={"pk": group.pk}),
|
reverse("authentik_api:group-detail", kwargs={"pk": group.pk}),
|
||||||
data={"is_superuser": False},
|
data={"is_superuser": False},
|
||||||
)
|
)
|
||||||
self.assertEqual(res.status_code, 403)
|
self.assertEqual(res.status_code, 400)
|
||||||
self.assertJSONEqual(
|
self.assertJSONEqual(
|
||||||
res.content,
|
res.content,
|
||||||
{"detail": "User does not have permission to set the given superuser status."},
|
{"is_superuser": ["User does not have permission to set superuser status to False."]},
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_superuser_update_no_change(self):
|
def test_superuser_update_no_change(self):
|
||||||
@ -176,27 +163,3 @@ class TestGroupsAPI(APITestCase):
|
|||||||
data={"name": generate_id(), "is_superuser": True},
|
data={"name": generate_id(), "is_superuser": True},
|
||||||
)
|
)
|
||||||
self.assertEqual(res.status_code, 201)
|
self.assertEqual(res.status_code, 201)
|
||||||
|
|
||||||
def test_superuser_create_no_perm(self):
|
|
||||||
"""Test creating a superuser group with no permission"""
|
|
||||||
assign_perm("authentik_core.add_group", self.login_user)
|
|
||||||
self.client.force_login(self.login_user)
|
|
||||||
res = self.client.post(
|
|
||||||
reverse("authentik_api:group-list"),
|
|
||||||
data={"name": generate_id(), "is_superuser": True},
|
|
||||||
)
|
|
||||||
self.assertEqual(res.status_code, 403)
|
|
||||||
self.assertJSONEqual(
|
|
||||||
res.content,
|
|
||||||
{"detail": "User does not have permission to set the given superuser status."},
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_no_superuser_create_no_perm(self):
|
|
||||||
"""Test creating a non-superuser group with no permission"""
|
|
||||||
assign_perm("authentik_core.add_group", self.login_user)
|
|
||||||
self.client.force_login(self.login_user)
|
|
||||||
res = self.client.post(
|
|
||||||
reverse("authentik_api:group-list"),
|
|
||||||
data={"name": generate_id()},
|
|
||||||
)
|
|
||||||
self.assertEqual(res.status_code, 201)
|
|
||||||
|
@ -132,14 +132,13 @@ class LicenseKey:
|
|||||||
"""Get a summarized version of all (not expired) licenses"""
|
"""Get a summarized version of all (not expired) licenses"""
|
||||||
total = LicenseKey(get_license_aud(), 0, "Summarized license", 0, 0)
|
total = LicenseKey(get_license_aud(), 0, "Summarized license", 0, 0)
|
||||||
for lic in License.objects.all():
|
for lic in License.objects.all():
|
||||||
if lic.is_valid:
|
total.internal_users += lic.internal_users
|
||||||
total.internal_users += lic.internal_users
|
total.external_users += lic.external_users
|
||||||
total.external_users += lic.external_users
|
|
||||||
total.license_flags.extend(lic.status.license_flags)
|
|
||||||
exp_ts = int(mktime(lic.expiry.timetuple()))
|
exp_ts = int(mktime(lic.expiry.timetuple()))
|
||||||
if total.exp == 0:
|
if total.exp == 0:
|
||||||
total.exp = exp_ts
|
total.exp = exp_ts
|
||||||
total.exp = max(total.exp, exp_ts)
|
total.exp = max(total.exp, exp_ts)
|
||||||
|
total.license_flags.extend(lic.status.license_flags)
|
||||||
return total
|
return total
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -39,10 +39,6 @@ class License(SerializerModel):
|
|||||||
internal_users = models.BigIntegerField()
|
internal_users = models.BigIntegerField()
|
||||||
external_users = models.BigIntegerField()
|
external_users = models.BigIntegerField()
|
||||||
|
|
||||||
@property
|
|
||||||
def is_valid(self) -> bool:
|
|
||||||
return self.expiry >= now()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def serializer(self) -> type[BaseSerializer]:
|
def serializer(self) -> type[BaseSerializer]:
|
||||||
from authentik.enterprise.api import LicenseSerializer
|
from authentik.enterprise.api import LicenseSerializer
|
||||||
|
@ -8,7 +8,6 @@ from django.test import TestCase
|
|||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
|
|
||||||
from authentik.core.models import User
|
|
||||||
from authentik.enterprise.license import LicenseKey
|
from authentik.enterprise.license import LicenseKey
|
||||||
from authentik.enterprise.models import (
|
from authentik.enterprise.models import (
|
||||||
THRESHOLD_READ_ONLY_WEEKS,
|
THRESHOLD_READ_ONLY_WEEKS,
|
||||||
@ -72,9 +71,9 @@ class TestEnterpriseLicense(TestCase):
|
|||||||
)
|
)
|
||||||
def test_valid_multiple(self):
|
def test_valid_multiple(self):
|
||||||
"""Check license verification"""
|
"""Check license verification"""
|
||||||
lic = License.objects.create(key=generate_id(), expiry=expiry_valid)
|
lic = License.objects.create(key=generate_id())
|
||||||
self.assertTrue(lic.status.status().is_valid)
|
self.assertTrue(lic.status.status().is_valid)
|
||||||
lic2 = License.objects.create(key=generate_id(), expiry=expiry_valid)
|
lic2 = License.objects.create(key=generate_id())
|
||||||
self.assertTrue(lic2.status.status().is_valid)
|
self.assertTrue(lic2.status.status().is_valid)
|
||||||
total = LicenseKey.get_total()
|
total = LicenseKey.get_total()
|
||||||
self.assertEqual(total.internal_users, 200)
|
self.assertEqual(total.internal_users, 200)
|
||||||
@ -233,9 +232,7 @@ class TestEnterpriseLicense(TestCase):
|
|||||||
)
|
)
|
||||||
def test_expiry_expired(self):
|
def test_expiry_expired(self):
|
||||||
"""Check license verification"""
|
"""Check license verification"""
|
||||||
User.objects.all().delete()
|
License.objects.create(key=generate_id())
|
||||||
License.objects.all().delete()
|
|
||||||
License.objects.create(key=generate_id(), expiry=expiry_expired)
|
|
||||||
self.assertEqual(LicenseKey.get_total().summary().status, LicenseUsageStatus.EXPIRED)
|
self.assertEqual(LicenseKey.get_total().summary().status, LicenseUsageStatus.EXPIRED)
|
||||||
|
|
||||||
@patch(
|
@patch(
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/sfe/bootstrap.min.css' %}">
|
<link rel="stylesheet" type="text/css" href="{% static 'dist/sfe/bootstrap.min.css' %}">
|
||||||
<meta name="sentry-trace" content="{{ sentry_trace }}" />
|
<meta name="sentry-trace" content="{{ sentry_trace }}" />
|
||||||
<link rel="prefetch" href="{{ flow_background_url }}" />
|
|
||||||
{% include "base/header_js.html" %}
|
{% include "base/header_js.html" %}
|
||||||
<style>
|
<style>
|
||||||
html,
|
html,
|
||||||
@ -23,7 +22,7 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
background-image: url("{{ flow_background_url }}");
|
background-image: url("{{ flow.background_url }}");
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
{% block head_before %}
|
{% block head_before %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
<link rel="prefetch" href="{{ flow_background_url }}" />
|
<link rel="prefetch" href="{{ flow.background_url }}" />
|
||||||
{% if flow.compatibility_mode and not inspector %}
|
{% if flow.compatibility_mode and not inspector %}
|
||||||
<script>ShadyDOM = { force: !navigator.webdriver };</script>
|
<script>ShadyDOM = { force: !navigator.webdriver };</script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -21,7 +21,7 @@ window.authentik.flow = {
|
|||||||
<script src="{% versioned_script 'dist/flow/FlowInterface-%v.js' %}" type="module"></script>
|
<script src="{% versioned_script 'dist/flow/FlowInterface-%v.js' %}" type="module"></script>
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
--ak-flow-background: url("{{ flow_background_url }}");
|
--ak-flow-background: url("{{ flow.background_url }}");
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -13,9 +13,7 @@ class FlowInterfaceView(InterfaceView):
|
|||||||
"""Flow interface"""
|
"""Flow interface"""
|
||||||
|
|
||||||
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
|
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
|
||||||
flow = get_object_or_404(Flow, slug=self.kwargs.get("flow_slug"))
|
kwargs["flow"] = get_object_or_404(Flow, slug=self.kwargs.get("flow_slug"))
|
||||||
kwargs["flow"] = flow
|
|
||||||
kwargs["flow_background_url"] = flow.background_url(self.request)
|
|
||||||
kwargs["inspector"] = "inspector" in self.request.GET
|
kwargs["inspector"] = "inspector" in self.request.GET
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
@ -363,9 +363,6 @@ def django_db_config(config: ConfigLoader | None = None) -> dict:
|
|||||||
pool_options = config.get_dict_from_b64_json("postgresql.pool_options", True)
|
pool_options = config.get_dict_from_b64_json("postgresql.pool_options", True)
|
||||||
if not pool_options:
|
if not pool_options:
|
||||||
pool_options = True
|
pool_options = True
|
||||||
# FIXME: Temporarily force pool to be deactivated.
|
|
||||||
# See https://github.com/goauthentik/authentik/issues/14320
|
|
||||||
pool_options = False
|
|
||||||
|
|
||||||
db = {
|
db = {
|
||||||
"default": {
|
"default": {
|
||||||
|
@ -17,7 +17,7 @@ from ldap3.core.exceptions import LDAPException
|
|||||||
from redis.exceptions import ConnectionError as RedisConnectionError
|
from redis.exceptions import ConnectionError as RedisConnectionError
|
||||||
from redis.exceptions import RedisError, ResponseError
|
from redis.exceptions import RedisError, ResponseError
|
||||||
from rest_framework.exceptions import APIException
|
from rest_framework.exceptions import APIException
|
||||||
from sentry_sdk import HttpTransport, get_current_scope
|
from sentry_sdk import HttpTransport
|
||||||
from sentry_sdk import init as sentry_sdk_init
|
from sentry_sdk import init as sentry_sdk_init
|
||||||
from sentry_sdk.api import set_tag
|
from sentry_sdk.api import set_tag
|
||||||
from sentry_sdk.integrations.argv import ArgvIntegration
|
from sentry_sdk.integrations.argv import ArgvIntegration
|
||||||
@ -27,7 +27,6 @@ from sentry_sdk.integrations.redis import RedisIntegration
|
|||||||
from sentry_sdk.integrations.socket import SocketIntegration
|
from sentry_sdk.integrations.socket import SocketIntegration
|
||||||
from sentry_sdk.integrations.stdlib import StdlibIntegration
|
from sentry_sdk.integrations.stdlib import StdlibIntegration
|
||||||
from sentry_sdk.integrations.threading import ThreadingIntegration
|
from sentry_sdk.integrations.threading import ThreadingIntegration
|
||||||
from sentry_sdk.tracing import BAGGAGE_HEADER_NAME, SENTRY_TRACE_HEADER_NAME
|
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
from websockets.exceptions import WebSocketException
|
from websockets.exceptions import WebSocketException
|
||||||
|
|
||||||
@ -96,8 +95,6 @@ def traces_sampler(sampling_context: dict) -> float:
|
|||||||
return 0
|
return 0
|
||||||
if _type == "websocket":
|
if _type == "websocket":
|
||||||
return 0
|
return 0
|
||||||
if CONFIG.get_bool("debug"):
|
|
||||||
return 1
|
|
||||||
return float(CONFIG.get("error_reporting.sample_rate", 0.1))
|
return float(CONFIG.get("error_reporting.sample_rate", 0.1))
|
||||||
|
|
||||||
|
|
||||||
@ -170,14 +167,3 @@ def before_send(event: dict, hint: dict) -> dict | None:
|
|||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
return None
|
return None
|
||||||
return event
|
return event
|
||||||
|
|
||||||
|
|
||||||
def get_http_meta():
|
|
||||||
"""Get sentry-related meta key-values"""
|
|
||||||
scope = get_current_scope()
|
|
||||||
meta = {
|
|
||||||
SENTRY_TRACE_HEADER_NAME: scope.get_traceparent() or "",
|
|
||||||
}
|
|
||||||
if bag := scope.get_baggage():
|
|
||||||
meta[BAGGAGE_HEADER_NAME] = bag.serialize()
|
|
||||||
return meta
|
|
||||||
|
@ -494,88 +494,86 @@ class TestConfig(TestCase):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# FIXME: Temporarily force pool to be deactivated.
|
def test_db_pool(self):
|
||||||
# See https://github.com/goauthentik/authentik/issues/14320
|
"""Test DB Config with pool"""
|
||||||
# def test_db_pool(self):
|
config = ConfigLoader()
|
||||||
# """Test DB Config with pool"""
|
config.set("postgresql.host", "foo")
|
||||||
# config = ConfigLoader()
|
config.set("postgresql.name", "foo")
|
||||||
# config.set("postgresql.host", "foo")
|
config.set("postgresql.user", "foo")
|
||||||
# config.set("postgresql.name", "foo")
|
config.set("postgresql.password", "foo")
|
||||||
# config.set("postgresql.user", "foo")
|
config.set("postgresql.port", "foo")
|
||||||
# config.set("postgresql.password", "foo")
|
config.set("postgresql.test.name", "foo")
|
||||||
# config.set("postgresql.port", "foo")
|
config.set("postgresql.use_pool", True)
|
||||||
# config.set("postgresql.test.name", "foo")
|
conf = django_db_config(config)
|
||||||
# config.set("postgresql.use_pool", True)
|
self.assertEqual(
|
||||||
# conf = django_db_config(config)
|
conf,
|
||||||
# self.assertEqual(
|
{
|
||||||
# conf,
|
"default": {
|
||||||
# {
|
"ENGINE": "authentik.root.db",
|
||||||
# "default": {
|
"HOST": "foo",
|
||||||
# "ENGINE": "authentik.root.db",
|
"NAME": "foo",
|
||||||
# "HOST": "foo",
|
"OPTIONS": {
|
||||||
# "NAME": "foo",
|
"pool": True,
|
||||||
# "OPTIONS": {
|
"sslcert": None,
|
||||||
# "pool": True,
|
"sslkey": None,
|
||||||
# "sslcert": None,
|
"sslmode": None,
|
||||||
# "sslkey": None,
|
"sslrootcert": None,
|
||||||
# "sslmode": None,
|
},
|
||||||
# "sslrootcert": None,
|
"PASSWORD": "foo",
|
||||||
# },
|
"PORT": "foo",
|
||||||
# "PASSWORD": "foo",
|
"TEST": {"NAME": "foo"},
|
||||||
# "PORT": "foo",
|
"USER": "foo",
|
||||||
# "TEST": {"NAME": "foo"},
|
"CONN_MAX_AGE": 0,
|
||||||
# "USER": "foo",
|
"CONN_HEALTH_CHECKS": False,
|
||||||
# "CONN_MAX_AGE": 0,
|
"DISABLE_SERVER_SIDE_CURSORS": False,
|
||||||
# "CONN_HEALTH_CHECKS": False,
|
}
|
||||||
# "DISABLE_SERVER_SIDE_CURSORS": False,
|
},
|
||||||
# }
|
)
|
||||||
# },
|
|
||||||
# )
|
|
||||||
|
|
||||||
# def test_db_pool_options(self):
|
def test_db_pool_options(self):
|
||||||
# """Test DB Config with pool"""
|
"""Test DB Config with pool"""
|
||||||
# config = ConfigLoader()
|
config = ConfigLoader()
|
||||||
# config.set("postgresql.host", "foo")
|
config.set("postgresql.host", "foo")
|
||||||
# config.set("postgresql.name", "foo")
|
config.set("postgresql.name", "foo")
|
||||||
# config.set("postgresql.user", "foo")
|
config.set("postgresql.user", "foo")
|
||||||
# config.set("postgresql.password", "foo")
|
config.set("postgresql.password", "foo")
|
||||||
# config.set("postgresql.port", "foo")
|
config.set("postgresql.port", "foo")
|
||||||
# config.set("postgresql.test.name", "foo")
|
config.set("postgresql.test.name", "foo")
|
||||||
# config.set("postgresql.use_pool", True)
|
config.set("postgresql.use_pool", True)
|
||||||
# config.set(
|
config.set(
|
||||||
# "postgresql.pool_options",
|
"postgresql.pool_options",
|
||||||
# base64.b64encode(
|
base64.b64encode(
|
||||||
# dumps(
|
dumps(
|
||||||
# {
|
{
|
||||||
# "max_size": 15,
|
"max_size": 15,
|
||||||
# }
|
}
|
||||||
# ).encode()
|
).encode()
|
||||||
# ).decode(),
|
).decode(),
|
||||||
# )
|
)
|
||||||
# conf = django_db_config(config)
|
conf = django_db_config(config)
|
||||||
# self.assertEqual(
|
self.assertEqual(
|
||||||
# conf,
|
conf,
|
||||||
# {
|
{
|
||||||
# "default": {
|
"default": {
|
||||||
# "ENGINE": "authentik.root.db",
|
"ENGINE": "authentik.root.db",
|
||||||
# "HOST": "foo",
|
"HOST": "foo",
|
||||||
# "NAME": "foo",
|
"NAME": "foo",
|
||||||
# "OPTIONS": {
|
"OPTIONS": {
|
||||||
# "pool": {
|
"pool": {
|
||||||
# "max_size": 15,
|
"max_size": 15,
|
||||||
# },
|
},
|
||||||
# "sslcert": None,
|
"sslcert": None,
|
||||||
# "sslkey": None,
|
"sslkey": None,
|
||||||
# "sslmode": None,
|
"sslmode": None,
|
||||||
# "sslrootcert": None,
|
"sslrootcert": None,
|
||||||
# },
|
},
|
||||||
# "PASSWORD": "foo",
|
"PASSWORD": "foo",
|
||||||
# "PORT": "foo",
|
"PORT": "foo",
|
||||||
# "TEST": {"NAME": "foo"},
|
"TEST": {"NAME": "foo"},
|
||||||
# "USER": "foo",
|
"USER": "foo",
|
||||||
# "CONN_MAX_AGE": 0,
|
"CONN_MAX_AGE": 0,
|
||||||
# "CONN_HEALTH_CHECKS": False,
|
"CONN_HEALTH_CHECKS": False,
|
||||||
# "DISABLE_SERVER_SIDE_CURSORS": False,
|
"DISABLE_SERVER_SIDE_CURSORS": False,
|
||||||
# }
|
}
|
||||||
# },
|
},
|
||||||
# )
|
)
|
||||||
|
@ -56,7 +56,6 @@ EXPOSE 3389 6636 9300
|
|||||||
|
|
||||||
USER 1000
|
USER 1000
|
||||||
|
|
||||||
ENV TMPDIR=/dev/shm/ \
|
ENV GOFIPS=1
|
||||||
GOFIPS=1
|
|
||||||
|
|
||||||
ENTRYPOINT ["/ldap"]
|
ENTRYPOINT ["/ldap"]
|
||||||
|
@ -97,7 +97,6 @@ elif [[ "$1" == "test-all" ]]; then
|
|||||||
elif [[ "$1" == "healthcheck" ]]; then
|
elif [[ "$1" == "healthcheck" ]]; then
|
||||||
run_authentik healthcheck $(cat $MODE_FILE)
|
run_authentik healthcheck $(cat $MODE_FILE)
|
||||||
elif [[ "$1" == "dump_config" ]]; then
|
elif [[ "$1" == "dump_config" ]]; then
|
||||||
shift
|
|
||||||
exec python -m authentik.lib.config $@
|
exec python -m authentik.lib.config $@
|
||||||
elif [[ "$1" == "debug" ]]; then
|
elif [[ "$1" == "debug" ]]; then
|
||||||
exec sleep infinity
|
exec sleep infinity
|
||||||
|
8
lifecycle/aws/package-lock.json
generated
8
lifecycle/aws/package-lock.json
generated
@ -9,7 +9,7 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"aws-cdk": "^2.1014.0",
|
"aws-cdk": "^2.1013.0",
|
||||||
"cross-env": "^7.0.3"
|
"cross-env": "^7.0.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -17,9 +17,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/aws-cdk": {
|
"node_modules/aws-cdk": {
|
||||||
"version": "2.1014.0",
|
"version": "2.1013.0",
|
||||||
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1014.0.tgz",
|
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1013.0.tgz",
|
||||||
"integrity": "sha512-es101rtRAClix9BncNL54iW90MiOyRv4iCC5tv/firGDnidS6pPinuK0IIFt0RO6w0+3heRxWBXg8HY+f9877w==",
|
"integrity": "sha512-cbq4cOoEIZueMWenGgfI4RujS+AQ9GaMCTlW/3CnvEIhMD8j/tgZx7PTtgMuvwYrRoEeb/wTxgLPgUd5FhsoHA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
"node": ">=20"
|
"node": ">=20"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"aws-cdk": "^2.1014.0",
|
"aws-cdk": "^2.1013.0",
|
||||||
"cross-env": "^7.0.3"
|
"cross-env": "^7.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,6 @@ EXPOSE 9000 9300 9443
|
|||||||
|
|
||||||
USER 1000
|
USER 1000
|
||||||
|
|
||||||
ENV TMPDIR=/dev/shm/ \
|
ENV GOFIPS=1
|
||||||
GOFIPS=1
|
|
||||||
|
|
||||||
ENTRYPOINT ["/proxy"]
|
ENTRYPOINT ["/proxy"]
|
||||||
|
180
pyproject.toml
180
pyproject.toml
@ -5,100 +5,100 @@ description = ""
|
|||||||
authors = [{ name = "authentik Team", email = "hello@goauthentik.io" }]
|
authors = [{ name = "authentik Team", email = "hello@goauthentik.io" }]
|
||||||
requires-python = "==3.13.*"
|
requires-python = "==3.13.*"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"argon2-cffi==23.1.0",
|
"argon2-cffi",
|
||||||
"celery==5.5.2",
|
"celery",
|
||||||
"channels==4.2.2",
|
"channels",
|
||||||
"channels-redis==4.2.1",
|
"channels-redis",
|
||||||
"cryptography==44.0.3",
|
"cryptography",
|
||||||
"dacite==1.9.2",
|
"dacite",
|
||||||
"deepmerge==2.0",
|
"deepmerge",
|
||||||
"defusedxml==0.7.1",
|
"defusedxml",
|
||||||
"django==5.1.9",
|
"django",
|
||||||
"django-countries==7.6.1",
|
"django-countries",
|
||||||
"django-cte==1.3.3",
|
"django-cte",
|
||||||
"django-filter==25.1",
|
"django-filter",
|
||||||
"django-guardian<3.0.0",
|
"django-guardian",
|
||||||
"django-model-utils==5.0.0",
|
"django-model-utils",
|
||||||
"django-pglock==1.7.1",
|
"django-pglock",
|
||||||
"django-prometheus==2.3.1",
|
"django-prometheus",
|
||||||
"django-redis==5.4.0",
|
"django-redis",
|
||||||
"django-storages[s3]==1.14.6",
|
"django-storages[s3]",
|
||||||
"django-tenants==3.7.0",
|
"django-tenants",
|
||||||
"djangorestframework==3.16.0",
|
"djangorestframework",
|
||||||
"djangorestframework-guardian==0.3.0",
|
"djangorestframework-guardian",
|
||||||
"docker==7.1.0",
|
"docker",
|
||||||
"drf-orjson-renderer==1.7.3",
|
"drf-orjson-renderer",
|
||||||
"drf-spectacular==0.28.0",
|
"drf-spectacular",
|
||||||
"dumb-init==1.2.5.post1",
|
"dumb-init",
|
||||||
"duo-client==5.5.0",
|
"duo-client",
|
||||||
"fido2==1.2.0",
|
"fido2",
|
||||||
"flower==2.0.1",
|
"flower",
|
||||||
"geoip2==5.1.0",
|
"geoip2",
|
||||||
"geopy==2.4.1",
|
"geopy",
|
||||||
"google-api-python-client==2.169.0",
|
"google-api-python-client",
|
||||||
"gssapi==1.9.0",
|
"gssapi",
|
||||||
"gunicorn==23.0.0",
|
"gunicorn",
|
||||||
"jsonpatch==1.33",
|
"jsonpatch",
|
||||||
"jwcrypto==1.5.6",
|
"jwcrypto",
|
||||||
"kubernetes==32.0.1",
|
"kubernetes",
|
||||||
"ldap3==2.9.1",
|
"ldap3",
|
||||||
"lxml==5.4.0",
|
"lxml",
|
||||||
"msgraph-sdk==1.30.0",
|
"msgraph-sdk",
|
||||||
"opencontainers==0.0.14",
|
"opencontainers",
|
||||||
"packaging==25.0",
|
"packaging",
|
||||||
"paramiko==3.5.1",
|
"paramiko",
|
||||||
"psycopg[c,pool]==3.2.9",
|
"psycopg[c, pool]",
|
||||||
"pydantic==2.11.4",
|
"pydantic",
|
||||||
"pydantic-scim==0.0.8",
|
"pydantic-scim",
|
||||||
"pyjwt==2.10.1",
|
"pyjwt",
|
||||||
"pyrad==2.4",
|
"pyrad",
|
||||||
"python-kadmin-rs==0.6.0",
|
"python-kadmin-rs",
|
||||||
"pyyaml==6.0.2",
|
"pyyaml",
|
||||||
"requests-oauthlib==2.0.0",
|
"requests-oauthlib",
|
||||||
"scim2-filter-parser==0.7.0",
|
"scim2-filter-parser",
|
||||||
"sentry-sdk==2.28.0",
|
"sentry-sdk",
|
||||||
"service-identity==24.2.0",
|
"service_identity",
|
||||||
"setproctitle==1.3.6",
|
"setproctitle",
|
||||||
"structlog==25.3.0",
|
"structlog",
|
||||||
"swagger-spec-validator==3.0.4",
|
"swagger-spec-validator",
|
||||||
"tenant-schemas-celery==4.0.1",
|
"tenant-schemas-celery",
|
||||||
"twilio==9.6.1",
|
"twilio",
|
||||||
"ua-parser==1.0.1",
|
"ua-parser",
|
||||||
"unidecode==1.4.0",
|
"unidecode",
|
||||||
"urllib3<3",
|
"urllib3 <3",
|
||||||
"uvicorn[standard]==0.34.2",
|
"uvicorn[standard]",
|
||||||
"watchdog==6.0.0",
|
"watchdog",
|
||||||
"webauthn==2.5.2",
|
"webauthn",
|
||||||
"wsproto==1.2.0",
|
"wsproto",
|
||||||
"xmlsec==1.3.15",
|
"xmlsec",
|
||||||
"zxcvbn==4.5.0",
|
"zxcvbn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependency-groups]
|
[dependency-groups]
|
||||||
dev = [
|
dev = [
|
||||||
"aws-cdk-lib==2.188.0",
|
"aws-cdk-lib",
|
||||||
"bandit==1.8.3",
|
"bandit",
|
||||||
"black==25.1.0",
|
"black",
|
||||||
"bump2version==1.0.1",
|
"bump2version",
|
||||||
"channels[daphne]==4.2.2",
|
"channels[daphne]",
|
||||||
"codespell==2.4.1",
|
"codespell",
|
||||||
"colorama==0.4.6",
|
"colorama",
|
||||||
"constructs==10.4.2",
|
"constructs",
|
||||||
"coverage[toml]==7.8.0",
|
"coverage[toml]",
|
||||||
"debugpy==1.8.14",
|
"debugpy",
|
||||||
"drf-jsonschema-serializer==3.0.0",
|
"drf-jsonschema-serializer",
|
||||||
"freezegun==1.5.1",
|
"freezegun",
|
||||||
"importlib-metadata==8.6.1",
|
"importlib-metadata",
|
||||||
"k5test==0.10.4",
|
"k5test",
|
||||||
"pdoc==15.0.3",
|
"pdoc",
|
||||||
"pytest==8.3.5",
|
"pytest",
|
||||||
"pytest-django==4.11.1",
|
"pytest-django",
|
||||||
"pytest-github-actions-annotate-failures==0.3.0",
|
"pytest-github-actions-annotate-failures",
|
||||||
"pytest-randomly==3.16.0",
|
"pytest-randomly",
|
||||||
"pytest-timeout==2.4.0",
|
"pytest-timeout",
|
||||||
"requests-mock==1.12.1",
|
"requests-mock",
|
||||||
"ruff==0.11.9",
|
"ruff",
|
||||||
"selenium==4.32.0",
|
"selenium",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.uv]
|
[tool.uv]
|
||||||
|
@ -56,7 +56,6 @@ HEALTHCHECK --interval=5s --retries=20 --start-period=3s CMD [ "/rac", "healthch
|
|||||||
|
|
||||||
USER 1000
|
USER 1000
|
||||||
|
|
||||||
ENV TMPDIR=/dev/shm/ \
|
ENV GOFIPS=1
|
||||||
GOFIPS=1
|
|
||||||
|
|
||||||
ENTRYPOINT ["/rac"]
|
ENTRYPOINT ["/rac"]
|
||||||
|
@ -56,7 +56,6 @@ EXPOSE 1812/udp 9300
|
|||||||
|
|
||||||
USER 1000
|
USER 1000
|
||||||
|
|
||||||
ENV TMPDIR=/dev/shm/ \
|
ENV GOFIPS=1
|
||||||
GOFIPS=1
|
|
||||||
|
|
||||||
ENTRYPOINT ["/radius"]
|
ENTRYPOINT ["/radius"]
|
||||||
|
@ -1,51 +0,0 @@
|
|||||||
"""test default login (using SFE interface) flow"""
|
|
||||||
|
|
||||||
from time import sleep
|
|
||||||
|
|
||||||
from selenium.webdriver.common.by import By
|
|
||||||
from selenium.webdriver.common.keys import Keys
|
|
||||||
|
|
||||||
from authentik.blueprints.tests import apply_blueprint
|
|
||||||
from tests.e2e.utils import SeleniumTestCase, retry
|
|
||||||
|
|
||||||
|
|
||||||
class TestFlowsLoginSFE(SeleniumTestCase):
|
|
||||||
"""test default login flow"""
|
|
||||||
|
|
||||||
def login(self):
|
|
||||||
"""Do entire login flow adjusted for SFE"""
|
|
||||||
flow_executor = self.driver.find_element(By.ID, "flow-sfe-container")
|
|
||||||
identification_stage = flow_executor.find_element(By.ID, "ident-form")
|
|
||||||
|
|
||||||
identification_stage.find_element(By.CSS_SELECTOR, "input[name=uid_field]").click()
|
|
||||||
identification_stage.find_element(By.CSS_SELECTOR, "input[name=uid_field]").send_keys(
|
|
||||||
self.user.username
|
|
||||||
)
|
|
||||||
identification_stage.find_element(By.CSS_SELECTOR, "input[name=uid_field]").send_keys(
|
|
||||||
Keys.ENTER
|
|
||||||
)
|
|
||||||
|
|
||||||
password_stage = flow_executor.find_element(By.ID, "password-form")
|
|
||||||
password_stage.find_element(By.CSS_SELECTOR, "input[name=password]").send_keys(
|
|
||||||
self.user.username
|
|
||||||
)
|
|
||||||
password_stage.find_element(By.CSS_SELECTOR, "input[name=password]").send_keys(Keys.ENTER)
|
|
||||||
sleep(1)
|
|
||||||
|
|
||||||
@retry()
|
|
||||||
@apply_blueprint(
|
|
||||||
"default/flow-default-authentication-flow.yaml",
|
|
||||||
"default/flow-default-invalidation-flow.yaml",
|
|
||||||
)
|
|
||||||
def test_login(self):
|
|
||||||
"""test default login flow"""
|
|
||||||
self.driver.get(
|
|
||||||
self.url(
|
|
||||||
"authentik_core:if-flow",
|
|
||||||
flow_slug="default-authentication-flow",
|
|
||||||
query={"sfe": True},
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.login()
|
|
||||||
self.wait_for_url(self.if_user_url("/library"))
|
|
||||||
self.assert_user(self.user)
|
|
@ -26,7 +26,6 @@ from selenium import webdriver
|
|||||||
from selenium.common.exceptions import NoSuchElementException, TimeoutException, WebDriverException
|
from selenium.common.exceptions import NoSuchElementException, TimeoutException, WebDriverException
|
||||||
from selenium.webdriver.common.by import By
|
from selenium.webdriver.common.by import By
|
||||||
from selenium.webdriver.common.keys import Keys
|
from selenium.webdriver.common.keys import Keys
|
||||||
from selenium.webdriver.remote.command import Command
|
|
||||||
from selenium.webdriver.remote.webdriver import WebDriver
|
from selenium.webdriver.remote.webdriver import WebDriver
|
||||||
from selenium.webdriver.remote.webelement import WebElement
|
from selenium.webdriver.remote.webelement import WebElement
|
||||||
from selenium.webdriver.support.wait import WebDriverWait
|
from selenium.webdriver.support.wait import WebDriverWait
|
||||||
@ -198,12 +197,7 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase):
|
|||||||
super().tearDown()
|
super().tearDown()
|
||||||
if IS_CI:
|
if IS_CI:
|
||||||
print("::group::Browser logs")
|
print("::group::Browser logs")
|
||||||
# Very verbose way to get browser logs
|
for line in self.driver.get_log("browser"):
|
||||||
# https://github.com/SeleniumHQ/selenium/pull/15641
|
|
||||||
# for some reason this removes the `get_log` API from Remote Webdriver
|
|
||||||
# and only keeps it on the local Chrome web driver, even when using
|
|
||||||
# a remote chrome driver...? (nvm the fact this was released as a minor version)
|
|
||||||
for line in self.driver.execute(Command.GET_LOG, {"type": "browser"})["value"]:
|
|
||||||
print(line["message"])
|
print(line["message"])
|
||||||
if IS_CI:
|
if IS_CI:
|
||||||
print("::endgroup::")
|
print("::endgroup::")
|
||||||
@ -241,7 +235,7 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase):
|
|||||||
return element
|
return element
|
||||||
|
|
||||||
def login(self):
|
def login(self):
|
||||||
"""Do entire login flow"""
|
"""Do entire login flow and check user afterwards"""
|
||||||
flow_executor = self.get_shadow_root("ak-flow-executor")
|
flow_executor = self.get_shadow_root("ak-flow-executor")
|
||||||
identification_stage = self.get_shadow_root("ak-stage-identification", flow_executor)
|
identification_stage = self.get_shadow_root("ak-stage-identification", flow_executor)
|
||||||
|
|
||||||
|
377
uv.lock
generated
377
uv.lock
generated
@ -265,100 +265,100 @@ dev = [
|
|||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
{ name = "argon2-cffi", specifier = "==23.1.0" },
|
{ name = "argon2-cffi" },
|
||||||
{ name = "celery", specifier = "==5.5.2" },
|
{ name = "celery" },
|
||||||
{ name = "channels", specifier = "==4.2.2" },
|
{ name = "channels" },
|
||||||
{ name = "channels-redis", specifier = "==4.2.1" },
|
{ name = "channels-redis" },
|
||||||
{ name = "cryptography", specifier = "==44.0.3" },
|
{ name = "cryptography" },
|
||||||
{ name = "dacite", specifier = "==1.9.2" },
|
{ name = "dacite" },
|
||||||
{ name = "deepmerge", specifier = "==2.0" },
|
{ name = "deepmerge" },
|
||||||
{ name = "defusedxml", specifier = "==0.7.1" },
|
{ name = "defusedxml" },
|
||||||
{ name = "django", specifier = "==5.1.9" },
|
{ name = "django" },
|
||||||
{ name = "django-countries", specifier = "==7.6.1" },
|
{ name = "django-countries" },
|
||||||
{ name = "django-cte", specifier = "==1.3.3" },
|
{ name = "django-cte" },
|
||||||
{ name = "django-filter", specifier = "==25.1" },
|
{ name = "django-filter" },
|
||||||
{ name = "django-guardian", specifier = "<3.0.0" },
|
{ name = "django-guardian" },
|
||||||
{ name = "django-model-utils", specifier = "==5.0.0" },
|
{ name = "django-model-utils" },
|
||||||
{ name = "django-pglock", specifier = "==1.7.1" },
|
{ name = "django-pglock" },
|
||||||
{ name = "django-prometheus", specifier = "==2.3.1" },
|
{ name = "django-prometheus" },
|
||||||
{ name = "django-redis", specifier = "==5.4.0" },
|
{ name = "django-redis" },
|
||||||
{ name = "django-storages", extras = ["s3"], specifier = "==1.14.6" },
|
{ name = "django-storages", extras = ["s3"] },
|
||||||
{ name = "django-tenants", git = "https://github.com/rissson/django-tenants.git?branch=authentik-fixes" },
|
{ name = "django-tenants", git = "https://github.com/rissson/django-tenants.git?branch=authentik-fixes" },
|
||||||
{ name = "djangorestframework", git = "https://github.com/authentik-community/django-rest-framework?rev=896722bab969fabc74a08b827da59409cf9f1a4e" },
|
{ name = "djangorestframework", git = "https://github.com/authentik-community/django-rest-framework?rev=896722bab969fabc74a08b827da59409cf9f1a4e" },
|
||||||
{ name = "djangorestframework-guardian", specifier = "==0.3.0" },
|
{ name = "djangorestframework-guardian" },
|
||||||
{ name = "docker", specifier = "==7.1.0" },
|
{ name = "docker" },
|
||||||
{ name = "drf-orjson-renderer", specifier = "==1.7.3" },
|
{ name = "drf-orjson-renderer" },
|
||||||
{ name = "drf-spectacular", specifier = "==0.28.0" },
|
{ name = "drf-spectacular" },
|
||||||
{ name = "dumb-init", specifier = "==1.2.5.post1" },
|
{ name = "dumb-init" },
|
||||||
{ name = "duo-client", specifier = "==5.5.0" },
|
{ name = "duo-client" },
|
||||||
{ name = "fido2", specifier = "==1.2.0" },
|
{ name = "fido2" },
|
||||||
{ name = "flower", specifier = "==2.0.1" },
|
{ name = "flower" },
|
||||||
{ name = "geoip2", specifier = "==5.1.0" },
|
{ name = "geoip2" },
|
||||||
{ name = "geopy", specifier = "==2.4.1" },
|
{ name = "geopy" },
|
||||||
{ name = "google-api-python-client", specifier = "==2.169.0" },
|
{ name = "google-api-python-client" },
|
||||||
{ name = "gssapi", specifier = "==1.9.0" },
|
{ name = "gssapi" },
|
||||||
{ name = "gunicorn", specifier = "==23.0.0" },
|
{ name = "gunicorn" },
|
||||||
{ name = "jsonpatch", specifier = "==1.33" },
|
{ name = "jsonpatch" },
|
||||||
{ name = "jwcrypto", specifier = "==1.5.6" },
|
{ name = "jwcrypto" },
|
||||||
{ name = "kubernetes", specifier = "==32.0.1" },
|
{ name = "kubernetes" },
|
||||||
{ name = "ldap3", specifier = "==2.9.1" },
|
{ name = "ldap3" },
|
||||||
{ name = "lxml", specifier = "==5.4.0" },
|
{ name = "lxml" },
|
||||||
{ name = "msgraph-sdk", specifier = "==1.30.0" },
|
{ name = "msgraph-sdk" },
|
||||||
{ name = "opencontainers", git = "https://github.com/BeryJu/oci-python?rev=c791b19056769cd67957322806809ab70f5bead8" },
|
{ name = "opencontainers", git = "https://github.com/BeryJu/oci-python?rev=c791b19056769cd67957322806809ab70f5bead8" },
|
||||||
{ name = "packaging", specifier = "==25.0" },
|
{ name = "packaging" },
|
||||||
{ name = "paramiko", specifier = "==3.5.1" },
|
{ name = "paramiko" },
|
||||||
{ name = "psycopg", extras = ["c", "pool"], specifier = "==3.2.9" },
|
{ name = "psycopg", extras = ["c", "pool"] },
|
||||||
{ name = "pydantic", specifier = "==2.11.4" },
|
{ name = "pydantic" },
|
||||||
{ name = "pydantic-scim", specifier = "==0.0.8" },
|
{ name = "pydantic-scim" },
|
||||||
{ name = "pyjwt", specifier = "==2.10.1" },
|
{ name = "pyjwt" },
|
||||||
{ name = "pyrad", specifier = "==2.4" },
|
{ name = "pyrad" },
|
||||||
{ name = "python-kadmin-rs", specifier = "==0.6.0" },
|
{ name = "python-kadmin-rs" },
|
||||||
{ name = "pyyaml", specifier = "==6.0.2" },
|
{ name = "pyyaml" },
|
||||||
{ name = "requests-oauthlib", specifier = "==2.0.0" },
|
{ name = "requests-oauthlib" },
|
||||||
{ name = "scim2-filter-parser", specifier = "==0.7.0" },
|
{ name = "scim2-filter-parser" },
|
||||||
{ name = "sentry-sdk", specifier = "==2.28.0" },
|
{ name = "sentry-sdk" },
|
||||||
{ name = "service-identity", specifier = "==24.2.0" },
|
{ name = "service-identity" },
|
||||||
{ name = "setproctitle", specifier = "==1.3.6" },
|
{ name = "setproctitle" },
|
||||||
{ name = "structlog", specifier = "==25.3.0" },
|
{ name = "structlog" },
|
||||||
{ name = "swagger-spec-validator", specifier = "==3.0.4" },
|
{ name = "swagger-spec-validator" },
|
||||||
{ name = "tenant-schemas-celery", specifier = "==4.0.1" },
|
{ name = "tenant-schemas-celery" },
|
||||||
{ name = "twilio", specifier = "==9.6.1" },
|
{ name = "twilio" },
|
||||||
{ name = "ua-parser", specifier = "==1.0.1" },
|
{ name = "ua-parser" },
|
||||||
{ name = "unidecode", specifier = "==1.4.0" },
|
{ name = "unidecode" },
|
||||||
{ name = "urllib3", specifier = "<3" },
|
{ name = "urllib3", specifier = "<3" },
|
||||||
{ name = "uvicorn", extras = ["standard"], specifier = "==0.34.2" },
|
{ name = "uvicorn", extras = ["standard"] },
|
||||||
{ name = "watchdog", specifier = "==6.0.0" },
|
{ name = "watchdog" },
|
||||||
{ name = "webauthn", specifier = "==2.5.2" },
|
{ name = "webauthn" },
|
||||||
{ name = "wsproto", specifier = "==1.2.0" },
|
{ name = "wsproto" },
|
||||||
{ name = "xmlsec", specifier = "==1.3.15" },
|
{ name = "xmlsec" },
|
||||||
{ name = "zxcvbn", specifier = "==4.5.0" },
|
{ name = "zxcvbn" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata.requires-dev]
|
[package.metadata.requires-dev]
|
||||||
dev = [
|
dev = [
|
||||||
{ name = "aws-cdk-lib", specifier = "==2.188.0" },
|
{ name = "aws-cdk-lib" },
|
||||||
{ name = "bandit", specifier = "==1.8.3" },
|
{ name = "bandit" },
|
||||||
{ name = "black", specifier = "==25.1.0" },
|
{ name = "black" },
|
||||||
{ name = "bump2version", specifier = "==1.0.1" },
|
{ name = "bump2version" },
|
||||||
{ name = "channels", extras = ["daphne"], specifier = "==4.2.2" },
|
{ name = "channels", extras = ["daphne"] },
|
||||||
{ name = "codespell", specifier = "==2.4.1" },
|
{ name = "codespell" },
|
||||||
{ name = "colorama", specifier = "==0.4.6" },
|
{ name = "colorama" },
|
||||||
{ name = "constructs", specifier = "==10.4.2" },
|
{ name = "constructs" },
|
||||||
{ name = "coverage", extras = ["toml"], specifier = "==7.8.0" },
|
{ name = "coverage", extras = ["toml"] },
|
||||||
{ name = "debugpy", specifier = "==1.8.14" },
|
{ name = "debugpy" },
|
||||||
{ name = "drf-jsonschema-serializer", specifier = "==3.0.0" },
|
{ name = "drf-jsonschema-serializer" },
|
||||||
{ name = "freezegun", specifier = "==1.5.1" },
|
{ name = "freezegun" },
|
||||||
{ name = "importlib-metadata", specifier = "==8.6.1" },
|
{ name = "importlib-metadata" },
|
||||||
{ name = "k5test", specifier = "==0.10.4" },
|
{ name = "k5test" },
|
||||||
{ name = "pdoc", specifier = "==15.0.3" },
|
{ name = "pdoc" },
|
||||||
{ name = "pytest", specifier = "==8.3.5" },
|
{ name = "pytest" },
|
||||||
{ name = "pytest-django", specifier = "==4.11.1" },
|
{ name = "pytest-django" },
|
||||||
{ name = "pytest-github-actions-annotate-failures", specifier = "==0.3.0" },
|
{ name = "pytest-github-actions-annotate-failures" },
|
||||||
{ name = "pytest-randomly", specifier = "==3.16.0" },
|
{ name = "pytest-randomly" },
|
||||||
{ name = "pytest-timeout", specifier = "==2.4.0" },
|
{ name = "pytest-timeout" },
|
||||||
{ name = "requests-mock", specifier = "==1.12.1" },
|
{ name = "requests-mock" },
|
||||||
{ name = "ruff", specifier = "==0.11.9" },
|
{ name = "ruff" },
|
||||||
{ name = "selenium", specifier = "==4.32.0" },
|
{ name = "selenium" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -387,16 +387,16 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aws-cdk-asset-awscli-v1"
|
name = "aws-cdk-asset-awscli-v1"
|
||||||
version = "2.2.235"
|
version = "2.2.231"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "jsii" },
|
{ name = "jsii" },
|
||||||
{ name = "publication" },
|
{ name = "publication" },
|
||||||
{ name = "typeguard" },
|
{ name = "typeguard" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/20/e8/6706ee98e9ba436aa07ca3a65d79cf40c50005f4f760f139bec0f6c3606a/aws_cdk_asset_awscli_v1-2.2.235.tar.gz", hash = "sha256:0a2023f9d32158ae86d43dfeac2ba7679e8a050cb99b7565b26192e60e57a91c", size = 19130124, upload-time = "2025-05-05T15:24:02.938Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/01/b2/4a142d1d8093691c1b54b7b35f463f6defa1d0a8a08b7be2277eae73c726/aws_cdk_asset_awscli_v1-2.2.231.tar.gz", hash = "sha256:859d99e0fcdc2f6ada44090ad9f921743da3ca3a6d9f39ab06836d4c8e0fbc23", size = 17960944, upload-time = "2025-04-07T16:48:17.423Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/97/27/b167173d7fb784848563d596085dc8e95cabbe7b01f8a5c0ac1ed6a80c36/aws_cdk_asset_awscli_v1-2.2.235-py3-none-any.whl", hash = "sha256:701a47a97419b917ce73cf9c922a26c2895943b4b18b191e1285572b8584ae1e", size = 19128489, upload-time = "2025-05-05T15:23:59.87Z" },
|
{ url = "https://files.pythonhosted.org/packages/68/2d/dae06874ab3a66ad898d9c2d792c863b8b8249b203a1d8e3b36dfca44a93/aws_cdk_asset_awscli_v1-2.2.231-py3-none-any.whl", hash = "sha256:06d6b1d9e52272c315b944320f7039b47c6a6058f063fa33ab0ec06fea17bfbe", size = 17959325, upload-time = "2025-04-07T16:48:14.477Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -461,7 +461,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "azure-identity"
|
name = "azure-identity"
|
||||||
version = "1.22.0"
|
version = "1.21.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "azure-core" },
|
{ name = "azure-core" },
|
||||||
@ -470,9 +470,9 @@ dependencies = [
|
|||||||
{ name = "msal-extensions" },
|
{ name = "msal-extensions" },
|
||||||
{ name = "typing-extensions" },
|
{ name = "typing-extensions" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/58/8e/1b5916f5e1696bf05b009cf7d41383cea54aa8536d4a4f6f88cca15eb6a4/azure_identity-1.22.0.tar.gz", hash = "sha256:c8f5ef23e5295c2fa300c984dd9f5e1fe43503fc25c121c37ff6a15e39b800b9", size = 263346, upload-time = "2025-05-06T20:22:24.13Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/b5/a1/f1a683672e7a88ea0e3119f57b6c7843ed52650fdcac8bfa66ed84e86e40/azure_identity-1.21.0.tar.gz", hash = "sha256:ea22ce6e6b0f429bc1b8d9212d5b9f9877bd4c82f1724bfa910760612c07a9a6", size = 266445, upload-time = "2025-03-11T20:53:07.463Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/06/1a/6f13d7f95f68f37303c0e00e011d498e4524e70d354b2e11ef5ae89e0ce0/azure_identity-1.22.0-py3-none-any.whl", hash = "sha256:26d6c63f2ca453c77c3e74be8613941ad074e05d0c8be135247573752c249ad8", size = 185524, upload-time = "2025-05-06T20:22:25.991Z" },
|
{ url = "https://files.pythonhosted.org/packages/3d/9f/1f9f3ef4f49729ee207a712a5971a9ca747f2ca47d9cbf13cf6953e3478a/azure_identity-1.21.0-py3-none-any.whl", hash = "sha256:258ea6325537352440f71b35c3dffe9d240eae4a5126c1b7ce5efd5766bd9fd9", size = 189190, upload-time = "2025-03-11T20:53:09.197Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -571,30 +571,30 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "boto3"
|
name = "boto3"
|
||||||
version = "1.38.13"
|
version = "1.38.8"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "botocore" },
|
{ name = "botocore" },
|
||||||
{ name = "jmespath" },
|
{ name = "jmespath" },
|
||||||
{ name = "s3transfer" },
|
{ name = "s3transfer" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/c7/89/a47f62b3f81a2e3484d2a2b8dd4906c5b6e57da0af0bd59d36f99ba20baf/boto3-1.38.13.tar.gz", hash = "sha256:6633bce2b73284acce1453ca85834c7c5a59e0dbcce1170be461cc079bdcdfcf", size = 111812, upload-time = "2025-05-09T19:33:02.962Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/b1/8e/bf339382eaff15b3575d23b2b6f06769765001234c2ccaafa50a20931379/boto3-1.38.8.tar.gz", hash = "sha256:6bbc75bb51be9c5a33d07a4adf13d133c60f77b7c47bef1c46fda90b1297a867", size = 111798, upload-time = "2025-05-03T00:18:45.139Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/72/25/79e219648f10d060d152542fcf3be0093120471774b99c1a7f41ceaeca9b/boto3-1.38.13-py3-none-any.whl", hash = "sha256:668400d13889d2d2fcd66ce785cc0b0fc040681f58a9c7f67daa9149a52b6c63", size = 139934, upload-time = "2025-05-09T19:33:00.855Z" },
|
{ url = "https://files.pythonhosted.org/packages/84/d9/1bd6c2a6c3d3bf1d8b0be52c39230bd1e14bb55b7ecc04f42fcb68b27343/boto3-1.38.8-py3-none-any.whl", hash = "sha256:f3a4d79f499f567d327d2d8846d02ad18244d2927f88858a42a2438f52d9a0ef", size = 139899, upload-time = "2025-05-03T00:18:42.787Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "botocore"
|
name = "botocore"
|
||||||
version = "1.38.13"
|
version = "1.38.8"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "jmespath" },
|
{ name = "jmespath" },
|
||||||
{ name = "python-dateutil" },
|
{ name = "python-dateutil" },
|
||||||
{ name = "urllib3" },
|
{ name = "urllib3" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/de/36/5b0faba074684744244e1e030e73fd5612bc2c38f557eec0a7f1a3d7ddd2/botocore-1.38.13.tar.gz", hash = "sha256:22feee15753cd3f9f7179d041604078a1024701497d27b22be7c6707e8d13ccb", size = 13882010, upload-time = "2025-05-09T19:32:51.172Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/1e/18/1ec9220e180106d8055365a9bb4926db9840211c65f5fd70a5a90b0873cf/botocore-1.38.8.tar.gz", hash = "sha256:68d739300cc94232373517b27c5570de6ae6d809a2db644f30219f5c8e0371ce", size = 13871026, upload-time = "2025-05-03T00:18:32.427Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/94/df/a7a8097471d5a3bc7d408850222292d874ffc190aef7e1cacf9af770339e/botocore-1.38.13-py3-none-any.whl", hash = "sha256:de29fee43a1f02787fb5b3756ec09917d5661ed95b2b2d64797ab04196f69e14", size = 13544507, upload-time = "2025-05-09T19:32:37.727Z" },
|
{ url = "https://files.pythonhosted.org/packages/b4/66/e5a314d1e868cd35ec5c5d11360387c2a85e8d408f084616337f1a282c61/botocore-1.38.8-py3-none-any.whl", hash = "sha256:f6ae08a56fe94e18d2aa223611a3b5e94123315d0cb3cb85764b029b2326c710", size = 13531917, upload-time = "2025-05-03T00:18:26.389Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -750,14 +750,14 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "click"
|
name = "click"
|
||||||
version = "8.2.0"
|
version = "8.1.8"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/cd/0f/62ca20172d4f87d93cf89665fbaedcd560ac48b465bd1d92bfc7ea6b0a41/click-8.2.0.tar.gz", hash = "sha256:f5452aeddd9988eefa20f90f05ab66f17fce1ee2a36907fd30b05bbb5953814d", size = 235857, upload-time = "2025-05-10T22:21:03.111Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/a2/58/1f37bf81e3c689cc74ffa42102fa8915b59085f54a6e4a80bc6265c0f6bf/click-8.2.0-py3-none-any.whl", hash = "sha256:6b303f0b2aa85f1cb4e5303078fadcbcd4e476f114fab9b5007005711839325c", size = 102156, upload-time = "2025-05-10T22:21:01.352Z" },
|
{ url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -979,16 +979,16 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django"
|
name = "django"
|
||||||
version = "5.1.9"
|
version = "5.1.8"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "asgiref" },
|
{ name = "asgiref" },
|
||||||
{ name = "sqlparse" },
|
{ name = "sqlparse" },
|
||||||
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/10/08/2e6f05494b3fc0a3c53736846034f882b82ee6351791a7815bbb45715d79/django-5.1.9.tar.gz", hash = "sha256:565881bdd0eb67da36442e9ac788bda90275386b549070d70aee86327781a4fc", size = 10710887, upload-time = "2025-05-07T14:06:45.257Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/00/40/45adc1b93435d1b418654a734b68351bb6ce0a0e5e37b2f0e9aeb1a2e233/Django-5.1.8.tar.gz", hash = "sha256:42e92a1dd2810072bcc40a39a212b693f94406d0ba0749e68eb642f31dc770b4", size = 10723602, upload-time = "2025-04-02T11:19:56.028Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/e1/d1/d8b6b8250b84380d5a123e099ad3298a49407d81598faa13b43a2c6d96d7/django-5.1.9-py3-none-any.whl", hash = "sha256:2fd1d4a0a66a5ba702699eb692e75b0d828b73cc2f4e1fc4b6a854a918967411", size = 8277363, upload-time = "2025-05-07T14:06:37.426Z" },
|
{ url = "https://files.pythonhosted.org/packages/ec/0d/e6dd0ed898b920fec35c6eeeb9acbeb831fff19ad21c5e684744df1d4a36/Django-5.1.8-py3-none-any.whl", hash = "sha256:11b28fa4b00e59d0def004e9ee012fefbb1065a5beb39ee838983fd24493ad4f", size = 8277130, upload-time = "2025-04-02T11:19:51.591Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1354,16 +1354,16 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "geoip2"
|
name = "geoip2"
|
||||||
version = "5.1.0"
|
version = "5.0.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "aiohttp" },
|
{ name = "aiohttp" },
|
||||||
{ name = "maxminddb" },
|
{ name = "maxminddb" },
|
||||||
{ name = "requests" },
|
{ name = "requests" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/0f/5f/902835f485d1c423aca9097a0e91925d6a706049f64e678ec781b168734d/geoip2-5.1.0.tar.gz", hash = "sha256:ee3f87f0ce9325eb6484fe18cbd9771a03d0a2bad1dd156fa3584fafa562d39a", size = 268166, upload-time = "2025-05-05T19:40:29.202Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/17/d7/21cfa1072b8ec5937c6af0cf8b624b4be9b44a7ca82f4335900df5482076/geoip2-5.0.1.tar.gz", hash = "sha256:90af8b6d3687f3bef251f2708ad017b30d627d1144c0040eabc4c9017a807d86", size = 175854, upload-time = "2025-01-28T23:22:04.689Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/eb/43/aa9a10d0c971d0a0e353111a97913357f9271fb9a9867ec1053f79ca61be/geoip2-5.1.0-py3-none-any.whl", hash = "sha256:445a058995ad5bb3e665ae716413298d4383b1fb38d372ad59b9b405f6b0ca19", size = 27691, upload-time = "2025-05-05T19:40:26.082Z" },
|
{ url = "https://files.pythonhosted.org/packages/0f/6c/4f17beb65444cd0b8e602c2ea64ef1ec8ff32e1aa8c6a3be3fad7113b947/geoip2-5.0.1-py3-none-any.whl", hash = "sha256:128b7c9e6b55fb66178428a9400bd4f8b011cf64f147b1ed9e3a4766e61a9b78", size = 28064, upload-time = "2025-01-28T23:22:01.662Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1412,16 +1412,16 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "google-auth"
|
name = "google-auth"
|
||||||
version = "2.40.1"
|
version = "2.39.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "cachetools" },
|
{ name = "cachetools" },
|
||||||
{ name = "pyasn1-modules" },
|
{ name = "pyasn1-modules" },
|
||||||
{ name = "rsa" },
|
{ name = "rsa" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/94/a5/38c21d0e731bb716cffcf987bd9a3555cb95877ab4b616cfb96939933f20/google_auth-2.40.1.tar.gz", hash = "sha256:58f0e8416a9814c1d86c9b7f6acf6816b51aba167b2c76821965271bac275540", size = 280975, upload-time = "2025-05-07T01:04:55.3Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/cb/8e/8f45c9a32f73e786e954b8f9761c61422955d23c45d1e8c347f9b4b59e8e/google_auth-2.39.0.tar.gz", hash = "sha256:73222d43cdc35a3aeacbfdcaf73142a97839f10de930550d89ebfe1d0a00cde7", size = 274834, upload-time = "2025-04-14T17:44:49.402Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/a1/b1/1272c6e80847ba5349f5ccb7574596393d1e222543f5003cb810865c3575/google_auth-2.40.1-py2.py3-none-any.whl", hash = "sha256:ed4cae4f5c46b41bae1d19c036e06f6c371926e97b19e816fc854eff811974ee", size = 216101, upload-time = "2025-05-07T01:04:53.612Z" },
|
{ url = "https://files.pythonhosted.org/packages/ce/12/ad37a1ef86006d0a0117fc06a4a00bd461c775356b534b425f00dde208ea/google_auth-2.39.0-py2.py3-none-any.whl", hash = "sha256:0150b6711e97fb9f52fe599f55648950cc4540015565d8fbb31be2ad6e1548a2", size = 212319, upload-time = "2025-04-14T17:44:47.699Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1680,7 +1680,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jsii"
|
name = "jsii"
|
||||||
version = "1.112.0"
|
version = "1.111.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "attrs" },
|
{ name = "attrs" },
|
||||||
@ -1691,9 +1691,9 @@ dependencies = [
|
|||||||
{ name = "typeguard" },
|
{ name = "typeguard" },
|
||||||
{ name = "typing-extensions" },
|
{ name = "typing-extensions" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/ad/3e/270b5236035fc7bb2cdd7f55ea25f85489d35d971870cbec32c3d9e99d7f/jsii-1.112.0.tar.gz", hash = "sha256:6b7d19f361c2565b76828ecbe8cbed8b8d6028a82aa98a46b206a4ee5083157e", size = 624533, upload-time = "2025-05-07T14:45:52.574Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/29/e0/f1edbe7adb75c58ef13f4b24a8f2521cc3f7f5bd79751d071900065cf0a7/jsii-1.111.0.tar.gz", hash = "sha256:db523ab9b6575c84d6ed8779cdbdc739abd48a7cb0723b66beb84c1a9dc31c7c", size = 624365, upload-time = "2025-04-02T16:35:49.643Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/44/af/8554b632e2b82f37a7422782aba5db2a1fbff4887faa7ec850818def8407/jsii-1.112.0-py3-none-any.whl", hash = "sha256:6510c223074d9b206fd0570849a791e4d9ecfff7ffe68428de73870cea9f55a1", size = 600681, upload-time = "2025-05-07T14:45:51.136Z" },
|
{ url = "https://files.pythonhosted.org/packages/3c/8a/d3a80a0b0ecb2c175eacbe48542695213ef4315b2e6bd62bafd244c06ae0/jsii-1.111.0-py3-none-any.whl", hash = "sha256:3084e31173e73d2eefee678c8ee31aa49428830509043057a421a4c0dde94434", size = 600503, upload-time = "2025-04-02T16:35:48.153Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1881,24 +1881,20 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "maxminddb"
|
name = "maxminddb"
|
||||||
version = "2.7.0"
|
version = "2.6.3"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/d1/10/7a7cf5219b74b19ea1834b43256e114564e8a845f447446ac821e1b9951e/maxminddb-2.7.0.tar.gz", hash = "sha256:23a715ed3b3aed07adae4beeed06c51fd582137b5ae13d3c6e5ca4890f70ebbf", size = 196583, upload-time = "2025-05-05T19:31:43.957Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/57/ae/422ec0f3b6a40f23de9477c42fce90126a3994dd51d06b50582973c0088e/maxminddb-2.6.3.tar.gz", hash = "sha256:d2c3806baa7aa047aa1bac7419e7e353db435f88f09d51106a84dbacf645d254", size = 181376, upload-time = "2025-01-09T16:12:13.7Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/8f/0e/68a558e11e8a2aaeb1b28be27c784052dcccd780175fa9e3d2693274e8d6/maxminddb-2.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2328575e2d2ab6179acf93c09745e9af10eb92aaa305cb5bd0f7c307d0dd398e", size = 35328, upload-time = "2025-05-05T19:30:35.134Z" },
|
{ url = "https://files.pythonhosted.org/packages/cc/e3/a3218d7cd35c930a08f7d7301334f9c85aa0a28dbac3f50e3d43f3d70734/maxminddb-2.6.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9580b2cd017185db07baacd9d629ca01f3fe6f236528681c88a0209725376e9c", size = 35235, upload-time = "2025-01-09T16:10:57.614Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/75/ff/2c98dda0d0aaa09dbe9a4030bd9ab056e0bf6c6559215e34185e2fd62d50/maxminddb-2.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c4f71a72f3dbdc2abd58c36ad0ad4bd936781354feee8538614d2170223675f0", size = 35098, upload-time = "2025-05-05T19:30:36.259Z" },
|
{ url = "https://files.pythonhosted.org/packages/85/40/11f23d1c1f6654618d87e995f56a789f00c1c07d5c986f9b14d81f04f90c/maxminddb-2.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:47828bed767b82c219ba7aa65f0cb03d7f7443d7270259ce931e133a40691d34", size = 35021, upload-time = "2025-01-09T16:10:58.692Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/9a/cb/99d1650daa24a9acb55c81412fcefa5d95b7e80a872876a902e14f33ec4d/maxminddb-2.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:566e7ea8296ad126a24df52e6c37382dc9660c414ceea4c4c687bbca2d522c28", size = 90193, upload-time = "2025-05-05T19:30:37.325Z" },
|
{ url = "https://files.pythonhosted.org/packages/68/7e/883adcb107fb45916328ecb40f980cc598dbcc7dfd2ccc871851c40836d6/maxminddb-2.6.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77112cb1a2e381de42c443d1bf222c58b9da203183bb2008dd370c3d2a587a4e", size = 90068, upload-time = "2025-01-09T16:10:59.874Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/c0/15/53ceb43e1e1e7493a66fb9a3b2d3248198316d2dbe746c585591276f1aad/maxminddb-2.7.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c10d94df0d5ea22873a5dc1af24d8972de0a22841dbd90a7e450f66a6f11ed21", size = 94695, upload-time = "2025-05-05T19:30:38.528Z" },
|
{ url = "https://files.pythonhosted.org/packages/2c/87/b57cf9ef4cf8b076f3b25df949b57c7b3ee0f4543f1f76f445afd313b96b/maxminddb-2.6.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:448d062e95242e3088df85fe7ed3f2890a9f4aea924bde336e9ff5d2337ca5fd", size = 89506, upload-time = "2025-01-09T16:11:01.057Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/2c/d7/e26d168d85e2503232d5df2a847641024afd11405fd5132816728cc9e399/maxminddb-2.7.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7f0e9a4db3c986f208dd4359e9d9e776e28ce8aae540da6f1a733fae3bb67ac", size = 91306, upload-time = "2025-05-05T19:30:40.078Z" },
|
{ url = "https://files.pythonhosted.org/packages/ff/f8/cf746032f267ee25bd32f70d71a63e857fec91e19a0907db885bdbb7b0c1/maxminddb-2.6.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a59d72bf373c61da156fd43e2be6da802f68370a50a2205de84ee76916e05f9f", size = 87612, upload-time = "2025-01-09T16:11:02.362Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/b5/b5/4bb9330ee29efb0c515cb8c6c500f367021c163ebb81380789e6ac846f8b/maxminddb-2.7.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef41949246035af8cb5970bee2e94bbc894203312fd6fb55cbd4fe30c6e44374", size = 89620, upload-time = "2025-05-05T19:30:41.279Z" },
|
{ url = "https://files.pythonhosted.org/packages/c0/9e/ff5c93e8e589c1544cad2a457c1b7e4169a256c8655928266a9de6f21cac/maxminddb-2.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e867852037a8a26a24cfcf31b697dce63d488e1617af244c2895568d8f6c7a31", size = 92310, upload-time = "2025-01-09T16:11:03.619Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/72/e5/1335e42615b57fd821a6c606119cb4babd85bc88839f8dbae0b5bb082d04/maxminddb-2.7.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:531be1066697b57928bce2ac9cb7e705b8cebdfa2e42dfbebc92b75fc53ad22f", size = 87751, upload-time = "2025-05-05T19:30:42.464Z" },
|
{ url = "https://files.pythonhosted.org/packages/99/44/56ed56377ba8c99f7eb3101479c063d46f18e5f0a9070432d74a2ed15f82/maxminddb-2.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5a1586260eac831d61c2665b26ca1ae3ad00caca57c8031346767f4527025311", size = 91227, upload-time = "2025-01-09T16:11:04.943Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/b2/9a/af47d3f7a15a49be61315f29bb0e232c1f5040f3afc685509cee1ebdaef7/maxminddb-2.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:265e938c12628fceb71665e28bfca206ee9d8ae6ac18282cbfc544753ccc8b9b", size = 93328, upload-time = "2025-05-05T19:30:43.697Z" },
|
{ url = "https://files.pythonhosted.org/packages/9b/58/cdb1a7c18a1946ad006657b52cb499e489d2b28a62490fd5aee14b356a55/maxminddb-2.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6eb23f842a72ab3096f9f9b1c292f4feb55a8d758567cb6d77637c2257a3187c", size = 92126, upload-time = "2025-01-09T16:11:06.149Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/2f/43/ecda2cc5a7ffae034692374401f97c3ef8fe15f22826ae2784b38ecf0cfd/maxminddb-2.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7b101cf6b79db4c046c9c9b157bb9730308074749c442f50d52a7a0e5d765357", size = 91701, upload-time = "2025-05-05T19:30:44.83Z" },
|
{ url = "https://files.pythonhosted.org/packages/39/94/4b37ffa77f8921a549805a62ce62f6fa453ea3c59c0dfcd584770fc59a8c/maxminddb-2.6.3-cp313-cp313-win32.whl", hash = "sha256:acf46e20709a27d2b519669888e3f53a37bc4204b98a0c690664c48ff8cb1364", size = 34751, upload-time = "2025-01-09T16:11:09.017Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/3c/14/8edd17bdeaddd9d7d138008d6fc14baacdda418a5346d09215d14870dfd2/maxminddb-2.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:faecf825f812d54e1cb053e75358656b280af1ea4b6f53b3f1a98c3f9fa41a46", size = 98466, upload-time = "2025-05-05T19:30:46.041Z" },
|
{ url = "https://files.pythonhosted.org/packages/1e/af/638811134e1a33cf75c2d2be1b0b9b90dd1f43216a4ef1f24e223f646b46/maxminddb-2.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:3015afb00e6168837938dbe5fda40ace37442c22b292ccee27c1690fbf6078ed", size = 36790, upload-time = "2025-01-09T16:11:10.093Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/d4/6f/cefabf7868b5406f4df04c3f4cb95dc802c1ad1b05f26046a4584a235268/maxminddb-2.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:dd266b3060bb6b6b05009b04ca93787fab0a00f16638827d34bab50cfdf68dd4", size = 96255, upload-time = "2025-05-05T19:30:47.785Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/6f/4a/f604c942dc9d3e755601831ee101198d3090c0dfa5485e2aaf3245075bf3/maxminddb-2.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9f30bdd4c618c372c0f4f981f2241aad8e3ab9c361bb1d299f213e9a4c2a3fd8", size = 92190, upload-time = "2025-05-05T19:30:49.052Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/ba/6f/563058bc28704b24fa04830acc40abca03debcaa2a12195d8269b490475b/maxminddb-2.7.0-cp313-cp313-win32.whl", hash = "sha256:023f23654b38345965cab3e33465a4b82edb2250ba7c6db5c175a872645c35c5", size = 34699, upload-time = "2025-05-05T19:30:50.202Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/e8/84/33e0389d97ca9bc7e902c1f4a74e626349043c942ba0b6458fa96cbea0a8/maxminddb-2.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:f81d678ab25d4867f95fb44cce3c67f6157d25dc8846191fd4eb0e38f49a263f", size = 36723, upload-time = "2025-05-05T19:30:51.799Z" },
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2065,7 +2061,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "msgraph-sdk"
|
name = "msgraph-sdk"
|
||||||
version = "1.30.0"
|
version = "1.28.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "azure-identity" },
|
{ name = "azure-identity" },
|
||||||
@ -2075,9 +2071,9 @@ dependencies = [
|
|||||||
{ name = "microsoft-kiota-serialization-text" },
|
{ name = "microsoft-kiota-serialization-text" },
|
||||||
{ name = "msgraph-core" },
|
{ name = "msgraph-core" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/e9/4a/4ff19671f6ea06f98fb2405f73a90350e4719ccc692e85e9e0c2fa066826/msgraph_sdk-1.30.0.tar.gz", hash = "sha256:59e30af6d7244c9009146d620c331e169701b651317746b16f561e2e2452e73f", size = 6608744, upload-time = "2025-05-13T13:09:12.594Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/b9/41/40bb3c630ca026182aefd79a9862ef4a1917b1161c83690c858d714788f5/msgraph_sdk-1.28.0.tar.gz", hash = "sha256:b2d64b7bd711ad285fc2c090dd524853a026848732e1c83874fe34561805350d", size = 6121069, upload-time = "2025-04-15T11:39:08.184Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/70/95/451ec4db8a924274a1f7260809ea03fe9c2b446d84dc5238e92e49a1b522/msgraph_sdk-1.30.0-py3-none-any.whl", hash = "sha256:6748f5cdb5ddbcff9e4f3fb073dd0a604cb00e1cf285dd0fea6969c93ba8282f", size = 27140767, upload-time = "2025-05-13T13:09:07.718Z" },
|
{ url = "https://files.pythonhosted.org/packages/a8/58/d8e9593ea81779d503831b5b06c8d9881d5affefe3df99ca20112c969e6f/msgraph_sdk-1.28.0-py3-none-any.whl", hash = "sha256:bd33b186371dfa8ed6375dfda92eef0931485633e69b06c001ce3c2fd3658f18", size = 25091309, upload-time = "2025-04-15T11:39:04.968Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2157,42 +2153,42 @@ source = { git = "https://github.com/BeryJu/oci-python?rev=c791b19056769cd679573
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opentelemetry-api"
|
name = "opentelemetry-api"
|
||||||
version = "1.33.0"
|
version = "1.32.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "deprecated" },
|
{ name = "deprecated" },
|
||||||
{ name = "importlib-metadata" },
|
{ name = "importlib-metadata" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/70/ca/920a73b4a11cd271ba1c62f34dba27d7783996a6a7ac0bac7c83b230736d/opentelemetry_api-1.33.0.tar.gz", hash = "sha256:cc4380fd2e6da7dcb52a828ea81844ed1f4f2eb638ca3c816775109d93d58ced", size = 65000, upload-time = "2025-05-09T14:56:00.967Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/42/40/2359245cd33641c2736a0136a50813352d72f3fc209de28fb226950db4a1/opentelemetry_api-1.32.1.tar.gz", hash = "sha256:a5be71591694a4d9195caf6776b055aa702e964d961051a0715d05f8632c32fb", size = 64138, upload-time = "2025-04-15T16:02:13.97Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/e6/c4/26c7ec8e51c19632f42503dbabed286c261fb06f8f61ffd348690e36958a/opentelemetry_api-1.33.0-py3-none-any.whl", hash = "sha256:158df154f628e6615b65fdf6e59f99afabea7213e72c5809dd4adf06c0d997cd", size = 65772, upload-time = "2025-05-09T14:55:38.395Z" },
|
{ url = "https://files.pythonhosted.org/packages/12/f2/89ea3361a305466bc6460a532188830351220b5f0851a5fa133155c16eca/opentelemetry_api-1.32.1-py3-none-any.whl", hash = "sha256:bbd19f14ab9f15f0e85e43e6a958aa4cb1f36870ee62b7fd205783a112012724", size = 65287, upload-time = "2025-04-15T16:01:49.747Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opentelemetry-sdk"
|
name = "opentelemetry-sdk"
|
||||||
version = "1.33.0"
|
version = "1.32.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "opentelemetry-api" },
|
{ name = "opentelemetry-api" },
|
||||||
{ name = "opentelemetry-semantic-conventions" },
|
{ name = "opentelemetry-semantic-conventions" },
|
||||||
{ name = "typing-extensions" },
|
{ name = "typing-extensions" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/37/0a/b7ae406175a2798a767e12db223e842911d9c398eea100c41c989afd2aa8/opentelemetry_sdk-1.33.0.tar.gz", hash = "sha256:a7fc56d1e07b218fcc316b24d21b59d3f1967b2ca22c217b05da3a26b797cc68", size = 161381, upload-time = "2025-05-09T14:56:12.347Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/a3/65/2069caef9257fae234ca0040d945c741aa7afbd83a7298ee70fc0bc6b6f4/opentelemetry_sdk-1.32.1.tar.gz", hash = "sha256:8ef373d490961848f525255a42b193430a0637e064dd132fd2a014d94792a092", size = 161044, upload-time = "2025-04-15T16:02:28.905Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/b4/34/831f5d9ae9375c9ba2446cb3cc0be79d8d73b78f813c9567e1615c2624f6/opentelemetry_sdk-1.33.0-py3-none-any.whl", hash = "sha256:bed376b6d37fbf00688bb65edfee817dd01d48b8559212831437529a6066049a", size = 118861, upload-time = "2025-05-09T14:55:56.956Z" },
|
{ url = "https://files.pythonhosted.org/packages/dc/00/d3976cdcb98027aaf16f1e980e54935eb820872792f0eaedd4fd7abb5964/opentelemetry_sdk-1.32.1-py3-none-any.whl", hash = "sha256:bba37b70a08038613247bc42beee5a81b0ddca422c7d7f1b097b32bf1c7e2f17", size = 118989, upload-time = "2025-04-15T16:02:08.814Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opentelemetry-semantic-conventions"
|
name = "opentelemetry-semantic-conventions"
|
||||||
version = "0.54b0"
|
version = "0.53b1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "deprecated" },
|
{ name = "deprecated" },
|
||||||
{ name = "opentelemetry-api" },
|
{ name = "opentelemetry-api" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/92/8c/bc970d1599ff40b7913c953a95195addf11c81a27cc85d5ed568e9f8c57f/opentelemetry_semantic_conventions-0.54b0.tar.gz", hash = "sha256:467b739977bdcb079af1af69f73632535cdb51099d5e3c5709a35d10fe02a9c9", size = 118646, upload-time = "2025-05-09T14:56:13.596Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/5e/b6/3c56e22e9b51bcb89edab30d54830958f049760bbd9ab0a759cece7bca88/opentelemetry_semantic_conventions-0.53b1.tar.gz", hash = "sha256:4c5a6fede9de61211b2e9fc1e02e8acacce882204cd770177342b6a3be682992", size = 114350, upload-time = "2025-04-15T16:02:29.793Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/c8/aa/f7c46c19aee189e0123ef7209eaafc417e242b2073485dfb40523d6d8612/opentelemetry_semantic_conventions-0.54b0-py3-none-any.whl", hash = "sha256:fad7c1cf8908fd449eb5cf9fbbeefb301acf4bc995101f85277899cec125d823", size = 194937, upload-time = "2025-05-09T14:55:58.562Z" },
|
{ url = "https://files.pythonhosted.org/packages/27/6b/a8fb94760ef8da5ec283e488eb43235eac3ae7514385a51b6accf881e671/opentelemetry_semantic_conventions-0.53b1-py3-none-any.whl", hash = "sha256:21df3ed13f035f8f3ea42d07cbebae37020367a53b47f1ebee3b10a381a00208", size = 188443, upload-time = "2025-04-15T16:02:10.095Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2290,11 +2286,11 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "platformdirs"
|
name = "platformdirs"
|
||||||
version = "4.3.8"
|
version = "4.3.7"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362, upload-time = "2025-05-07T22:47:42.121Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/b6/2d/7d512a3913d60623e7eb945c6d1b4f0bddf1d0b7ada5225274c87e5b53d1/platformdirs-4.3.7.tar.gz", hash = "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351", size = 21291, upload-time = "2025-03-19T20:36:10.989Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" },
|
{ url = "https://files.pythonhosted.org/packages/6d/45/59578566b3275b8fd9157885918fcd0c4d74162928a5310926887b856a51/platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94", size = 18499, upload-time = "2025-03-19T20:36:09.038Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2396,14 +2392,14 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "psycopg"
|
name = "psycopg"
|
||||||
version = "3.2.9"
|
version = "3.2.7"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/27/4a/93a6ab570a8d1a4ad171a1f4256e205ce48d828781312c0bbaff36380ecb/psycopg-3.2.9.tar.gz", hash = "sha256:2fbb46fcd17bc81f993f28c47f1ebea38d66ae97cc2dbc3cad73b37cefbff700", size = 158122, upload-time = "2025-05-13T16:11:15.533Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/fe/16/ca27b38762a630b70243f51eb6a728f903a17cddc4961626fa540577aba6/psycopg-3.2.7.tar.gz", hash = "sha256:9afa609c7ebf139827a38c0bf61be9c024a3ed743f56443de9d38e1efc260bf3", size = 157238, upload-time = "2025-04-30T13:05:22.867Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/44/b0/a73c195a56eb6b92e937a5ca58521a5c3346fb233345adc80fd3e2f542e2/psycopg-3.2.9-py3-none-any.whl", hash = "sha256:01a8dadccdaac2123c916208c96e06631641c0566b22005493f09663c7a8d3b6", size = 202705, upload-time = "2025-05-13T16:06:26.584Z" },
|
{ url = "https://files.pythonhosted.org/packages/cb/eb/6e32d259437125a17b0bc2624e06c86149c618501da1dcbc8539b2684f6f/psycopg-3.2.7-py3-none-any.whl", hash = "sha256:d39747d2d5b9658b69fa462ad21d31f1ba4a5722ad1d0cb952552bc0b4125451", size = 200028, upload-time = "2025-04-30T12:59:32.435Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.optional-dependencies]
|
[package.optional-dependencies]
|
||||||
@ -2416,9 +2412,9 @@ pool = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "psycopg-c"
|
name = "psycopg-c"
|
||||||
version = "3.2.9"
|
version = "3.2.7"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/83/7f/6147cb842081b0b32692bf5a0fdf58e9ac95418ebac1184d4431ec44b85f/psycopg_c-3.2.9.tar.gz", hash = "sha256:8c9f654f20c6c56bddc4543a3caab236741ee94b6732ab7090b95605502210e2", size = 609538, upload-time = "2025-05-13T16:11:19.856Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/b2/13/74e41e5195e6a0a02b9f1e3560bc714021b725e89a40f5879df58d4189c6/psycopg_c-3.2.7.tar.gz", hash = "sha256:14455cf71ed29fdfa725c550f8c58056a852bb27b55eb59e3a0f127ca92751a3", size = 609707, upload-time = "2025-04-30T13:05:24.834Z" }
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "psycopg-pool"
|
name = "psycopg-pool"
|
||||||
@ -2670,14 +2666,14 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pytest-timeout"
|
name = "pytest-timeout"
|
||||||
version = "2.4.0"
|
version = "2.3.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "pytest" },
|
{ name = "pytest" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/ac/82/4c9ecabab13363e72d880f2fb504c5f750433b2b6f16e99f4ec21ada284c/pytest_timeout-2.4.0.tar.gz", hash = "sha256:7e68e90b01f9eff71332b25001f85c75495fc4e3a836701876183c4bcfd0540a", size = 17973, upload-time = "2025-05-05T19:44:34.99Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/93/0d/04719abc7a4bdb3a7a1f968f24b0f5253d698c9cc94975330e9d3145befb/pytest-timeout-2.3.1.tar.gz", hash = "sha256:12397729125c6ecbdaca01035b9e5239d4db97352320af155b3f5de1ba5165d9", size = 17697, upload-time = "2024-03-07T21:04:01.069Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl", hash = "sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2", size = 14382, upload-time = "2025-05-05T19:44:33.502Z" },
|
{ url = "https://files.pythonhosted.org/packages/03/27/14af9ef8321f5edc7527e47def2a21d8118c6f329a9342cc61387a0c0599/pytest_timeout-2.3.1-py3-none-any.whl", hash = "sha256:68188cb703edfc6a18fad98dc25a3c61e9f24d644b0b70f33af545219fc7813e", size = 14148, upload-time = "2024-03-07T21:03:58.764Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2874,27 +2870,27 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruff"
|
name = "ruff"
|
||||||
version = "0.11.9"
|
version = "0.11.8"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/f5/e7/e55dda1c92cdcf34b677ebef17486669800de01e887b7831a1b8fdf5cb08/ruff-0.11.9.tar.gz", hash = "sha256:ebd58d4f67a00afb3a30bf7d383e52d0e036e6195143c6db7019604a05335517", size = 4132134, upload-time = "2025-05-09T16:19:41.511Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/52/f6/adcf73711f31c9f5393862b4281c875a462d9f639f4ccdf69dc368311c20/ruff-0.11.8.tar.gz", hash = "sha256:6d742d10626f9004b781f4558154bb226620a7242080e11caeffab1a40e99df8", size = 4086399, upload-time = "2025-05-01T14:53:24.459Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/fb/71/75dfb7194fe6502708e547941d41162574d1f579c4676a8eb645bf1a6842/ruff-0.11.9-py3-none-linux_armv6l.whl", hash = "sha256:a31a1d143a5e6f499d1fb480f8e1e780b4dfdd580f86e05e87b835d22c5c6f8c", size = 10335453, upload-time = "2025-05-09T16:18:58.2Z" },
|
{ url = "https://files.pythonhosted.org/packages/9f/60/c6aa9062fa518a9f86cb0b85248245cddcd892a125ca00441df77d79ef88/ruff-0.11.8-py3-none-linux_armv6l.whl", hash = "sha256:896a37516c594805e34020c4a7546c8f8a234b679a7716a3f08197f38913e1a3", size = 10272473, upload-time = "2025-05-01T14:52:37.252Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/74/fc/ad80c869b1732f53c4232bbf341f33c5075b2c0fb3e488983eb55964076a/ruff-0.11.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:66bc18ca783b97186a1f3100e91e492615767ae0a3be584e1266aa9051990722", size = 11072566, upload-time = "2025-05-09T16:19:01.432Z" },
|
{ url = "https://files.pythonhosted.org/packages/a0/e4/0325e50d106dc87c00695f7bcd5044c6d252ed5120ebf423773e00270f50/ruff-0.11.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ab86d22d3d721a40dd3ecbb5e86ab03b2e053bc93c700dc68d1c3346b36ce835", size = 11040862, upload-time = "2025-05-01T14:52:41.022Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/87/0d/0ccececef8a0671dae155cbf7a1f90ea2dd1dba61405da60228bbe731d35/ruff-0.11.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:bd576cd06962825de8aece49f28707662ada6a1ff2db848d1348e12c580acbf1", size = 10435020, upload-time = "2025-05-09T16:19:03.897Z" },
|
{ url = "https://files.pythonhosted.org/packages/e6/27/b87ea1a7be37fef0adbc7fd987abbf90b6607d96aa3fc67e2c5b858e1e53/ruff-0.11.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:258f3585057508d317610e8a412788cf726efeefa2fec4dba4001d9e6f90d46c", size = 10385273, upload-time = "2025-05-01T14:52:43.551Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/52/01/e249e1da6ad722278094e183cbf22379a9bbe5f21a3e46cef24ccab76e22/ruff-0.11.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b1d18b4be8182cc6fddf859ce432cc9631556e9f371ada52f3eaefc10d878de", size = 10593935, upload-time = "2025-05-09T16:19:06.455Z" },
|
{ url = "https://files.pythonhosted.org/packages/d3/f7/3346161570d789045ed47a86110183f6ac3af0e94e7fd682772d89f7f1a1/ruff-0.11.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:727d01702f7c30baed3fc3a34901a640001a2828c793525043c29f7614994a8c", size = 10578330, upload-time = "2025-05-01T14:52:45.48Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/ed/9a/40cf91f61e3003fe7bd43f1761882740e954506c5a0f9097b1cff861f04c/ruff-0.11.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0f3f46f759ac623e94824b1e5a687a0df5cd7f5b00718ff9c24f0a894a683be7", size = 10172971, upload-time = "2025-05-09T16:19:10.261Z" },
|
{ url = "https://files.pythonhosted.org/packages/c6/c3/327fb950b4763c7b3784f91d3038ef10c13b2d42322d4ade5ce13a2f9edb/ruff-0.11.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3dca977cc4fc8f66e89900fa415ffe4dbc2e969da9d7a54bfca81a128c5ac219", size = 10122223, upload-time = "2025-05-01T14:52:47.675Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/61/12/d395203de1e8717d7a2071b5a340422726d4736f44daf2290aad1085075f/ruff-0.11.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f34847eea11932d97b521450cf3e1d17863cfa5a94f21a056b93fb86f3f3dba2", size = 11748631, upload-time = "2025-05-09T16:19:12.307Z" },
|
{ url = "https://files.pythonhosted.org/packages/de/c7/ba686bce9adfeb6c61cb1bbadc17d58110fe1d602f199d79d4c880170f19/ruff-0.11.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c657fa987d60b104d2be8b052d66da0a2a88f9bd1d66b2254333e84ea2720c7f", size = 11697353, upload-time = "2025-05-01T14:52:50.264Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/66/d6/ef4d5eba77677eab511644c37c55a3bb8dcac1cdeb331123fe342c9a16c9/ruff-0.11.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f33b15e00435773df97cddcd263578aa83af996b913721d86f47f4e0ee0ff271", size = 12409236, upload-time = "2025-05-09T16:19:15.006Z" },
|
{ url = "https://files.pythonhosted.org/packages/53/8e/a4fb4a1ddde3c59e73996bb3ac51844ff93384d533629434b1def7a336b0/ruff-0.11.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f2e74b021d0de5eceb8bd32919f6ff8a9b40ee62ed97becd44993ae5b9949474", size = 12375936, upload-time = "2025-05-01T14:52:52.394Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/c5/8f/5a2c5fc6124dd925a5faf90e1089ee9036462118b619068e5b65f8ea03df/ruff-0.11.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7b27613a683b086f2aca8996f63cb3dd7bc49e6eccf590563221f7b43ded3f65", size = 11881436, upload-time = "2025-05-09T16:19:17.063Z" },
|
{ url = "https://files.pythonhosted.org/packages/ad/a1/9529cb1e2936e2479a51aeb011307e7229225df9ac64ae064d91ead54571/ruff-0.11.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f9b5ef39820abc0f2c62111f7045009e46b275f5b99d5e59dda113c39b7f4f38", size = 11850083, upload-time = "2025-05-01T14:52:55.424Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/39/d1/9683f469ae0b99b95ef99a56cfe8c8373c14eba26bd5c622150959ce9f64/ruff-0.11.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e0d88756e63e8302e630cee3ce2ffb77859797cc84a830a24473939e6da3ca6", size = 13982759, upload-time = "2025-05-09T16:19:19.693Z" },
|
{ url = "https://files.pythonhosted.org/packages/3e/94/8f7eac4c612673ae15a4ad2bc0ee62e03c68a2d4f458daae3de0e47c67ba/ruff-0.11.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1dba3135ca503727aa4648152c0fa67c3b1385d3dc81c75cd8a229c4b2a1458", size = 14005834, upload-time = "2025-05-01T14:52:58.056Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/4e/0b/c53a664f06e0faab596397867c6320c3816df479e888fe3af63bc3f89699/ruff-0.11.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:537c82c9829d7811e3aa680205f94c81a2958a122ac391c0eb60336ace741a70", size = 11541985, upload-time = "2025-05-09T16:19:21.831Z" },
|
{ url = "https://files.pythonhosted.org/packages/1e/7c/6f63b46b2be870cbf3f54c9c4154d13fac4b8827f22fa05ac835c10835b2/ruff-0.11.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f024d32e62faad0f76b2d6afd141b8c171515e4fb91ce9fd6464335c81244e5", size = 11503713, upload-time = "2025-05-01T14:53:01.244Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/23/a0/156c4d7e685f6526a636a60986ee4a3c09c8c4e2a49b9a08c9913f46c139/ruff-0.11.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:440ac6a7029f3dee7d46ab7de6f54b19e34c2b090bb4f2480d0a2d635228f381", size = 10465775, upload-time = "2025-05-09T16:19:24.401Z" },
|
{ url = "https://files.pythonhosted.org/packages/3a/91/57de411b544b5fe072779678986a021d87c3ee5b89551f2ca41200c5d643/ruff-0.11.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d365618d3ad747432e1ae50d61775b78c055fee5936d77fb4d92c6f559741948", size = 10457182, upload-time = "2025-05-01T14:53:03.726Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/43/d5/88b9a6534d9d4952c355e38eabc343df812f168a2c811dbce7d681aeb404/ruff-0.11.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:71c539bac63d0788a30227ed4d43b81353c89437d355fdc52e0cda4ce5651787", size = 10170957, upload-time = "2025-05-09T16:19:27.08Z" },
|
{ url = "https://files.pythonhosted.org/packages/01/49/cfe73e0ce5ecdd3e6f1137bf1f1be03dcc819d1bfe5cff33deb40c5926db/ruff-0.11.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4d9aaa91035bdf612c8ee7266153bcf16005c7c7e2f5878406911c92a31633cb", size = 10101027, upload-time = "2025-05-01T14:53:06.555Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/f0/b8/2bd533bdaf469dc84b45815ab806784d561fab104d993a54e1852596d581/ruff-0.11.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c67117bc82457e4501473c5f5217d49d9222a360794bfb63968e09e70f340abd", size = 11143307, upload-time = "2025-05-09T16:19:29.462Z" },
|
{ url = "https://files.pythonhosted.org/packages/56/21/a5cfe47c62b3531675795f38a0ef1c52ff8de62eaddf370d46634391a3fb/ruff-0.11.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0eba551324733efc76116d9f3a0d52946bc2751f0cd30661564117d6fd60897c", size = 11111298, upload-time = "2025-05-01T14:53:08.825Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/2f/d9/43cfba291788459b9bfd4e09a0479aa94d05ab5021d381a502d61a807ec1/ruff-0.11.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e4b78454f97aa454586e8a5557facb40d683e74246c97372af3c2d76901d697b", size = 11603026, upload-time = "2025-05-09T16:19:31.569Z" },
|
{ url = "https://files.pythonhosted.org/packages/36/98/f76225f87e88f7cb669ae92c062b11c0a1e91f32705f829bd426f8e48b7b/ruff-0.11.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:161eb4cff5cfefdb6c9b8b3671d09f7def2f960cee33481dd898caf2bcd02304", size = 11566884, upload-time = "2025-05-01T14:53:11.626Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/22/e6/7ed70048e89b01d728ccc950557a17ecf8df4127b08a56944b9d0bae61bc/ruff-0.11.9-py3-none-win32.whl", hash = "sha256:7fe1bc950e7d7b42caaee2a8a3bc27410547cc032c9558ee2e0f6d3b209e845a", size = 10548627, upload-time = "2025-05-09T16:19:33.657Z" },
|
{ url = "https://files.pythonhosted.org/packages/de/7e/fff70b02e57852fda17bd43f99dda37b9bcf3e1af3d97c5834ff48d04715/ruff-0.11.8-py3-none-win32.whl", hash = "sha256:5b18caa297a786465cc511d7f8be19226acf9c0a1127e06e736cd4e1878c3ea2", size = 10451102, upload-time = "2025-05-01T14:53:14.303Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/90/36/1da5d566271682ed10f436f732e5f75f926c17255c9c75cefb77d4bf8f10/ruff-0.11.9-py3-none-win_amd64.whl", hash = "sha256:52edaa4a6d70f8180343a5b7f030c7edd36ad180c9f4d224959c2d689962d964", size = 11634340, upload-time = "2025-05-09T16:19:35.815Z" },
|
{ url = "https://files.pythonhosted.org/packages/7b/a9/eaa571eb70648c9bde3120a1d5892597de57766e376b831b06e7c1e43945/ruff-0.11.8-py3-none-win_amd64.whl", hash = "sha256:6e70d11043bef637c5617297bdedec9632af15d53ac1e1ba29c448da9341b0c4", size = 11597410, upload-time = "2025-05-01T14:53:16.571Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/40/f7/70aad26e5877c8f7ee5b161c4c9fa0100e63fc4c944dc6d97b9c7e871417/ruff-0.11.9-py3-none-win_arm64.whl", hash = "sha256:bcf42689c22f2e240f496d0c183ef2c6f7b35e809f12c1db58f75d9aa8d630ca", size = 10741080, upload-time = "2025-05-09T16:19:39.605Z" },
|
{ url = "https://files.pythonhosted.org/packages/cd/be/f6b790d6ae98f1f32c645f8540d5c96248b72343b0a56fab3a07f2941897/ruff-0.11.8-py3-none-win_arm64.whl", hash = "sha256:304432e4c4a792e3da85b7699feb3426a0908ab98bf29df22a31b0cdd098fac2", size = 10713129, upload-time = "2025-05-01T14:53:22.27Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2923,7 +2919,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "selenium"
|
name = "selenium"
|
||||||
version = "4.32.0"
|
version = "4.31.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "certifi" },
|
{ name = "certifi" },
|
||||||
@ -2933,22 +2929,22 @@ dependencies = [
|
|||||||
{ name = "urllib3", extra = ["socks"] },
|
{ name = "urllib3", extra = ["socks"] },
|
||||||
{ name = "websocket-client" },
|
{ name = "websocket-client" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/54/2d/fafffe946099033ccf22bf89e12eede14c1d3c5936110c5f6f2b9830722c/selenium-4.32.0.tar.gz", hash = "sha256:b9509bef4056f4083772abb1ae19ff57247d617a29255384b26be6956615b206", size = 870997, upload-time = "2025-05-02T20:35:27.325Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/e0/bf/642cce8b5a9edad8e4880fdefbeb24f69bec2086b1121c63f883c412b797/selenium-4.31.0.tar.gz", hash = "sha256:441cffc436a2e6659fe3cfb012692435652efd38b0d368d16f661a5db47825f5", size = 855418, upload-time = "2025-04-05T00:43:06.447Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/ea/37/d07ed9d13e571b2115d4ed6956d156c66816ceec0b03b2e463e80d09f572/selenium-4.32.0-py3-none-any.whl", hash = "sha256:c4d9613f8a45693d61530c9660560fadb52db7d730237bc788ddedf442391f97", size = 9369668, upload-time = "2025-05-02T20:35:24.726Z" },
|
{ url = "https://files.pythonhosted.org/packages/32/53/212db779d2481b0a8428365960596f8d5a4d482ae12c441d0507fd54aaf2/selenium-4.31.0-py3-none-any.whl", hash = "sha256:7b8b8d5e424d7133cb7aa656263b19ac505ec26d65c0f921a696e7e2c5ccd95b", size = 9350584, upload-time = "2025-04-05T00:43:04.04Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sentry-sdk"
|
name = "sentry-sdk"
|
||||||
version = "2.28.0"
|
version = "2.27.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "certifi" },
|
{ name = "certifi" },
|
||||||
{ name = "urllib3" },
|
{ name = "urllib3" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/5e/bb/6a41b2e0e9121bed4d2ec68d50568ab95c49f4744156a9bbb789c866c66d/sentry_sdk-2.28.0.tar.gz", hash = "sha256:14d2b73bc93afaf2a9412490329099e6217761cbab13b6ee8bc0e82927e1504e", size = 325052, upload-time = "2025-05-12T07:53:12.785Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/cf/b6/a92ae6fa6d7e6e536bc586776b1669b84fb724dfe21b8ff08297f2d7c969/sentry_sdk-2.27.0.tar.gz", hash = "sha256:90f4f883f9eff294aff59af3d58c2d1b64e3927b28d5ada2b9b41f5aeda47daf", size = 323556, upload-time = "2025-04-24T10:09:37.927Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/9b/4e/b1575833094c088dfdef63fbca794518860fcbc8002aadf51ebe8b6a387f/sentry_sdk-2.28.0-py2.py3-none-any.whl", hash = "sha256:51496e6cb3cb625b99c8e08907c67a9112360259b0ef08470e532c3ab184a232", size = 341693, upload-time = "2025-05-12T07:53:10.882Z" },
|
{ url = "https://files.pythonhosted.org/packages/dd/8b/fb496a45854e37930b57564a20fb8e90dd0f8b6add0491527c00f2163b00/sentry_sdk-2.27.0-py2.py3-none-any.whl", hash = "sha256:c58935bfff8af6a0856d37e8adebdbc7b3281c2b632ec823ef03cd108d216ff0", size = 340786, upload-time = "2025-04-24T10:09:35.897Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3000,11 +2996,11 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "setuptools"
|
name = "setuptools"
|
||||||
version = "80.4.0"
|
version = "80.3.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/95/32/0cc40fe41fd2adb80a2f388987f4f8db3c866c69e33e0b4c8b093fdf700e/setuptools-80.4.0.tar.gz", hash = "sha256:5a78f61820bc088c8e4add52932ae6b8cf423da2aff268c23f813cfbb13b4006", size = 1315008, upload-time = "2025-05-09T20:42:27.972Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/70/dc/3976b322de9d2e87ed0007cf04cc7553969b6c7b3f48a565d0333748fbcd/setuptools-80.3.1.tar.gz", hash = "sha256:31e2c58dbb67c99c289f51c16d899afedae292b978f8051efaf6262d8212f927", size = 1315082, upload-time = "2025-05-04T18:47:04.397Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/b1/93/dba5ed08c2e31ec7cdc2ce75705a484ef0be1a2fecac8a58272489349de8/setuptools-80.4.0-py3-none-any.whl", hash = "sha256:6cdc8cb9a7d590b237dbe4493614a9b75d0559b888047c1f67d49ba50fc3edb2", size = 1200812, upload-time = "2025-05-09T20:42:25.325Z" },
|
{ url = "https://files.pythonhosted.org/packages/53/7e/5d8af3317ddbf9519b687bd1c39d8737fde07d97f54df65553faca5cffb1/setuptools-80.3.1-py3-none-any.whl", hash = "sha256:ea8e00d7992054c4c592aeb892f6ad51fe1b4d90cc6947cc45c45717c40ec537", size = 1201172, upload-time = "2025-05-04T18:47:02.575Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3099,14 +3095,14 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tenant-schemas-celery"
|
name = "tenant-schemas-celery"
|
||||||
version = "4.0.1"
|
version = "3.0.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "celery" },
|
{ name = "celery" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/19/f8/cf055bf171b5d83d6fe96f1840fba90d3d274be2b5c35cd21b873302b128/tenant_schemas_celery-4.0.1.tar.gz", hash = "sha256:8b8f055fcd82aa53274c09faf88653a935241518d93b86ab2d43a3df3b70c7f8", size = 18870, upload-time = "2025-04-22T18:23:51.061Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/d0/fe/cfe19eb7cc3ad8e39d7df7b7c44414bf665b6ac6660c998eb498f89d16c6/tenant_schemas_celery-3.0.0.tar.gz", hash = "sha256:6be3ae1a5826f262f0f3dd343c6a85a34a1c59b89e04ae37de018f36562fed55", size = 15954, upload-time = "2024-05-19T11:16:41.837Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/e9/a8/fd663c461550d6fedfb24e987acc1557ae5b6615ca08fc6c70dbaaa88aa5/tenant_schemas_celery-4.0.1-py3-none-any.whl", hash = "sha256:d06a3ff6956db3a95168ce2051b7bff2765f9ce0d070e14df92f07a2b60ae0a0", size = 21364, upload-time = "2025-04-22T18:23:49.899Z" },
|
{ url = "https://files.pythonhosted.org/packages/db/2c/376e1e641ad08b374c75d896468a7be2e6906ce3621fd0c9f9dc09ff1963/tenant_schemas_celery-3.0.0-py3-none-any.whl", hash = "sha256:ca0f69e78ef698eb4813468231df5a0ab6a660c08e657b65f5ac92e16887eec8", size = 18108, upload-time = "2024-05-19T11:16:39.92Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3160,7 +3156,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "twilio"
|
name = "twilio"
|
||||||
version = "9.6.1"
|
version = "9.6.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "aiohttp" },
|
{ name = "aiohttp" },
|
||||||
@ -3168,9 +3164,9 @@ dependencies = [
|
|||||||
{ name = "pyjwt" },
|
{ name = "pyjwt" },
|
||||||
{ name = "requests" },
|
{ name = "requests" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/95/78/453ff0d35442c53490c22d077f580684a2352846c721d3e01f4c6dfa85bd/twilio-9.6.1.tar.gz", hash = "sha256:bb80b31d4d9e55c33872efef7fb99373149ed4093f21c56cf582797da45862f5", size = 987002, upload-time = "2025-05-13T09:56:55.183Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/91/e9/ffc6e52465ffc16fad31fa64aea4e10e06cb4803447310c539c6fd66e859/twilio-9.6.0.tar.gz", hash = "sha256:bcb6cbc7f1dad09717d48d3e610573b6a55fa4a1f6fd1006f5b59cf6878b5562", size = 986499, upload-time = "2025-05-05T10:48:17.921Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/02/f4/36fe2566a3ad7f71a89fd28ea2ebb6b2aa05c3a4d5a55b3ca6c358768c6b/twilio-9.6.1-py2.py3-none-any.whl", hash = "sha256:441fdab61b9a204eef770368380b962cbf08dc0fe9f757fe4b1d63ced37ddeed", size = 1859407, upload-time = "2025-05-13T09:56:53.094Z" },
|
{ url = "https://files.pythonhosted.org/packages/b5/04/1d9f452b1089c634bd6d64b40b9002c935b8214e9b08a7cbbfef204c8186/twilio-9.6.0-py2.py3-none-any.whl", hash = "sha256:19e8554c56324186973dcb3121de34626755db15331767e3021a2e23f80c6a3b", size = 1859151, upload-time = "2025-05-05T10:48:15.394Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3487,17 +3483,12 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xmlsec"
|
name = "xmlsec"
|
||||||
version = "1.3.15"
|
version = "1.3.14"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "lxml" },
|
{ name = "lxml" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/6b/0b/d851367799b865500efd0b255c39fc5d30892ea28c1569ca185a76d19576/xmlsec-1.3.15.tar.gz", hash = "sha256:baa856b83d0012e278e6f6cbec96ac8128de667ca9fa9a2eeb02c752e816f6d8", size = 114117, upload-time = "2025-03-11T22:37:00.567Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/25/5b/244459b51dfe91211c1d9ec68fb5307dfc51e014698f52de575d25f753e0/xmlsec-1.3.14.tar.gz", hash = "sha256:934f804f2f895bcdb86f1eaee236b661013560ee69ec108d29cdd6e5f292a2d9", size = 68854, upload-time = "2024-04-17T19:34:29.388Z" }
|
||||||
wheels = [
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/13/17/0a272e6087ddb24bec96528acf061341845f458671e2a5cb35ff867a7c89/xmlsec-1.3.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6ac2154311d32a6571e22f224ed16356029e59bd5ca76edeb3922a809adfe89c", size = 3746315, upload-time = "2025-03-11T22:36:43.675Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/b7/91/7ce9317e3a2a03e3811e62be52e091c1e661da2d59b5c7f60ec1840a1e6b/xmlsec-1.3.15-cp313-cp313-win32.whl", hash = "sha256:5ed218129f89b0592926ad2be42c017bece469db9b7380dc41bc09b01ca26d5d", size = 2146158, upload-time = "2025-03-11T22:36:44.887Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/3d/e0/93311b9eedc11055ba667e666dc6ca1e2cc59c2356e91b73c3d5a6738fbf/xmlsec-1.3.15-cp313-cp313-win_amd64.whl", hash = "sha256:5fc29e69b064323317b3862751a3a8107670e0a17510ca4517bbdc1939a90b1a", size = 2442027, upload-time = "2025-03-11T22:36:46.431Z" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yarl"
|
name = "yarl"
|
||||||
|
1
web/package-lock.json
generated
1
web/package-lock.json
generated
@ -7,6 +7,7 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "@goauthentik/web",
|
"name": "@goauthentik/web",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
".",
|
".",
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
"lint:precommit": "wireit",
|
"lint:precommit": "wireit",
|
||||||
"lint:types": "wireit",
|
"lint:types": "wireit",
|
||||||
"lit-analyse": "wireit",
|
"lit-analyse": "wireit",
|
||||||
|
"postinstall": "bash scripts/patch-spotlight.sh",
|
||||||
"precommit": "wireit",
|
"precommit": "wireit",
|
||||||
"prettier": "wireit",
|
"prettier": "wireit",
|
||||||
"prettier-check": "wireit",
|
"prettier-check": "wireit",
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* @import { Message as ESBuildMessage } from "esbuild";
|
* @import { Message as ESBuildMessage } from "esbuild";
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const logPrefix = "authentik/dev/web: ";
|
const logPrefix = "👷 [ESBuild]";
|
||||||
const log = console.debug.bind(console, logPrefix);
|
const log = console.debug.bind(console, logPrefix);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -76,7 +76,7 @@ export class ESBuildObserver extends EventSource {
|
|||||||
*/
|
*/
|
||||||
#startListener = () => {
|
#startListener = () => {
|
||||||
this.#trackActivity();
|
this.#trackActivity();
|
||||||
log("⏰ Build started...");
|
log("⏰ Build started...");
|
||||||
};
|
};
|
||||||
|
|
||||||
#internalErrorListener = () => {
|
#internalErrorListener = () => {
|
||||||
@ -86,7 +86,7 @@ export class ESBuildObserver extends EventSource {
|
|||||||
clearTimeout(this.#keepAliveInterval);
|
clearTimeout(this.#keepAliveInterval);
|
||||||
|
|
||||||
this.close();
|
this.close();
|
||||||
log("⛔️ Closing connection");
|
log("⛔️ Closing connection");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -126,13 +126,13 @@ export class ESBuildObserver extends EventSource {
|
|||||||
this.#trackActivity();
|
this.#trackActivity();
|
||||||
|
|
||||||
if (!this.online) {
|
if (!this.online) {
|
||||||
log("🚫 Build finished while offline.");
|
log("🚫 Build finished while offline.");
|
||||||
this.deferredReload = true;
|
this.deferredReload = true;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
log("🛎️ Build completed! Reloading...");
|
log("🛎️ Build completed! Reloading...");
|
||||||
|
|
||||||
// We use an animation frame to keep the reload from happening before the
|
// We use an animation frame to keep the reload from happening before the
|
||||||
// event loop has a chance to process the message.
|
// event loop has a chance to process the message.
|
||||||
@ -189,13 +189,13 @@ export class ESBuildObserver extends EventSource {
|
|||||||
|
|
||||||
if (!this.deferredReload) return;
|
if (!this.deferredReload) return;
|
||||||
|
|
||||||
log("🛎️ Reloading after offline build...");
|
log("🛎️ Reloading after offline build...");
|
||||||
this.deferredReload = false;
|
this.deferredReload = false;
|
||||||
|
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
});
|
});
|
||||||
|
|
||||||
log("🛎️ Listening for build changes...");
|
log("🛎️ Listening for build changes...");
|
||||||
|
|
||||||
this.#keepAliveInterval = setInterval(() => {
|
this.#keepAliveInterval = setInterval(() => {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
@ -203,7 +203,7 @@ export class ESBuildObserver extends EventSource {
|
|||||||
if (now - this.lastUpdatedAt < 10_000) return;
|
if (now - this.lastUpdatedAt < 10_000) return;
|
||||||
|
|
||||||
this.alive = false;
|
this.alive = false;
|
||||||
log("👋 Waiting for build to start...");
|
log("👋 Waiting for build to start...");
|
||||||
}, 15_000);
|
}, 15_000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,16 +47,7 @@ class SimpleFlowExecutor {
|
|||||||
return `${ak().api.base}api/v3/flows/executor/${this.flowSlug}/?query=${encodeURIComponent(window.location.search.substring(1))}`;
|
return `${ak().api.base}api/v3/flows/executor/${this.flowSlug}/?query=${encodeURIComponent(window.location.search.substring(1))}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
loading() {
|
|
||||||
this.container.innerHTML = `<div class="d-flex justify-content-center">
|
|
||||||
<div class="spinner-border spinner-border-md" role="status">
|
|
||||||
<span class="sr-only">Loading...</span>
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
this.loading();
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "GET",
|
type: "GET",
|
||||||
url: this.apiURL,
|
url: this.apiURL,
|
||||||
@ -210,9 +201,6 @@ class PasswordStage extends Stage<PasswordChallenge> {
|
|||||||
<form id="password-form">
|
<form id="password-form">
|
||||||
<img class="mb-4 brand-icon" src="${ak().brand.branding_logo}" alt="">
|
<img class="mb-4 brand-icon" src="${ak().brand.branding_logo}" alt="">
|
||||||
<h1 class="h3 mb-3 fw-normal text-center">${this.challenge?.flowInfo?.title}</h1>
|
<h1 class="h3 mb-3 fw-normal text-center">${this.challenge?.flowInfo?.title}</h1>
|
||||||
<div class="form-label-group my-3">
|
|
||||||
<input type="text" readonly class="form-control-plaintext" value="Welcome, ${this.challenge?.pendingUser}.">
|
|
||||||
</div>
|
|
||||||
<div class="form-label-group my-3 has-validation">
|
<div class="form-label-group my-3 has-validation">
|
||||||
<input type="password" autofocus class="form-control ${this.error("password").length > 0 ? IS_INVALID : ""}" name="password" placeholder="Password">
|
<input type="password" autofocus class="form-control ${this.error("password").length > 0 ? IS_INVALID : ""}" name="password" placeholder="Password">
|
||||||
${this.renderInputError("password")}
|
${this.renderInputError("password")}
|
||||||
|
33
web/scripts/patch-spotlight.sh
Normal file
33
web/scripts/patch-spotlight.sh
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
TARGET="./node_modules/@spotlightjs/overlay/dist/index-"[0-9a-f]*.js
|
||||||
|
|
||||||
|
if [[ $(grep -L "QX2" "$TARGET" > /dev/null 2> /dev/null) ]]; then
|
||||||
|
patch --forward -V none --no-backup-if-mismatch -p0 $TARGET <<EOF
|
||||||
|
|
||||||
|
TARGET=$(find "./node_modules/@spotlightjs/overlay/dist/" -name "index-[0-9a-f]*.js");
|
||||||
|
|
||||||
|
if ! grep -GL 'QX2 = ' "$TARGET" > /dev/null ; then
|
||||||
|
patch --forward --no-backup-if-mismatch -p0 "$TARGET" <<EOF
|
||||||
|
>>>>>>> main
|
||||||
|
--- a/index-5682ce90.js 2024-06-13 16:19:28
|
||||||
|
+++ b/index-5682ce90.js 2024-06-13 16:20:23
|
||||||
|
@@ -4958,11 +4958,10 @@
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
-const q2 = w.lazy(() => import("./main-3257b7fc.js").then((n) => n.m));
|
||||||
|
+const q2 = w.lazy(() => import("./main-3257b7fc.js").then((n) => n.m)), QX2 = () => {};
|
||||||
|
function Gp({
|
||||||
|
data: n,
|
||||||
|
- onUpdateData: a = () => {
|
||||||
|
- },
|
||||||
|
+ onUpdateData: a = QX2,
|
||||||
|
editingEnabled: s = !1,
|
||||||
|
clipboardEnabled: o = !1,
|
||||||
|
displayDataTypes: c = !1,
|
||||||
|
EOF
|
||||||
|
|
||||||
|
else
|
||||||
|
echo "spotlight overlay.js patch already applied"
|
||||||
|
fi
|
@ -131,9 +131,9 @@ export class AdminInterface extends WithLicenseSummary(AuthenticatedInterface) {
|
|||||||
//#region Lifecycle
|
//#region Lifecycle
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
configureSentry(true);
|
|
||||||
super();
|
super();
|
||||||
this.ws = new WebsocketClient();
|
this.ws = new WebsocketClient();
|
||||||
|
|
||||||
this.#sidebarMatcher = window.matchMedia("(min-width: 1200px)");
|
this.#sidebarMatcher = window.matchMedia("(min-width: 1200px)");
|
||||||
this.sidebarOpen = this.#sidebarMatcher.matches;
|
this.sidebarOpen = this.#sidebarMatcher.matches;
|
||||||
}
|
}
|
||||||
@ -167,6 +167,7 @@ export class AdminInterface extends WithLicenseSummary(AuthenticatedInterface) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async firstUpdated(): Promise<void> {
|
async firstUpdated(): Promise<void> {
|
||||||
|
configureSentry(true);
|
||||||
this.user = await me();
|
this.user = await me();
|
||||||
|
|
||||||
const canAccessAdmin =
|
const canAccessAdmin =
|
||||||
|
@ -113,7 +113,8 @@ export class ApplicationViewPage extends AKElement {
|
|||||||
|
|
||||||
renderApp(): TemplateResult {
|
renderApp(): TemplateResult {
|
||||||
if (!this.application) {
|
if (!this.application) {
|
||||||
return html`<ak-empty-state loading header=${msg("Loading")}> </ak-empty-state>`;
|
return html`<ak-empty-state ?loading="${true}" header=${msg("Loading")}>
|
||||||
|
</ak-empty-state>`;
|
||||||
}
|
}
|
||||||
return html`<ak-tabs>
|
return html`<ak-tabs>
|
||||||
${this.missingOutpost
|
${this.missingOutpost
|
||||||
|
@ -42,7 +42,7 @@ export class ProviderViewPage extends AKElement {
|
|||||||
|
|
||||||
renderProvider(): TemplateResult {
|
renderProvider(): TemplateResult {
|
||||||
if (!this.provider) {
|
if (!this.provider) {
|
||||||
return html`<ak-empty-state loading ?fullHeight=${true}></ak-empty-state>`;
|
return html`<ak-empty-state ?loading=${true} ?fullHeight=${true}></ak-empty-state>`;
|
||||||
}
|
}
|
||||||
switch (this.provider?.component) {
|
switch (this.provider?.component) {
|
||||||
case "ak-provider-saml-form":
|
case "ak-provider-saml-form":
|
||||||
|
@ -432,7 +432,7 @@ export class OAuth2ProviderViewPage extends AKElement {
|
|||||||
<div class="pf-c-card__body">
|
<div class="pf-c-card__body">
|
||||||
${this.preview
|
${this.preview
|
||||||
? html`<pre>${JSON.stringify(this.preview?.preview, null, 4)}</pre>`
|
? html`<pre>${JSON.stringify(this.preview?.preview, null, 4)}</pre>`
|
||||||
: html` <ak-empty-state loading></ak-empty-state> `}
|
: html` <ak-empty-state ?loading=${true}></ak-empty-state> `}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
@ -502,7 +502,7 @@ export class SAMLProviderViewPage extends AKElement {
|
|||||||
|
|
||||||
renderTabPreview(): TemplateResult {
|
renderTabPreview(): TemplateResult {
|
||||||
if (!this.preview) {
|
if (!this.preview) {
|
||||||
return html`<ak-empty-state loading></ak-empty-state>`;
|
return html`<ak-empty-state ?loading=${true}></ak-empty-state>`;
|
||||||
}
|
}
|
||||||
return html` <div
|
return html` <div
|
||||||
class="pf-c-page__main-section pf-m-no-padding-mobile pf-l-grid pf-m-gutter"
|
class="pf-c-page__main-section pf-m-no-padding-mobile pf-l-grid pf-m-gutter"
|
||||||
|
@ -34,7 +34,7 @@ export class SourceViewPage extends AKElement {
|
|||||||
|
|
||||||
renderSource(): TemplateResult {
|
renderSource(): TemplateResult {
|
||||||
if (!this.source) {
|
if (!this.source) {
|
||||||
return html`<ak-empty-state loading ?fullHeight=${true}></ak-empty-state>`;
|
return html`<ak-empty-state ?loading=${true} ?fullHeight=${true}></ak-empty-state>`;
|
||||||
}
|
}
|
||||||
switch (this.source?.component) {
|
switch (this.source?.component) {
|
||||||
case "ak-source-kerberos-form":
|
case "ak-source-kerberos-form":
|
||||||
|
@ -6,7 +6,6 @@ import {
|
|||||||
} from "@goauthentik/common/api/middleware";
|
} from "@goauthentik/common/api/middleware";
|
||||||
import { EVENT_LOCALE_REQUEST, VERSION } from "@goauthentik/common/constants";
|
import { EVENT_LOCALE_REQUEST, VERSION } from "@goauthentik/common/constants";
|
||||||
import { globalAK } from "@goauthentik/common/global";
|
import { globalAK } from "@goauthentik/common/global";
|
||||||
import { SentryMiddleware } from "@goauthentik/common/sentry";
|
|
||||||
|
|
||||||
import { Config, Configuration, CoreApi, CurrentBrand, RootApi } from "@goauthentik/api";
|
import { Config, Configuration, CoreApi, CurrentBrand, RootApi } from "@goauthentik/api";
|
||||||
|
|
||||||
@ -67,13 +66,21 @@ export function brand(): Promise<CurrentBrand> {
|
|||||||
return globalBrandPromise;
|
return globalBrandPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getMetaContent(key: string): string {
|
||||||
|
const metaEl = document.querySelector<HTMLMetaElement>(`meta[name=${key}]`);
|
||||||
|
if (!metaEl) return "";
|
||||||
|
return metaEl.content;
|
||||||
|
}
|
||||||
|
|
||||||
export const DEFAULT_CONFIG = new Configuration({
|
export const DEFAULT_CONFIG = new Configuration({
|
||||||
basePath: `${globalAK().api.base}api/v3`,
|
basePath: `${globalAK().api.base}api/v3`,
|
||||||
|
headers: {
|
||||||
|
"sentry-trace": getMetaContent("sentry-trace"),
|
||||||
|
},
|
||||||
middleware: [
|
middleware: [
|
||||||
new CSRFMiddleware(),
|
new CSRFMiddleware(),
|
||||||
new EventMiddleware(),
|
new EventMiddleware(),
|
||||||
new LoggingMiddleware(globalAK().brand),
|
new LoggingMiddleware(globalAK().brand),
|
||||||
new SentryMiddleware(),
|
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
import { config } from "@goauthentik/common/api/config";
|
||||||
import { VERSION } from "@goauthentik/common/constants";
|
import { VERSION } from "@goauthentik/common/constants";
|
||||||
import { globalAK } from "@goauthentik/common/global";
|
|
||||||
import { me } from "@goauthentik/common/users";
|
import { me } from "@goauthentik/common/users";
|
||||||
import { readInterfaceRouteParam } from "@goauthentik/elements/router/utils";
|
import { readInterfaceRouteParam } from "@goauthentik/elements/router/utils";
|
||||||
import {
|
import {
|
||||||
@ -10,16 +10,8 @@ import {
|
|||||||
setTag,
|
setTag,
|
||||||
setUser,
|
setUser,
|
||||||
} from "@sentry/browser";
|
} from "@sentry/browser";
|
||||||
import { getTraceData } from "@sentry/core";
|
|
||||||
import * as Spotlight from "@spotlightjs/spotlight";
|
|
||||||
|
|
||||||
import {
|
import { CapabilitiesEnum, Config, ResponseError } from "@goauthentik/api";
|
||||||
CapabilitiesEnum,
|
|
||||||
FetchParams,
|
|
||||||
Middleware,
|
|
||||||
RequestContext,
|
|
||||||
ResponseError,
|
|
||||||
} from "@goauthentik/api";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A generic error that can be thrown without triggering Sentry's reporting.
|
* A generic error that can be thrown without triggering Sentry's reporting.
|
||||||
@ -29,94 +21,69 @@ export class SentryIgnoredError extends Error {}
|
|||||||
export const TAG_SENTRY_COMPONENT = "authentik.component";
|
export const TAG_SENTRY_COMPONENT = "authentik.component";
|
||||||
export const TAG_SENTRY_CAPABILITIES = "authentik.capabilities";
|
export const TAG_SENTRY_CAPABILITIES = "authentik.capabilities";
|
||||||
|
|
||||||
let _sentryConfigured = false;
|
export async function configureSentry(canDoPpi = false): Promise<Config> {
|
||||||
|
const cfg = await config();
|
||||||
|
|
||||||
export function configureSentry(canDoPpi = false) {
|
if (cfg.errorReporting.enabled) {
|
||||||
const cfg = globalAK().config;
|
init({
|
||||||
const debug = cfg.capabilities.includes(CapabilitiesEnum.CanDebug);
|
dsn: cfg.errorReporting.sentryDsn,
|
||||||
if (!cfg.errorReporting.enabled && !debug) {
|
ignoreErrors: [
|
||||||
return cfg;
|
/network/gi,
|
||||||
}
|
/fetch/gi,
|
||||||
init({
|
/module/gi,
|
||||||
dsn: cfg.errorReporting.sentryDsn,
|
// Error on edge on ios,
|
||||||
ignoreErrors: [
|
// https://stackoverflow.com/questions/69261499/what-is-instantsearchsdkjsbridgeclearhighlight
|
||||||
/network/gi,
|
/instantSearchSDKJSBridgeClearHighlight/gi,
|
||||||
/fetch/gi,
|
// Seems to be an issue in Safari and Firefox
|
||||||
/module/gi,
|
/MutationObserver.observe/gi,
|
||||||
// Error on edge on ios,
|
/NS_ERROR_FAILURE/gi,
|
||||||
// https://stackoverflow.com/questions/69261499/what-is-instantsearchsdkjsbridgeclearhighlight
|
],
|
||||||
/instantSearchSDKJSBridgeClearHighlight/gi,
|
release: `authentik@${VERSION}`,
|
||||||
// Seems to be an issue in Safari and Firefox
|
|
||||||
/MutationObserver.observe/gi,
|
|
||||||
/NS_ERROR_FAILURE/gi,
|
|
||||||
],
|
|
||||||
release: `authentik@${VERSION}`,
|
|
||||||
integrations: [
|
|
||||||
browserTracingIntegration({
|
|
||||||
// https://docs.sentry.io/platforms/javascript/tracing/instrumentation/automatic-instrumentation/#custom-routing
|
|
||||||
instrumentNavigation: false,
|
|
||||||
instrumentPageLoad: false,
|
|
||||||
traceFetch: false,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
tracePropagationTargets: [window.location.origin],
|
|
||||||
tracesSampleRate: debug ? 1.0 : cfg.errorReporting.tracesSampleRate,
|
|
||||||
environment: cfg.errorReporting.environment,
|
|
||||||
beforeSend: (
|
|
||||||
event: ErrorEvent,
|
|
||||||
hint: EventHint,
|
|
||||||
): ErrorEvent | PromiseLike<ErrorEvent | null> | null => {
|
|
||||||
if (!hint) {
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
if (hint.originalException instanceof SentryIgnoredError) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
hint.originalException instanceof ResponseError ||
|
|
||||||
hint.originalException instanceof DOMException
|
|
||||||
) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return event;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
setTag(TAG_SENTRY_CAPABILITIES, cfg.capabilities.join(","));
|
|
||||||
if (window.location.pathname.includes("if/")) {
|
|
||||||
setTag(TAG_SENTRY_COMPONENT, `web/${readInterfaceRouteParam()}`);
|
|
||||||
}
|
|
||||||
if (debug) {
|
|
||||||
Spotlight.init({
|
|
||||||
injectImmediately: true,
|
|
||||||
integrations: [
|
integrations: [
|
||||||
Spotlight.sentry({
|
browserTracingIntegration({
|
||||||
injectIntoSDK: true,
|
shouldCreateSpanForRequest: (url: string) => {
|
||||||
|
return url.startsWith(window.location.host);
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
tracesSampleRate: cfg.errorReporting.tracesSampleRate,
|
||||||
|
environment: cfg.errorReporting.environment,
|
||||||
|
beforeSend: (
|
||||||
|
event: ErrorEvent,
|
||||||
|
hint: EventHint,
|
||||||
|
): ErrorEvent | PromiseLike<ErrorEvent | null> | null => {
|
||||||
|
if (!hint) {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
if (hint.originalException instanceof SentryIgnoredError) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
hint.originalException instanceof ResponseError ||
|
||||||
|
hint.originalException instanceof DOMException
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return event;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
console.debug("authentik/config: Enabled Sentry Spotlight");
|
setTag(TAG_SENTRY_CAPABILITIES, cfg.capabilities.join(","));
|
||||||
}
|
if (window.location.pathname.includes("if/")) {
|
||||||
if (cfg.errorReporting.sendPii && canDoPpi) {
|
setTag(TAG_SENTRY_COMPONENT, `web/${readInterfaceRouteParam()}`);
|
||||||
me().then((user) => {
|
}
|
||||||
setUser({ email: user.user.email });
|
if (cfg.capabilities.includes(CapabilitiesEnum.CanDebug)) {
|
||||||
console.debug("authentik/config: Sentry with PII enabled.");
|
const Spotlight = await import("@spotlightjs/spotlight");
|
||||||
});
|
|
||||||
} else {
|
Spotlight.init({ injectImmediately: true });
|
||||||
console.debug("authentik/config: Sentry enabled.");
|
}
|
||||||
}
|
if (cfg.errorReporting.sendPii && canDoPpi) {
|
||||||
_sentryConfigured = true;
|
me().then((user) => {
|
||||||
}
|
setUser({ email: user.user.email });
|
||||||
|
console.debug("authentik/config: Sentry with PII enabled.");
|
||||||
export class SentryMiddleware implements Middleware {
|
});
|
||||||
pre?(context: RequestContext): Promise<FetchParams | void> {
|
} else {
|
||||||
if (!_sentryConfigured) {
|
console.debug("authentik/config: Sentry enabled.");
|
||||||
return Promise.resolve(context);
|
|
||||||
}
|
}
|
||||||
const traceData = getTraceData();
|
|
||||||
// @ts-ignore
|
|
||||||
context.init.headers["baggage"] = traceData["baggage"];
|
|
||||||
// @ts-ignore
|
|
||||||
context.init.headers["sentry-trace"] = traceData["sentry-trace"];
|
|
||||||
return Promise.resolve(context);
|
|
||||||
}
|
}
|
||||||
|
return cfg;
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ export class Diagram extends AKElement {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!this.diagram) {
|
if (!this.diagram) {
|
||||||
return html`<ak-empty-state loading></ak-empty-state>`;
|
return html`<ak-empty-state ?loading=${true}></ak-empty-state>`;
|
||||||
}
|
}
|
||||||
return html`${until(
|
return html`${until(
|
||||||
mermaid.render("graph", this.diagram).then((r) => {
|
mermaid.render("graph", this.diagram).then((r) => {
|
||||||
|
@ -23,20 +23,9 @@ const configContext = Symbol("configContext");
|
|||||||
const modalController = Symbol("modalController");
|
const modalController = Symbol("modalController");
|
||||||
const versionContext = Symbol("versionContext");
|
const versionContext = Symbol("versionContext");
|
||||||
|
|
||||||
export abstract class LightInterface extends AKElement implements ThemedElement {
|
export abstract class Interface extends AKElement implements ThemedElement {
|
||||||
protected static readonly PFBaseStyleSheet = createStyleSheetUnsafe(PFBase);
|
protected static readonly PFBaseStyleSheet = createStyleSheetUnsafe(PFBase);
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
const styleParent = resolveStyleSheetParent(document);
|
|
||||||
|
|
||||||
this.dataset.akInterfaceRoot = this.tagName.toLowerCase();
|
|
||||||
|
|
||||||
appendStyleSheet(styleParent, Interface.PFBaseStyleSheet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export abstract class Interface extends LightInterface implements ThemedElement {
|
|
||||||
[configContext]: ConfigContextController;
|
[configContext]: ConfigContextController;
|
||||||
|
|
||||||
[modalController]: ModalOrchestrationController;
|
[modalController]: ModalOrchestrationController;
|
||||||
@ -49,6 +38,12 @@ export abstract class Interface extends LightInterface implements ThemedElement
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
const styleParent = resolveStyleSheetParent(document);
|
||||||
|
|
||||||
|
this.dataset.akInterfaceRoot = this.tagName.toLowerCase();
|
||||||
|
|
||||||
|
appendStyleSheet(styleParent, Interface.PFBaseStyleSheet);
|
||||||
|
|
||||||
this.addController(new BrandContextController(this));
|
this.addController(new BrandContextController(this));
|
||||||
this[configContext] = new ConfigContextController(this);
|
this[configContext] = new ConfigContextController(this);
|
||||||
this[modalController] = new ModalOrchestrationController(this);
|
this[modalController] = new ModalOrchestrationController(this);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { AuthenticatedInterface, Interface, LightInterface } from "./Interface";
|
import { AuthenticatedInterface, Interface } from "./Interface";
|
||||||
|
|
||||||
export { Interface, AuthenticatedInterface, LightInterface };
|
export { Interface, AuthenticatedInterface };
|
||||||
export default Interface;
|
export default Interface;
|
||||||
|
@ -230,7 +230,9 @@ export abstract class AKChart<T> extends AKElement {
|
|||||||
<p slot="body">${pluckErrorDetail(this.error)}</p>
|
<p slot="body">${pluckErrorDetail(this.error)}</p>
|
||||||
</ak-empty-state>
|
</ak-empty-state>
|
||||||
`
|
`
|
||||||
: html`${this.chart ? html`` : html`<ak-empty-state loading></ak-empty-state>`}`}
|
: html`${this.chart
|
||||||
|
? html``
|
||||||
|
: html`<ak-empty-state ?loading="${true}"></ak-empty-state>`}`}
|
||||||
${this.centerText ? html` <span>${this.centerText}</span> ` : html``}
|
${this.centerText ? html` <span>${this.centerText}</span> ` : html``}
|
||||||
<canvas style="${this.chart === undefined ? "display: none;" : ""}"></canvas>
|
<canvas style="${this.chart === undefined ? "display: none;" : ""}"></canvas>
|
||||||
</div>
|
</div>
|
||||||
|
@ -71,7 +71,7 @@ export abstract class ModelForm<T, PKT extends string | number> extends Form<T>
|
|||||||
|
|
||||||
renderVisible(): TemplateResult {
|
renderVisible(): TemplateResult {
|
||||||
if ((this._instancePk && !this.instance) || !this._initialDataLoad) {
|
if ((this._instancePk && !this.instance) || !this._initialDataLoad) {
|
||||||
return html`<ak-empty-state loading></ak-empty-state>`;
|
return html`<ak-empty-state ?loading=${true}></ak-empty-state>`;
|
||||||
}
|
}
|
||||||
return super.renderVisible();
|
return super.renderVisible();
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ export class Route {
|
|||||||
if (this.callback) {
|
if (this.callback) {
|
||||||
return html`${until(
|
return html`${until(
|
||||||
this.callback(args),
|
this.callback(args),
|
||||||
html`<ak-empty-state loading></ak-empty-state>`,
|
html`<ak-empty-state ?loading=${true}></ak-empty-state>`,
|
||||||
)}`;
|
)}`;
|
||||||
}
|
}
|
||||||
if (this.element) {
|
if (this.element) {
|
||||||
|
@ -6,35 +6,19 @@ import { TemplateResult } from "lit";
|
|||||||
export class RouteMatch {
|
export class RouteMatch {
|
||||||
route: Route;
|
route: Route;
|
||||||
arguments: { [key: string]: string };
|
arguments: { [key: string]: string };
|
||||||
fullURL: string;
|
fullUrl?: string;
|
||||||
|
|
||||||
constructor(route: Route, fullUrl: string) {
|
constructor(route: Route) {
|
||||||
this.route = route;
|
this.route = route;
|
||||||
this.arguments = {};
|
this.arguments = {};
|
||||||
this.fullURL = fullUrl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): TemplateResult {
|
render(): TemplateResult {
|
||||||
return this.route.render(this.arguments);
|
return this.route.render(this.arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert the matched Route's URL regex to a sanitized, readable URL by replacing
|
|
||||||
* all regex values with placeholders according to the name of their regex group.
|
|
||||||
*
|
|
||||||
* @returns The sanitized URL for logging/tracing.
|
|
||||||
*/
|
|
||||||
sanitizedURL() {
|
|
||||||
let cleanedURL = this.fullURL;
|
|
||||||
for (const match of Object.keys(this.arguments)) {
|
|
||||||
const value = this.arguments[match];
|
|
||||||
cleanedURL = cleanedURL?.replace(value, `:${match}`);
|
|
||||||
}
|
|
||||||
return cleanedURL;
|
|
||||||
}
|
|
||||||
|
|
||||||
toString(): string {
|
toString(): string {
|
||||||
return `<RouteMatch url=${this.sanitizedURL()} route=${this.route} arguments=${JSON.stringify(
|
return `<RouteMatch url=${this.fullUrl} route=${this.route} arguments=${JSON.stringify(
|
||||||
this.arguments,
|
this.arguments,
|
||||||
)}>`;
|
)}>`;
|
||||||
}
|
}
|
||||||
|
@ -3,15 +3,8 @@ import { AKElement } from "@goauthentik/elements/Base";
|
|||||||
import { Route } from "@goauthentik/elements/router/Route";
|
import { Route } from "@goauthentik/elements/router/Route";
|
||||||
import { RouteMatch } from "@goauthentik/elements/router/RouteMatch";
|
import { RouteMatch } from "@goauthentik/elements/router/RouteMatch";
|
||||||
import "@goauthentik/elements/router/Router404";
|
import "@goauthentik/elements/router/Router404";
|
||||||
import {
|
|
||||||
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
|
|
||||||
getClient,
|
|
||||||
startBrowserTracingNavigationSpan,
|
|
||||||
startBrowserTracingPageLoadSpan,
|
|
||||||
} from "@sentry/browser";
|
|
||||||
import { Client, Span } from "@sentry/types";
|
|
||||||
|
|
||||||
import { CSSResult, PropertyValues, TemplateResult, css, html } from "lit";
|
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
|
|
||||||
// Poliyfill for hashchange.newURL,
|
// Poliyfill for hashchange.newURL,
|
||||||
@ -60,9 +53,6 @@ export class RouterOutlet extends AKElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
routes: Route[] = [];
|
routes: Route[] = [];
|
||||||
|
|
||||||
private sentryClient?: Client;
|
|
||||||
private pageLoadSpan?: Span;
|
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult[] {
|
||||||
return [
|
return [
|
||||||
css`
|
css`
|
||||||
@ -79,15 +69,6 @@ export class RouterOutlet extends AKElement {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
window.addEventListener("hashchange", (ev: HashChangeEvent) => this.navigate(ev));
|
window.addEventListener("hashchange", (ev: HashChangeEvent) => this.navigate(ev));
|
||||||
this.sentryClient = getClient();
|
|
||||||
if (this.sentryClient) {
|
|
||||||
this.pageLoadSpan = startBrowserTracingPageLoadSpan(this.sentryClient, {
|
|
||||||
name: window.location.pathname,
|
|
||||||
attributes: {
|
|
||||||
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: "url",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated(): void {
|
firstUpdated(): void {
|
||||||
@ -111,8 +92,9 @@ export class RouterOutlet extends AKElement {
|
|||||||
this.routes.some((route) => {
|
this.routes.some((route) => {
|
||||||
const match = route.url.exec(activeUrl);
|
const match = route.url.exec(activeUrl);
|
||||||
if (match !== null) {
|
if (match !== null) {
|
||||||
matchedRoute = new RouteMatch(route, activeUrl);
|
matchedRoute = new RouteMatch(route);
|
||||||
matchedRoute.arguments = match.groups || {};
|
matchedRoute.arguments = match.groups || {};
|
||||||
|
matchedRoute.fullUrl = activeUrl;
|
||||||
console.debug("authentik/router: found match ", matchedRoute);
|
console.debug("authentik/router: found match ", matchedRoute);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -125,31 +107,13 @@ export class RouterOutlet extends AKElement {
|
|||||||
<ak-router-404 url=${activeUrl}></ak-router-404>
|
<ak-router-404 url=${activeUrl}></ak-router-404>
|
||||||
</div>`;
|
</div>`;
|
||||||
});
|
});
|
||||||
matchedRoute = new RouteMatch(route, activeUrl);
|
matchedRoute = new RouteMatch(route);
|
||||||
matchedRoute.arguments = route.url.exec(activeUrl)?.groups || {};
|
matchedRoute.arguments = route.url.exec(activeUrl)?.groups || {};
|
||||||
|
matchedRoute.fullUrl = activeUrl;
|
||||||
}
|
}
|
||||||
this.current = matchedRoute;
|
this.current = matchedRoute;
|
||||||
}
|
}
|
||||||
|
|
||||||
updated(changedProperties: PropertyValues<this>): void {
|
|
||||||
if (!changedProperties.has("current") || !this.current) return;
|
|
||||||
if (!this.sentryClient) return;
|
|
||||||
// https://docs.sentry.io/platforms/javascript/tracing/instrumentation/automatic-instrumentation/#custom-routing
|
|
||||||
if (this.pageLoadSpan) {
|
|
||||||
this.pageLoadSpan.updateName(this.current.sanitizedURL());
|
|
||||||
this.pageLoadSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, "route");
|
|
||||||
this.pageLoadSpan = undefined;
|
|
||||||
} else {
|
|
||||||
startBrowserTracingNavigationSpan(this.sentryClient, {
|
|
||||||
op: "navigation",
|
|
||||||
name: this.current.sanitizedURL(),
|
|
||||||
attributes: {
|
|
||||||
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: "route",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render(): TemplateResult | undefined {
|
render(): TemplateResult | undefined {
|
||||||
return this.current?.render();
|
return this.current?.render();
|
||||||
}
|
}
|
||||||
|
@ -121,7 +121,7 @@ export class SyncStatusCard extends AKElement {
|
|||||||
|
|
||||||
renderSyncStatus(): TemplateResult {
|
renderSyncStatus(): TemplateResult {
|
||||||
if (this.loading) {
|
if (this.loading) {
|
||||||
return html`<ak-empty-state loading></ak-empty-state>`;
|
return html`<ak-empty-state ?loading=${true}></ak-empty-state>`;
|
||||||
}
|
}
|
||||||
if (!this.syncState) {
|
if (!this.syncState) {
|
||||||
return html`${msg("No sync status.")}`;
|
return html`${msg("No sync status.")}`;
|
||||||
|
@ -19,7 +19,7 @@ describe("ak-empty-state", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should render the default loader", async () => {
|
it("should render the default loader", async () => {
|
||||||
render(html`<ak-empty-state loading header=${msg("Loading")}> </ak-empty-state>`);
|
render(html`<ak-empty-state ?loading=${true} header=${msg("Loading")}> </ak-empty-state>`);
|
||||||
|
|
||||||
const empty = await $("ak-empty-state").$(">>>.pf-c-empty-state__icon");
|
const empty = await $("ak-empty-state").$(">>>.pf-c-empty-state__icon");
|
||||||
await expect(empty).toExist();
|
await expect(empty).toExist();
|
||||||
|
@ -139,7 +139,8 @@ export class UserSourceSettingsPage extends AKElement {
|
|||||||
})}
|
})}
|
||||||
`}
|
`}
|
||||||
`
|
`
|
||||||
: html`<ak-empty-state loading header=${msg("Loading")}> </ak-empty-state>`}
|
: html`<ak-empty-state ?loading="${true}" header=${msg("Loading")}>
|
||||||
|
</ak-empty-state>`}
|
||||||
</ul>`;
|
</ul>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,7 +171,6 @@ export class FlowExecutor extends Interface implements StageHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
configureSentry();
|
|
||||||
super();
|
super();
|
||||||
this.ws = new WebsocketClient();
|
this.ws = new WebsocketClient();
|
||||||
const inspector = new URL(window.location.toString()).searchParams.get("inspector");
|
const inspector = new URL(window.location.toString()).searchParams.get("inspector");
|
||||||
@ -238,6 +237,7 @@ export class FlowExecutor extends Interface implements StageHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async firstUpdated(): Promise<void> {
|
async firstUpdated(): Promise<void> {
|
||||||
|
configureSentry();
|
||||||
if (this.config?.capabilities.includes(CapabilitiesEnum.CanDebug)) {
|
if (this.config?.capabilities.includes(CapabilitiesEnum.CanDebug)) {
|
||||||
this.inspectorAvailable = true;
|
this.inspectorAvailable = true;
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,8 @@ export class SessionEnd extends BaseStage<SessionEndChallenge, unknown> {
|
|||||||
|
|
||||||
render(): TemplateResult {
|
render(): TemplateResult {
|
||||||
if (!this.challenge) {
|
if (!this.challenge) {
|
||||||
return html`<ak-empty-state loading header=${msg("Loading")}> </ak-empty-state>`;
|
return html`<ak-empty-state ?loading="${true}" header=${msg("Loading")}>
|
||||||
|
</ak-empty-state>`;
|
||||||
}
|
}
|
||||||
return html`<header class="pf-c-login__main-header">
|
return html`<header class="pf-c-login__main-header">
|
||||||
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
|
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { LightInterface } from "@goauthentik/elements/Interface";
|
import { Interface } from "@goauthentik/elements/Interface";
|
||||||
|
|
||||||
import { msg } from "@lit/localize";
|
import { msg } from "@lit/localize";
|
||||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||||
@ -10,7 +10,7 @@ import PFSpinner from "@patternfly/patternfly/components/Spinner/spinner.css";
|
|||||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||||
|
|
||||||
@customElement("ak-loading")
|
@customElement("ak-loading")
|
||||||
export class Loading extends LightInterface {
|
export class Loading extends Interface {
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult[] {
|
||||||
return [
|
return [
|
||||||
PFBase,
|
PFBase,
|
||||||
@ -25,6 +25,16 @@ export class Loading extends LightInterface {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerContexts(): void {
|
||||||
|
// Stub function to avoid making API requests for things we don't need. The `Interface` base class loads
|
||||||
|
// a bunch of data that is used globally by various things, however this is an interface that is shown
|
||||||
|
// very briefly and we don't need any of that data.
|
||||||
|
}
|
||||||
|
|
||||||
|
async _initCustomCSS(): Promise<void> {
|
||||||
|
// Stub function to avoid fetching custom CSS.
|
||||||
|
}
|
||||||
|
|
||||||
render(): TemplateResult {
|
render(): TemplateResult {
|
||||||
return html` <section
|
return html` <section
|
||||||
class="ak-static-page pf-c-page__main-section pf-m-no-padding-mobile pf-m-xl"
|
class="ak-static-page pf-c-page__main-section pf-m-no-padding-mobile pf-m-xl"
|
||||||
|
@ -102,7 +102,7 @@ export class LibraryPage extends AKElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loading() {
|
loading() {
|
||||||
return html`<ak-empty-state loading header=${msg("Loading")}> </ak-empty-state>`;
|
return html`<ak-empty-state ?loading="${true}" header=${msg("Loading")}> </ak-empty-state>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
running() {
|
running() {
|
||||||
|
@ -281,10 +281,10 @@ export class UserInterface extends AuthenticatedInterface {
|
|||||||
me?: SessionUser;
|
me?: SessionUser;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
configureSentry(true);
|
|
||||||
super();
|
super();
|
||||||
this.ws = new WebsocketClient();
|
this.ws = new WebsocketClient();
|
||||||
this.fetchConfigurationDetails();
|
this.fetchConfigurationDetails();
|
||||||
|
configureSentry(true);
|
||||||
this.toggleNotificationDrawer = this.toggleNotificationDrawer.bind(this);
|
this.toggleNotificationDrawer = this.toggleNotificationDrawer.bind(this);
|
||||||
this.toggleApiDrawer = this.toggleApiDrawer.bind(this);
|
this.toggleApiDrawer = this.toggleApiDrawer.bind(this);
|
||||||
this.fetchConfigurationDetails = this.fetchConfigurationDetails.bind(this);
|
this.fetchConfigurationDetails = this.fetchConfigurationDetails.bind(this);
|
||||||
|
@ -173,7 +173,8 @@ export class UserSettingsFlowExecutor
|
|||||||
level: MessageLevel.success,
|
level: MessageLevel.success,
|
||||||
message: msg("Successfully updated details"),
|
message: msg("Successfully updated details"),
|
||||||
});
|
});
|
||||||
return html`<ak-empty-state loading header=${msg("Loading")}> </ak-empty-state>`;
|
return html`<ak-empty-state ?loading=${true} header=${msg("Loading")}>
|
||||||
|
</ak-empty-state>`;
|
||||||
default:
|
default:
|
||||||
console.debug(
|
console.debug(
|
||||||
`authentik/user/flows: unsupported stage type ${this.challenge.component}`,
|
`authentik/user/flows: unsupported stage type ${this.challenge.component}`,
|
||||||
@ -194,7 +195,8 @@ export class UserSettingsFlowExecutor
|
|||||||
return html`<p>${msg("No settings flow configured.")}</p> `;
|
return html`<p>${msg("No settings flow configured.")}</p> `;
|
||||||
}
|
}
|
||||||
if (!this.challenge || this.loading) {
|
if (!this.challenge || this.loading) {
|
||||||
return html`<ak-empty-state loading header=${msg("Loading")}> </ak-empty-state>`;
|
return html`<ak-empty-state ?loading=${true} header=${msg("Loading")}>
|
||||||
|
</ak-empty-state>`;
|
||||||
}
|
}
|
||||||
return html` ${this.renderChallenge()} `;
|
return html` ${this.renderChallenge()} `;
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,8 @@ export class UserSettingsPromptStage extends PromptStage {
|
|||||||
|
|
||||||
render(): TemplateResult {
|
render(): TemplateResult {
|
||||||
if (!this.challenge) {
|
if (!this.challenge) {
|
||||||
return html`<ak-empty-state loading header=${msg("Loading")}> </ak-empty-state>`;
|
return html`<ak-empty-state ?loading="${true}" header=${msg("Loading")}>
|
||||||
|
</ak-empty-state>`;
|
||||||
}
|
}
|
||||||
return html`<div class="pf-c-login__main-body">
|
return html`<div class="pf-c-login__main-body">
|
||||||
<form
|
<form
|
||||||
|
@ -88,4 +88,4 @@ import Defaultflowlist from "../flow/flow_list/\_defaultflowlist.mdx";
|
|||||||
|
|
||||||
- **Layout**: select how the UI displays the flow when it is executed; with stacked elements, content left or right, and sidebar left or right.
|
- **Layout**: select how the UI displays the flow when it is executed; with stacked elements, content left or right, and sidebar left or right.
|
||||||
|
|
||||||
- **Background**: optionally, select a background image for the UI presentation of the flow. This overrides any default background image configured in the [Branding settings](../../../sys-mgmt/brands.md#branding-settings).
|
- **Background**: optionally, select a background image for the UI presentation of the flow.
|
||||||
|
@ -57,7 +57,7 @@ To bind a stage to a flow, follow these steps:
|
|||||||
|
|
||||||
## Bind users and groups to a flow's stage binding
|
## Bind users and groups to a flow's stage binding
|
||||||
|
|
||||||
You can use bindings to determine whether or not a stage is presented to a single user or any users within a group. You do this by binding the user or group to a stage binding within a specific flow. For example, if you have a flow that contains a stage that prompts the user for multi-factor authentication, but you only want certain users to see this stage (and fulfill the MFA prompt), then you would bind the appropriate group (or single user) to the stage binding for that flow.
|
You can use bindings to determine whehther or not a stage is presented to a single user or any users within a group. You do this by binding the user or group to a stage binding within a specific flow. For example, if you have a flow that contains a stage that prompts the user for multi-factor authentication, but you only want certain users to see this stage (and fulfill the MFA prompt), then you would bind the appropriate group (or single user) to the stage binding for that flow.
|
||||||
|
|
||||||
To bind a user or a group to a stage binding for a specific flow, follow these steps:
|
To bind a user or a group to a stage binding for a specific flow, follow these steps:
|
||||||
|
|
||||||
|
57
website/docs/customize/interfaces/_global/customcss.mdx
Normal file
57
website/docs/customize/interfaces/_global/customcss.mdx
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
### Custom CSS
|
||||||
|
|
||||||
|
To further modify the look of authentik, a custom CSS file can be created. Creating such a file is outside the scope of this document.
|
||||||
|
|
||||||
|
import TabItem from "@theme/TabItem";
|
||||||
|
import Tabs from "@theme/Tabs";
|
||||||
|
|
||||||
|
<Tabs
|
||||||
|
defaultValue="docker-compose"
|
||||||
|
values={[
|
||||||
|
{label: 'docker-compose', value: 'docker-compose'},
|
||||||
|
{label: 'Kubernetes', value: 'kubernetes'},
|
||||||
|
]}>
|
||||||
|
<TabItem value="docker-compose">
|
||||||
|
Create a `docker-compose.override.yml` file and add this block to mount the custom CSS file:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
server:
|
||||||
|
volumes:
|
||||||
|
- ./my-css-file.css:/web/dist/custom.css
|
||||||
|
```
|
||||||
|
|
||||||
|
Afterwards, run the upgrade commands from the latest release notes.
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
<TabItem value="kubernetes">
|
||||||
|
Create a ConfigMap with your css file:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: authentik-custom-css
|
||||||
|
namespace: authentik
|
||||||
|
data:
|
||||||
|
custom.css: |
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, in the helm chart add this to your `values.yaml` file:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
volumes:
|
||||||
|
- name: custom-css
|
||||||
|
configMap:
|
||||||
|
name: authentik-custom-css
|
||||||
|
volumeMounts:
|
||||||
|
- name: custom-css
|
||||||
|
mountPath: /web/dist/custom.css
|
||||||
|
subPath: custom.css
|
||||||
|
```
|
||||||
|
|
||||||
|
Afterwards, run the upgrade commands from the latest release notes.
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
@ -1,3 +1,5 @@
|
|||||||
### Global customization
|
## Global customization
|
||||||
|
|
||||||
See [Brand Settings](../../../sys-mgmt/brands.md#branding-settings)
|
import CustomCSS from "./customcss.mdx";
|
||||||
|
|
||||||
|
<CustomCSS />
|
||||||
|
@ -29,10 +29,9 @@ authentik:
|
|||||||
To store the password and token in a secret, use:
|
To store the password and token in a secret, use:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
global:
|
envFrom:
|
||||||
envFrom:
|
- secretRef:
|
||||||
- secretRef:
|
name: _some-secret_
|
||||||
name: _some-secret_
|
|
||||||
```
|
```
|
||||||
|
|
||||||
where _some-secret_ contains the environment variables as in the documentation above.
|
where _some-secret_ contains the environment variables as in the documentation above.
|
||||||
|
@ -70,8 +70,7 @@ To check if your config has been applied correctly, you can run the following co
|
|||||||
- `AUTHENTIK_POSTGRESQL__USER`: Database user
|
- `AUTHENTIK_POSTGRESQL__USER`: Database user
|
||||||
- `AUTHENTIK_POSTGRESQL__PORT`: Database port, defaults to 5432
|
- `AUTHENTIK_POSTGRESQL__PORT`: Database port, defaults to 5432
|
||||||
- `AUTHENTIK_POSTGRESQL__PASSWORD`: Database password, defaults to the environment variable `POSTGRES_PASSWORD`
|
- `AUTHENTIK_POSTGRESQL__PASSWORD`: Database password, defaults to the environment variable `POSTGRES_PASSWORD`
|
||||||
{/* TODO: Temporarily deactivated feature, see https://github.com/goauthentik/authentik/issues/14320 */}
|
- `AUTHENTIK_POSTGRESQL__USE_POOL`: Use a [connection pool](https://docs.djangoproject.com/en/stable/ref/databases/#connection-pool) for PostgreSQL connections. Defaults to `false`. :ak-version[2025.4]
|
||||||
{/* - `AUTHENTIK_POSTGRESQL__USE_POOL`: Use a [connection pool](https://docs.djangoproject.com/en/stable/ref/databases/#connection-pool) for PostgreSQL connections. Defaults to `false`. :ak-version[2025.4] */}
|
|
||||||
- `AUTHENTIK_POSTGRESQL__POOL_OPTIONS`: Extra configuration to pass to the [ConnectionPool object](https://www.psycopg.org/psycopg3/docs/api/pool.html#psycopg_pool.ConnectionPool) when it is created. Must be a base64-encoded JSON dictionary. Ignored when `USE_POOL` is set to `false`. :ak-version[2025.4]
|
- `AUTHENTIK_POSTGRESQL__POOL_OPTIONS`: Extra configuration to pass to the [ConnectionPool object](https://www.psycopg.org/psycopg3/docs/api/pool.html#psycopg_pool.ConnectionPool) when it is created. Must be a base64-encoded JSON dictionary. Ignored when `USE_POOL` is set to `false`. :ak-version[2025.4]
|
||||||
- `AUTHENTIK_POSTGRESQL__USE_PGBOUNCER`: Adjust configuration to support connection to PgBouncer. Deprecated, see below
|
- `AUTHENTIK_POSTGRESQL__USE_PGBOUNCER`: Adjust configuration to support connection to PgBouncer. Deprecated, see below
|
||||||
- `AUTHENTIK_POSTGRESQL__USE_PGPOOL`: Adjust configuration to support connection to Pgpool. Deprecated, see below
|
- `AUTHENTIK_POSTGRESQL__USE_PGPOOL`: Adjust configuration to support connection to Pgpool. Deprecated, see below
|
||||||
@ -357,7 +356,7 @@ Defaults to `86400`.
|
|||||||
|
|
||||||
### `AUTHENTIK_SESSION_STORAGE`:ak-version[2024.4]
|
### `AUTHENTIK_SESSION_STORAGE`:ak-version[2024.4]
|
||||||
|
|
||||||
Configure if the sessions are stored in the cache or the database. Defaults to `db`. Allowed values are `cache` and `db`. Note that changing this value will invalidate all previous sessions.
|
Configure if the sessions are stored in the cache or the database. Defaults to `cache`. Allowed values are `cache` and `db`. Note that changing this value will invalidate all previous sessions.
|
||||||
|
|
||||||
### `AUTHENTIK_SESSIONS__UNAUTHENTICATED_AGE`:ak-version[2025.4]
|
### `AUTHENTIK_SESSIONS__UNAUTHENTICATED_AGE`:ak-version[2025.4]
|
||||||
|
|
||||||
|
@ -7,8 +7,7 @@ slug: "/releases/2025.4"
|
|||||||
|
|
||||||
- **Improve membership resolution for the LDAP Source** Allow lookups of LDAP group memberships from user attributes as an alternative to lookups from group attributes. This also allows for nested group lookups in Active Directory.
|
- **Improve membership resolution for the LDAP Source** Allow lookups of LDAP group memberships from user attributes as an alternative to lookups from group attributes. This also allows for nested group lookups in Active Directory.
|
||||||
|
|
||||||
<!-- TODO: Temporarily deactivated feature, see https://github.com/goauthentik/authentik/issues/14320 -->
|
- **Support for PostgreSQL Connection Pools** PostgreSQL Connection Pools provides a set of open connections in order to reduce latency.
|
||||||
<!-- - **Support for PostgreSQL Connection Pools** PostgreSQL Connection Pools provides a set of open connections in order to reduce latency. -->
|
|
||||||
|
|
||||||
- **RBAC: Initial Permissions** :ak-preview Provides more flexible access control by assigning permissions to the user/role creating a new object in authentik. Use **Initial Permissions** as a pragmatic way to implement the principle of least privilege.
|
- **RBAC: Initial Permissions** :ak-preview Provides more flexible access control by assigning permissions to the user/role creating a new object in authentik. Use **Initial Permissions** as a pragmatic way to implement the principle of least privilege.
|
||||||
|
|
||||||
@ -65,7 +64,7 @@ Previously, sessions were stored by default in the cache. Now, they are stored i
|
|||||||
|
|
||||||
Reputation scores now have a configurable numerical limit in addition to the [already existing temporal limit](https://docs.goauthentik.io/docs/install-config/configuration/#authentik_reputation_expiry).
|
Reputation scores now have a configurable numerical limit in addition to the [already existing temporal limit](https://docs.goauthentik.io/docs/install-config/configuration/#authentik_reputation_expiry).
|
||||||
|
|
||||||
<!-- - **Support for PostgreSQL Connection Pools**: See [description](#highlights) under Highlights. Refer to our [documentation](../../install-config/configuration/configuration.mdx). -->
|
- **Support for PostgreSQL Connection Pools**: See [description](#highlights) under Highlights. Refer to our [documentation](../../install-config/configuration/configuration.mdx).
|
||||||
|
|
||||||
- **Password History Policy**: See [description](#highlights) under Highlights. Refer to our [documentation](../../customize/policies/unique_password.md).
|
- **Password History Policy**: See [description](#highlights) under Highlights. Refer to our [documentation](../../customize/policies/unique_password.md).
|
||||||
|
|
||||||
|
@ -23,14 +23,11 @@ The brand settings define the visual identity of the brand, including:
|
|||||||
|
|
||||||
- **Branding title**: Displayed in the browser tab (document title) and throughout the UI;
|
- **Branding title**: Displayed in the browser tab (document title) and throughout the UI;
|
||||||
- **Logo**: Appears in the sidebar/header;
|
- **Logo**: Appears in the sidebar/header;
|
||||||
|
|
||||||
:::info
|
|
||||||
Starting with authentik 2024.6.2, the placeholder `%(theme)s` can be used in the logo configuration option, which will be replaced with the active theme.
|
|
||||||
:::
|
|
||||||
|
|
||||||
- **Favicon**: Shown on the browser tab.
|
- **Favicon**: Shown on the browser tab.
|
||||||
- **Default flow background** :ak-version[2025.4]: Default background image for the flow executor, can be overridden per flow, see [Flow configuration options](../add-secure-apps/flows-stages/flow/index.md#flow-configuration-options).
|
|
||||||
- **Custom CSS** :ak-version[2025.4]: Add custom CSS to further customize the look of authentik. Creating such a file is outside the scope of this document.
|
:::info
|
||||||
|
Starting with authentik 2024.6.2, the placeholder `%(theme)s` can be used in the logo configuration option, which will be replaced with the active theme.
|
||||||
|
:::
|
||||||
|
|
||||||
### External user settings
|
### External user settings
|
||||||
|
|
||||||
|
@ -4,9 +4,12 @@
|
|||||||
* @import * as Preset from "@docusaurus/preset-classic";
|
* @import * as Preset from "@docusaurus/preset-classic";
|
||||||
* @import * as OpenApiPlugin from "docusaurus-plugin-openapi-docs";
|
* @import * as OpenApiPlugin from "docusaurus-plugin-openapi-docs";
|
||||||
* @import { BuildUrlValues } from "remark-github";
|
* @import { BuildUrlValues } from "remark-github";
|
||||||
|
* @import { ReleasesPluginOptions } from "./releases/plugin.mjs"
|
||||||
*/
|
*/
|
||||||
import { createDocusaurusConfig } from "@goauthentik/docusaurus-config";
|
import { createDocusaurusConfig } from "@goauthentik/docusaurus-config";
|
||||||
import { createRequire } from "node:module";
|
import { createRequire } from "node:module";
|
||||||
|
import * as path from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
import remarkDirective from "remark-directive";
|
import remarkDirective from "remark-directive";
|
||||||
import remarkGithub, { defaultBuildUrl } from "remark-github";
|
import remarkGithub, { defaultBuildUrl } from "remark-github";
|
||||||
|
|
||||||
@ -15,6 +18,7 @@ import remarkPreviewDirective from "./remark/preview-directive.mjs";
|
|||||||
import remarkSupportDirective from "./remark/support-directive.mjs";
|
import remarkSupportDirective from "./remark/support-directive.mjs";
|
||||||
import remarkVersionDirective from "./remark/version-directive.mjs";
|
import remarkVersionDirective from "./remark/version-directive.mjs";
|
||||||
|
|
||||||
|
const __dirname = fileURLToPath(new URL(".", import.meta.url));
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -131,6 +135,12 @@ const config = createDocusaurusConfig({
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
|
[
|
||||||
|
"./releases/plugin.mjs",
|
||||||
|
/** @type {ReleasesPluginOptions} */ ({
|
||||||
|
docsDirectory: path.join(__dirname, "docs"),
|
||||||
|
}),
|
||||||
|
],
|
||||||
[
|
[
|
||||||
"@docusaurus/plugin-content-docs",
|
"@docusaurus/plugin-content-docs",
|
||||||
{
|
{
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
---
|
|
||||||
title: Integrate with Coder
|
|
||||||
sidebar_label: Coder
|
|
||||||
support_level: community
|
|
||||||
---
|
|
||||||
|
|
||||||
## What is Coder
|
|
||||||
|
|
||||||
> Coder is an open-source platform that provides browser-based cloud development environments, enabling developers and teams to securely write, edit, and manage code remotely without the need for local setup.
|
|
||||||
>
|
|
||||||
> -- https://coder.com
|
|
||||||
|
|
||||||
## Preparation
|
|
||||||
|
|
||||||
The following placeholders are used in this guide:
|
|
||||||
|
|
||||||
- `coder.company` is the FQDN of your Coder installation.
|
|
||||||
- `authentik.company` is the FQDN of the authentik installation.
|
|
||||||
|
|
||||||
:::note
|
|
||||||
This documentation lists only the settings that you need to change from their default values. Be aware that any changes other than those explicitly mentioned in this guide could cause issues accessing your application.
|
|
||||||
:::
|
|
||||||
|
|
||||||
## authentik configuration
|
|
||||||
|
|
||||||
To support the integration of Coder with authentik, you need to create an application/provider pair in authentik.
|
|
||||||
|
|
||||||
### Create an application and provider in authentik
|
|
||||||
|
|
||||||
1. Log in to authentik as an admin, and open the authentik Admin interface.
|
|
||||||
2. Navigate to **Applications** > **Applications** and click **Create with Provider** to create an application and provider pair. (Alternatively you can first create a provider separately, then create the application and connect it with the provider.)
|
|
||||||
|
|
||||||
- **Application**: provide a descriptive name, an optional group for the type of application, the policy engine mode, and optional UI settings.
|
|
||||||
- **Choose a Provider type**: select **OAuth2/OpenID Connect** as the provider type.
|
|
||||||
- **Configure the Provider**: provide a name (or accept the auto-provided name), the authorization flow to use for this provider, and the following required configurations.
|
|
||||||
- Note the **Client ID**,**Client Secret**, and **slug** values because they will be required later.
|
|
||||||
- Set a `Strict` redirect URI to `https://coder.company/api/v2/users/oidc/callback`.
|
|
||||||
- Select any available signing key.
|
|
||||||
- **Configure Bindings** _(optional)_: you can create a [binding](/docs/add-secure-apps/flows-stages/bindings/) (policy, group, or user) to manage the listing and access to applications on a user's **My applications** page.
|
|
||||||
|
|
||||||
3. Click **Submit** to save the new application and provider.
|
|
||||||
|
|
||||||
## Coder configuration
|
|
||||||
|
|
||||||
To support the integration of Coder with authentik, add the following environment variables to your Coder deployment:
|
|
||||||
|
|
||||||
```yaml showLineNumbers
|
|
||||||
CODER_OIDC_ISSUER_URL=https://authentik.company/application/o/<application slug>/
|
|
||||||
CODER_OIDC_EMAIL_DOMAIN=acme.company,acme-corp.company
|
|
||||||
CODER_OIDC_CLIENT_ID=<Client ID from authentik>
|
|
||||||
CODER_OIDC_CLIENT_SECRET=<Client secret from authentik>
|
|
||||||
CODER_OIDC_SIGN_IN_TEXT=Log in with authentik
|
|
||||||
CODER_OIDC_ICON_URL=https://authentik.company/static/dist/assets/icons/icon.png
|
|
||||||
```
|
|
||||||
|
|
||||||
## Resources
|
|
||||||
|
|
||||||
- [Coder OIDC authentication documentatiom](https://coder.com/docs/admin/users/oidc-auth/)
|
|
||||||
|
|
||||||
## Configuration verification
|
|
||||||
|
|
||||||
To confirm that authentik is properly configured with Coder, log out and attempt to log back in by clicking **Log in with authentik**.
|
|
@ -129,8 +129,6 @@ environment:
|
|||||||
GF_AUTH_OAUTH_AUTO_LOGIN: "true"
|
GF_AUTH_OAUTH_AUTO_LOGIN: "true"
|
||||||
# Optionally map user groups to Grafana roles
|
# Optionally map user groups to Grafana roles
|
||||||
GF_AUTH_GENERIC_OAUTH_ROLE_ATTRIBUTE_PATH: "contains(groups, 'Grafana Admins') && 'Admin' || contains(groups, 'Grafana Editors') && 'Editor' || 'Viewer'"
|
GF_AUTH_GENERIC_OAUTH_ROLE_ATTRIBUTE_PATH: "contains(groups, 'Grafana Admins') && 'Admin' || contains(groups, 'Grafana Editors') && 'Editor' || 'Viewer'"
|
||||||
# Required if Grafana is running behind a reverse proxy
|
|
||||||
GF_SERVER_ROOT_URL: "https://grafana.company"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
</TabItem>
|
</TabItem>
|
||||||
@ -196,8 +194,7 @@ For more information on group/role mappings, see [Grafana's docs](https://grafan
|
|||||||
|
|
||||||
### Grafana Configuration Considerations
|
### Grafana Configuration Considerations
|
||||||
|
|
||||||
To ensure redirects work correctly in Grafana, make sure the `root_url` in your configuration accurately reflects how users access Grafana through your reverse proxy. For example, if your Grafana instance is behind a proxy and accessed at `https://grafana.company`, set `root_url` to `https://grafana.company`. This ensures that OAuth and other redirects use the correct URL, such as `https://grafana.company/login/generic_oauth`, instead of defaulting to something like `localhost:3000`.
|
Make sure in your configuration that `root_url` is set correctly, otherwise your redirect url might get processed incorrectly. For example, if your grafana instance is running on the default configuration and is accessible behind a reverse proxy at `https://grafana.company`, your redirect url will end up looking like this, `https://grafana.company/`.
|
||||||
|
|
||||||
If you get `user does not belong to org` error when trying to log into grafana for the first time via OAuth, check if you have an organization with the ID of `1`, if not, then you have to add the following to your grafana config:
|
If you get `user does not belong to org` error when trying to log into grafana for the first time via OAuth, check if you have an organization with the ID of `1`, if not, then you have to add the following to your grafana config:
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
|
@ -30,14 +30,12 @@ To support the integration of Paperless-ngx with authentik, you need to create a
|
|||||||
1. Log in to authentik as an admin, and open the authentik Admin interface.
|
1. Log in to authentik as an admin, and open the authentik Admin interface.
|
||||||
2. Navigate to **Applications** > **Applications** and click **Create with Provider** to create an application and provider pair. (Alternatively you can first create a provider separately, then create the application and connect it with the provider.)
|
2. Navigate to **Applications** > **Applications** and click **Create with Provider** to create an application and provider pair. (Alternatively you can first create a provider separately, then create the application and connect it with the provider.)
|
||||||
|
|
||||||
- **Application**: provide a descriptive name, an optional group for the type of application, the policy engine mode, and optional UI settings.
|
- **Application**: provide a descriptive name, an optional group for the type of application, the policy engine mode, and optional UI settings.
|
||||||
- **Choose a Provider type**: select **OAuth2/OpenID Connect** as the provider type.
|
- **Choose a Provider type**: select **OAuth2/OpenID Connect** as the provider type.
|
||||||
- **Configure the Provider**: provide a name (or accept the auto-provided name), the authorization flow to use for this provider, and the following required configurations.
|
- **Configure the Provider**: provide a name (or accept the auto-provided name), the authorization flow to use for this provider, and the following required configurations.
|
||||||
- Note the **Client ID**,**Client Secret**, and **slug** values because they will be required later.
|
- Note the **Client ID**,**Client Secret**, and **slug** values because they will be required later.
|
||||||
- Set a `Strict` redirect URI to <kbd>https://<em>paperless.company</em>/accounts/oidc/authentik/login/callback/</kbd>.
|
- Set a `Strict` redirect URI to <kbd>https://<em>paperless.company</em>/accounts/oidc/authentik/login/callback/</kbd>.
|
||||||
- **Configure Bindings** _(optional)_: you can create a [binding](/docs/add-secure-apps/flows-stages/bindings/) (policy, group, or user) to manage the listing and access to applications on a user's **My applications** page.
|
- **Configure Bindings** _(optional)_: you can create a [binding](/docs/add-secure-apps/flows-stages/bindings/) (policy, group, or user) to manage the listing and access to applications on a user's **My applications** page.
|
||||||
- **Advanced protocol settings**:
|
|
||||||
- **Selected Scopes**: Additionally select the `authentik default OAuth Mapping: OpenID 'openid'` scope.
|
|
||||||
|
|
||||||
3. Click **Submit** to save the new application and provider.
|
3. Click **Submit** to save the new application and provider.
|
||||||
|
|
||||||
|
@ -61,7 +61,6 @@ Add the `oidc_providers` configuration:
|
|||||||
"name_claim": "name",
|
"name_claim": "name",
|
||||||
"email_claim": "email",
|
"email_claim": "email",
|
||||||
"scopes": ["openid", "profile", "email"]
|
"scopes": ["openid", "profile", "email"]
|
||||||
}
|
|
||||||
},
|
},
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: Integrate with SonarQube
|
title: Integrate with SonarQube
|
||||||
sidebar_label: SonarQube
|
sidebar_label: SonarQube
|
||||||
support_level: community
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<span className="badge badge--primary">Support level: Community</span>
|
||||||
|
|
||||||
## What is SonarQube
|
## What is SonarQube
|
||||||
|
|
||||||
> Self-managed static analysis tool for continuous codebase inspection
|
> Self-managed static analysis tool for continuous codebase inspection
|
||||||
|
@ -6,7 +6,7 @@ support_level: community
|
|||||||
|
|
||||||
## What is Service-Name
|
## What is Service-Name
|
||||||
|
|
||||||
> Insert a quick overview of what `<service-name>` is and what it does. Simply describe the product and what it is, how it is used, and do not include marketing or sales-oriented content.
|
> Insert a quick overview of what Service Name is and what it does. Simply describe the product and what it is, how it is used, and do not include marketing or sales-oriented content.
|
||||||
>
|
>
|
||||||
> -- https://service.xyz
|
> -- https://service.xyz
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ support_level: community
|
|||||||
|
|
||||||
The following placeholders are used in this guide:
|
The following placeholders are used in this guide:
|
||||||
|
|
||||||
- `service.company` is the FQDN of the `<service-name>` installation. (Remove this for SaaS)
|
- `service.company` is the FQDN of the Service installation. (Remove this for SaaS)
|
||||||
- `authentik.company` is the FQDN of the authentik installation.
|
- `authentik.company` is the FQDN of the authentik installation.
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
@ -23,7 +23,7 @@ This documentation lists only the settings that you need to change from their de
|
|||||||
|
|
||||||
## authentik configuration
|
## authentik configuration
|
||||||
|
|
||||||
To support the integration of `<service-name>` with authentik, you need to create an application/provider pair in authentik.
|
To support the integration of _Service_ with authentik, you need to create an application/provider pair in authentik.
|
||||||
|
|
||||||
_Any specific info about this integration can go here._
|
_Any specific info about this integration can go here._
|
||||||
|
|
||||||
@ -32,12 +32,13 @@ _Any specific info about this integration can go here._
|
|||||||
1. Log in to authentik as an admin, and open the authentik Admin interface.
|
1. Log in to authentik as an admin, and open the authentik Admin interface.
|
||||||
2. Navigate to **Applications** > **Applications** and click **Create with Provider** to create an application and provider pair. (Alternatively you can first create a provider separately, then create the application and connect it with the provider.)
|
2. Navigate to **Applications** > **Applications** and click **Create with Provider** to create an application and provider pair. (Alternatively you can first create a provider separately, then create the application and connect it with the provider.)
|
||||||
|
|
||||||
- **Application**: provide a descriptive name, an optional group for the type of application, the policy engine mode, and optional UI settings.
|
- **Application**: provide a descriptive name, an optional group for the type of application, the policy engine mode, and optional UI settings.
|
||||||
- _If there are any specific settings required, list them here. Refer to the [ownCloud integration documentation](https://github.com/goauthentik/authentik/blob/main/website/integrations/services/owncloud/index.md) for a complex requirements example._
|
- _If there are any specific settings required, list them here. Refer to the [ownCloud integration documentation](https://github.com/goauthentik/authentik/blob/main/website/integrations/services/owncloud/index.md) for a complex requirements example._
|
||||||
- **Choose a Provider type**: _If there is a specific provider type required, state that here._
|
- **Choose a Provider type**
|
||||||
- **Configure the Provider**: provide a name (or accept the auto-provided name), the authorization flow to use for this provider, and the following required configurations.
|
- _If there is a specific provider type required, state that here._
|
||||||
- _If there are any specific settings required, list them here. Refer to the [ownCloud integration documentation](https://github.com/goauthentik/authentik/blob/main/website/integrations/services/owncloud/index.md) for a complex requirements example._
|
- **Configure the Provider**: provide a name (or accept the auto-provided name), the authorization flow to use for this provider, and the following required configurations.
|
||||||
- **Configure Bindings** _(optional)_: you can create a [binding](/docs/add-secure-apps/flows-stages/bindings/) (policy, group, or user) to manage the listing and access to applications on a user's **My applications** page.
|
- _If there are any specific settings required, list them here. Refer to the [ownCloud integration documentation](https://github.com/goauthentik/authentik/blob/main/website/integrations/services/owncloud/index.md) for a complex requirements example._
|
||||||
|
- **Configure Bindings** _(optional)_: you can create a [binding](/docs/add-secure-apps/flows-stages/bindings/) (policy, group, or user) to manage the listing and access to applications on a user's **My applications** page.
|
||||||
|
|
||||||
3. Click **Submit** to save the new application and provider.
|
3. Click **Submit** to save the new application and provider.
|
||||||
|
|
||||||
@ -51,6 +52,6 @@ Insert Service configuration
|
|||||||
|
|
||||||
## Configuration verification
|
## Configuration verification
|
||||||
|
|
||||||
Template sentence that you can typically use here: "To confirm that authentik is properly configured with `<service-name>`, log out and log back in via authentik."
|
Template sentence that you can typically use here: "To confirm that authentik is properly configured with _Service Name_, log out and log back in via authentik."
|
||||||
|
|
||||||
If there are more specific validation methods for the Service (e.g., clicking a button), include these instructions for clarity.
|
If there are more specific validation methods for the Service (e.g., clicking a button), include these instructions for clarity.
|
||||||
|
57
website/package-lock.json
generated
57
website/package-lock.json
generated
@ -22,7 +22,6 @@
|
|||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"docusaurus-plugin-openapi-docs": "^4.4.0",
|
"docusaurus-plugin-openapi-docs": "^4.4.0",
|
||||||
"docusaurus-theme-openapi-docs": "^4.4.0",
|
"docusaurus-theme-openapi-docs": "^4.4.0",
|
||||||
"lightningcss-linux-x64-gnu": "1.30.1",
|
|
||||||
"postcss": "^8.5.3",
|
"postcss": "^8.5.3",
|
||||||
"prism-react-renderer": "^2.4.1",
|
"prism-react-renderer": "^2.4.1",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
@ -30,7 +29,7 @@
|
|||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"remark-directive": "^4.0.0",
|
"remark-directive": "^4.0.0",
|
||||||
"remark-github": "^12.0.0",
|
"remark-github": "^12.0.0",
|
||||||
"semver": "^7.7.2"
|
"semver": "^7.7.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@docusaurus/module-type-aliases": "^3.7.0",
|
"@docusaurus/module-type-aliases": "^3.7.0",
|
||||||
@ -51,18 +50,18 @@
|
|||||||
"node": ">=22.14.0"
|
"node": ">=22.14.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@rspack/binding-darwin-arm64": "1.3.10",
|
"@rspack/binding-darwin-arm64": "1.3.8",
|
||||||
"@rspack/binding-linux-arm64-gnu": "1.3.10",
|
"@rspack/binding-linux-arm64-gnu": "1.3.8",
|
||||||
"@rspack/binding-linux-x64-gnu": "1.3.10",
|
"@rspack/binding-linux-x64-gnu": "1.3.8",
|
||||||
"@swc/core-darwin-arm64": "1.11.24",
|
"@swc/core-darwin-arm64": "1.11.24",
|
||||||
"@swc/core-linux-arm64-gnu": "1.11.24",
|
"@swc/core-linux-arm64-gnu": "1.11.24",
|
||||||
"@swc/core-linux-x64-gnu": "1.11.24",
|
"@swc/core-linux-x64-gnu": "1.11.24",
|
||||||
"@swc/html-darwin-arm64": "1.11.24",
|
"@swc/html-darwin-arm64": "1.11.24",
|
||||||
"@swc/html-linux-arm64-gnu": "1.11.24",
|
"@swc/html-linux-arm64-gnu": "1.11.24",
|
||||||
"@swc/html-linux-x64-gnu": "1.11.24",
|
"@swc/html-linux-x64-gnu": "1.11.24",
|
||||||
"lightningcss-darwin-arm64": "1.30.1",
|
"lightningcss-darwin-arm64": "1.29.3",
|
||||||
"lightningcss-linux-arm64-gnu": "1.30.1",
|
"lightningcss-linux-arm64-gnu": "1.29.3",
|
||||||
"lightningcss-linux-x64-gnu": "1.30.1"
|
"lightningcss-linux-x64-gnu": "1.29.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@algolia/autocomplete-core": {
|
"node_modules/@algolia/autocomplete-core": {
|
||||||
@ -4676,9 +4675,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rspack/binding-darwin-arm64": {
|
"node_modules/@rspack/binding-darwin-arm64": {
|
||||||
"version": "1.3.10",
|
"version": "1.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-1.3.10.tgz",
|
"resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-1.3.8.tgz",
|
||||||
"integrity": "sha512-0k/j8OeMSVm5u5Nzckp9Ie7S7hprnvNegebnGr+L6VCyD7sMqm4m+4rLHs99ZklYdH0dZtY2+LrzrtjUZCqfew==",
|
"integrity": "sha512-FlfWZzwCxDfLwyiqGaCSINHt2Er1Wno9xZrf2QM7Ss00HyocPo4BUYGYBEi4dai/fPFoeYKeEAdsNdrVmFH4+g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -4703,9 +4702,9 @@
|
|||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/@rspack/binding-linux-arm64-gnu": {
|
"node_modules/@rspack/binding-linux-arm64-gnu": {
|
||||||
"version": "1.3.10",
|
"version": "1.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.3.10.tgz",
|
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.3.8.tgz",
|
||||||
"integrity": "sha512-zhF5ZNaT/7pxrm8xD3dWG1b4x+FO3LbVeZZG448CjpSo5T57kPD+SaGUU1GcPpn6mexf795x0SVS49aH7/e3Dg==",
|
"integrity": "sha512-PU9fv8knPvbxQb8NrDmTrLVpy8QY0vuhzk69/ZuLRW89c0P14HovYeHV+38cQHho4++avUQgVp6vnJI9vSQjtg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -4730,9 +4729,9 @@
|
|||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/@rspack/binding-linux-x64-gnu": {
|
"node_modules/@rspack/binding-linux-x64-gnu": {
|
||||||
"version": "1.3.10",
|
"version": "1.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.3.10.tgz",
|
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.3.8.tgz",
|
||||||
"integrity": "sha512-FMSi28VZhXMr15picOHFynULhqZ/FODPxRIS6uNrvPRYcbNuiO1v+VHV6X88mhOMmJ/aVF6OwjUO/o2l1FVa9Q==",
|
"integrity": "sha512-48hfwVsD2/Caa0HgZiqE1T20H89cnomcaP92++x8t4IQ2uKA9xCeBW87RD/AaKXcb78aM987ctE+asKjN8OVjw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -14469,9 +14468,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss-darwin-arm64": {
|
"node_modules/lightningcss-darwin-arm64": {
|
||||||
"version": "1.30.1",
|
"version": "1.29.3",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.3.tgz",
|
||||||
"integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==",
|
"integrity": "sha512-fb7raKO3pXtlNbQbiMeEu8RbBVHnpyqAoxTyTRMEWFQWmscGC2wZxoHzZ+YKAepUuKT9uIW5vL2QbFivTgprZg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -14549,9 +14548,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss-linux-arm64-gnu": {
|
"node_modules/lightningcss-linux-arm64-gnu": {
|
||||||
"version": "1.30.1",
|
"version": "1.29.3",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.3.tgz",
|
||||||
"integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==",
|
"integrity": "sha512-Pqau7jtgJNmQ/esugfmAT1aCFy/Gxc92FOxI+3n+LbMHBheBnk41xHDhc0HeYlx9G0xP5tK4t0Koy3QGGNqypw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -14589,9 +14588,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightningcss-linux-x64-gnu": {
|
"node_modules/lightningcss-linux-x64-gnu": {
|
||||||
"version": "1.30.1",
|
"version": "1.29.3",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.3.tgz",
|
||||||
"integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==",
|
"integrity": "sha512-ySZTNCpbfbK8rqpKJeJR2S0g/8UqqV3QnzcuWvpI60LWxnFN91nxpSSwCbzfOXkzKfar9j5eOuOplf+klKtINg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -22638,9 +22637,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/semver": {
|
"node_modules/semver": {
|
||||||
"version": "7.7.2",
|
"version": "7.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
|
||||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"bin": {
|
"bin": {
|
||||||
"semver": "bin/semver.js"
|
"semver": "bin/semver.js"
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"remark-directive": "^4.0.0",
|
"remark-directive": "^4.0.0",
|
||||||
"remark-github": "^12.0.0",
|
"remark-github": "^12.0.0",
|
||||||
"semver": "^7.7.2"
|
"semver": "^7.7.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@docusaurus/module-type-aliases": "^3.7.0",
|
"@docusaurus/module-type-aliases": "^3.7.0",
|
||||||
@ -60,18 +60,18 @@
|
|||||||
"typescript": "~5.8.2"
|
"typescript": "~5.8.2"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@rspack/binding-darwin-arm64": "1.3.10",
|
"@rspack/binding-darwin-arm64": "1.3.8",
|
||||||
"@rspack/binding-linux-arm64-gnu": "1.3.10",
|
"@rspack/binding-linux-arm64-gnu": "1.3.8",
|
||||||
"@rspack/binding-linux-x64-gnu": "1.3.10",
|
"@rspack/binding-linux-x64-gnu": "1.3.8",
|
||||||
"@swc/core-darwin-arm64": "1.11.24",
|
"@swc/core-darwin-arm64": "1.11.24",
|
||||||
"@swc/core-linux-arm64-gnu": "1.11.24",
|
"@swc/core-linux-arm64-gnu": "1.11.24",
|
||||||
"@swc/core-linux-x64-gnu": "1.11.24",
|
"@swc/core-linux-x64-gnu": "1.11.24",
|
||||||
"@swc/html-darwin-arm64": "1.11.24",
|
"@swc/html-darwin-arm64": "1.11.24",
|
||||||
"@swc/html-linux-arm64-gnu": "1.11.24",
|
"@swc/html-linux-arm64-gnu": "1.11.24",
|
||||||
"@swc/html-linux-x64-gnu": "1.11.24",
|
"@swc/html-linux-x64-gnu": "1.11.24",
|
||||||
"lightningcss-darwin-arm64": "1.30.1",
|
"lightningcss-darwin-arm64": "1.29.3",
|
||||||
"lightningcss-linux-arm64-gnu": "1.30.1",
|
"lightningcss-linux-arm64-gnu": "1.29.3",
|
||||||
"lightningcss-linux-x64-gnu": "1.30.1"
|
"lightningcss-linux-x64-gnu": "1.29.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=22.14.0"
|
"node": ">=22.14.0"
|
||||||
|
64
website/releases/plugin.mjs
Normal file
64
website/releases/plugin.mjs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* @file Docusaurus releases plugin.
|
||||||
|
*
|
||||||
|
* @import { LoadContext, Plugin } from "@docusaurus/types"
|
||||||
|
*/
|
||||||
|
import * as fs from "node:fs/promises";
|
||||||
|
import * as path from "node:path";
|
||||||
|
|
||||||
|
import { collectReleaseFiles } from "./utils.mjs";
|
||||||
|
|
||||||
|
const PLUGIN_NAME = "ak-releases-plugin";
|
||||||
|
const RELEASES_FILENAME = "releases.gen.json";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {object} ReleasesPluginOptions
|
||||||
|
* @property {string} docsDirectory The path to the documentation directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {object} AKReleasesPluginData
|
||||||
|
* @property {string} publicPath The URL to the plugin's public directory.
|
||||||
|
* @property {string[]} releases The available versions of the documentation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {LoadContext} loadContext
|
||||||
|
* @param {ReleasesPluginOptions} options
|
||||||
|
* @returns {Promise<Plugin<AKReleasesPluginData>>}
|
||||||
|
*/
|
||||||
|
async function akReleasesPlugin(loadContext, { docsDirectory }) {
|
||||||
|
return {
|
||||||
|
name: PLUGIN_NAME,
|
||||||
|
|
||||||
|
async loadContent() {
|
||||||
|
console.log(`🚀 ${PLUGIN_NAME} loaded`);
|
||||||
|
|
||||||
|
const releases = collectReleaseFiles(docsDirectory).map((release) => release.name);
|
||||||
|
|
||||||
|
const outputPath = path.join(loadContext.siteDir, "static", RELEASES_FILENAME);
|
||||||
|
|
||||||
|
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
||||||
|
await fs.writeFile(outputPath, JSON.stringify(releases, null, 2), "utf-8");
|
||||||
|
console.log(`✅ ${RELEASES_FILENAME} generated`);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {AKReleasesPluginData}
|
||||||
|
*/
|
||||||
|
const content = {
|
||||||
|
releases,
|
||||||
|
publicPath: path.join("/", RELEASES_FILENAME),
|
||||||
|
};
|
||||||
|
|
||||||
|
return content;
|
||||||
|
},
|
||||||
|
|
||||||
|
contentLoaded({ content, actions }) {
|
||||||
|
const { setGlobalData } = actions;
|
||||||
|
|
||||||
|
setGlobalData(content);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default akReleasesPlugin;
|
69
website/releases/utils.mjs
Normal file
69
website/releases/utils.mjs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* @file Docusaurus release utils.
|
||||||
|
*
|
||||||
|
* @import { SidebarItemConfig } from "@docusaurus/plugin-content-docs-types"
|
||||||
|
*/
|
||||||
|
import FastGlob from "fast-glob";
|
||||||
|
import * as path from "node:path";
|
||||||
|
import { coerce } from "semver";
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} releasesParentDirectory
|
||||||
|
* @returns {FastGlob.Entry[]}
|
||||||
|
*/
|
||||||
|
export function collectReleaseFiles(releasesParentDirectory) {
|
||||||
|
const releaseFiles = FastGlob.sync("releases/**/v*.{md,mdx}", {
|
||||||
|
cwd: releasesParentDirectory,
|
||||||
|
onlyFiles: true,
|
||||||
|
objectMode: true,
|
||||||
|
})
|
||||||
|
.map((fileEntry) => {
|
||||||
|
return {
|
||||||
|
...fileEntry,
|
||||||
|
path: fileEntry.path.replace(/\.mdx?$/, ""),
|
||||||
|
name: fileEntry.name.replace(/^v/, "").replace(/\.mdx?$/, ""),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => {
|
||||||
|
const aSemVer = coerce(a.name);
|
||||||
|
const bSemVer = coerce(b.name);
|
||||||
|
|
||||||
|
if (aSemVer && bSemVer) {
|
||||||
|
return bSemVer.compare(aSemVer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.name.localeCompare(a.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
return releaseFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SUPPORTED_RELEASE_COUNT = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {FastGlob.Entry[]} releaseFiles
|
||||||
|
*/
|
||||||
|
export function createReleaseSidebarEntries(releaseFiles) {
|
||||||
|
/**
|
||||||
|
* @type {SidebarItemConfig[]}
|
||||||
|
*/
|
||||||
|
let sidebarEntries = releaseFiles.map((fileEntry) => {
|
||||||
|
return path.join(fileEntry.path);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (releaseFiles.length > SUPPORTED_RELEASE_COUNT) {
|
||||||
|
// Then we add the rest of the releases as a category.
|
||||||
|
sidebarEntries = [
|
||||||
|
...sidebarEntries.slice(0, SUPPORTED_RELEASE_COUNT),
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
label: "Previous versions",
|
||||||
|
items: sidebarEntries.slice(SUPPORTED_RELEASE_COUNT),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return sidebarEntries;
|
||||||
|
}
|
@ -3,73 +3,20 @@
|
|||||||
*
|
*
|
||||||
* @import { SidebarItemConfig } from "@docusaurus/plugin-content-docs-types"
|
* @import { SidebarItemConfig } from "@docusaurus/plugin-content-docs-types"
|
||||||
*/
|
*/
|
||||||
import apiReference from "../docs/developer-docs/api/reference/sidebar";
|
import * as path from "node:path";
|
||||||
import { generateVersionDropdown } from "../src/utils.js";
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
/**
|
import apiReference from "../docs/developer-docs/api/reference/sidebar";
|
||||||
* @type {SidebarItemConfig[]}
|
import { collectReleaseFiles, createReleaseSidebarEntries } from "../releases/utils.mjs";
|
||||||
*/
|
|
||||||
const releases = [
|
const __dirname = fileURLToPath(new URL(".", import.meta.url));
|
||||||
"releases/2025/v2025.4",
|
|
||||||
"releases/2025/v2025.2",
|
const releases = collectReleaseFiles(path.join(__dirname, "..", "docs"));
|
||||||
"releases/2024/v2024.12",
|
|
||||||
{
|
|
||||||
type: "category",
|
|
||||||
label: "Previous versions",
|
|
||||||
items: [
|
|
||||||
"releases/2024/v2024.10",
|
|
||||||
"releases/2024/v2024.8",
|
|
||||||
"releases/2024/v2024.6",
|
|
||||||
"releases/2024/v2024.4",
|
|
||||||
"releases/2024/v2024.2",
|
|
||||||
"releases/2023/v2023.10",
|
|
||||||
"releases/2023/v2023.8",
|
|
||||||
"releases/2023/v2023.6",
|
|
||||||
"releases/2023/v2023.5",
|
|
||||||
"releases/2023/v2023.4",
|
|
||||||
"releases/2023/v2023.3",
|
|
||||||
"releases/2023/v2023.2",
|
|
||||||
"releases/2023/v2023.1",
|
|
||||||
"releases/2022/v2022.12",
|
|
||||||
"releases/2022/v2022.11",
|
|
||||||
"releases/2022/v2022.10",
|
|
||||||
"releases/2022/v2022.9",
|
|
||||||
"releases/2022/v2022.8",
|
|
||||||
"releases/2022/v2022.7",
|
|
||||||
"releases/2022/v2022.6",
|
|
||||||
"releases/2022/v2022.5",
|
|
||||||
"releases/2022/v2022.4",
|
|
||||||
"releases/2022/v2022.2",
|
|
||||||
"releases/2022/v2022.1",
|
|
||||||
"releases/2021/v2021.12",
|
|
||||||
"releases/2021/v2021.10",
|
|
||||||
"releases/2021/v2021.9",
|
|
||||||
"releases/2021/v2021.8",
|
|
||||||
"releases/2021/v2021.7",
|
|
||||||
"releases/2021/v2021.6",
|
|
||||||
"releases/2021/v2021.5",
|
|
||||||
"releases/2021/v2021.4",
|
|
||||||
"releases/2021/v2021.3",
|
|
||||||
"releases/2021/v2021.2",
|
|
||||||
"releases/2021/v2021.1",
|
|
||||||
"releases/old/v0.14",
|
|
||||||
"releases/old/v0.13",
|
|
||||||
"releases/old/v0.12",
|
|
||||||
"releases/old/v0.11",
|
|
||||||
"releases/old/v0.10",
|
|
||||||
"releases/old/v0.9",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {SidebarItemConfig[]}
|
* @type {SidebarItemConfig[]}
|
||||||
*/
|
*/
|
||||||
const items = [
|
const items = [
|
||||||
{
|
|
||||||
type: "html",
|
|
||||||
value: generateVersionDropdown(releases),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
type: "doc",
|
type: "doc",
|
||||||
id: "index",
|
id: "index",
|
||||||
@ -796,7 +743,7 @@ const items = [
|
|||||||
slug: "releases",
|
slug: "releases",
|
||||||
description: "Release Notes for recent authentik versions",
|
description: "Release Notes for recent authentik versions",
|
||||||
},
|
},
|
||||||
items: releases,
|
items: createReleaseSidebarEntries(releases),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -80,7 +80,6 @@ const items = [
|
|||||||
"services/argocd/index",
|
"services/argocd/index",
|
||||||
"services/awx-tower/index",
|
"services/awx-tower/index",
|
||||||
"services/cloudflare-access/index",
|
"services/cloudflare-access/index",
|
||||||
"services/coder/index",
|
|
||||||
"services/globalprotect/index",
|
"services/globalprotect/index",
|
||||||
"services/harbor/index",
|
"services/harbor/index",
|
||||||
"services/hashicorp-vault/index",
|
"services/hashicorp-vault/index",
|
||||||
|
231
website/src/components/VersionPicker/index.tsx
Normal file
231
website/src/components/VersionPicker/index.tsx
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
import { usePluginData } from "@docusaurus/useGlobalData";
|
||||||
|
import useIsBrowser from "@docusaurus/useIsBrowser";
|
||||||
|
import type { AKReleasesPluginData } from "@site/releases/plugin.mjs";
|
||||||
|
import clsx from "clsx";
|
||||||
|
import React, { memo, useEffect, useMemo, useState } from "react";
|
||||||
|
import { coerce } from "semver";
|
||||||
|
|
||||||
|
import "./styles.css";
|
||||||
|
|
||||||
|
const ProductionURL = new URL("https://docs.goauthentik.io");
|
||||||
|
const LocalhostAliases: ReadonlySet<string> = new Set(["localhost", "127.0.0.1"]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a semver, create the URL for the version.
|
||||||
|
*/
|
||||||
|
function createVersionURL(semver: string): string {
|
||||||
|
const subdomain = `version-${semver.replace(".", "-")}`;
|
||||||
|
|
||||||
|
return `https://${subdomain}.goauthentik.io`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predicate to determine if a hostname appears to be a prerelease origin.
|
||||||
|
*/
|
||||||
|
function isPrerelease(hostname: string | null): boolean {
|
||||||
|
if (!hostname) return false;
|
||||||
|
|
||||||
|
if (hostname === ProductionURL.hostname) return true;
|
||||||
|
if (hostname.endsWith(".netlify.app")) return true;
|
||||||
|
|
||||||
|
if (LocalhostAliases.has(hostname)) return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a hostname, parse the semver from the subdomain.
|
||||||
|
*/
|
||||||
|
function parseHostnameSemVer(hostname: string | null): string | null {
|
||||||
|
if (!hostname) return null;
|
||||||
|
|
||||||
|
const [, possibleSemVer] = hostname.match(/version-(.+)\.goauthentik\.io/) || [];
|
||||||
|
|
||||||
|
if (!possibleSemVer) return null;
|
||||||
|
|
||||||
|
const formattedSemVer = possibleSemVer.replace("-", ".");
|
||||||
|
|
||||||
|
if (!coerce(formattedSemVer)) return null;
|
||||||
|
|
||||||
|
return formattedSemVer;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VersionDropdownProps {
|
||||||
|
/**
|
||||||
|
* The hostname of the client.
|
||||||
|
*/
|
||||||
|
hostname: string | null;
|
||||||
|
/**
|
||||||
|
* The origin of the prerelease documentation.
|
||||||
|
*
|
||||||
|
* @format url
|
||||||
|
*/
|
||||||
|
prereleaseOrigin: string;
|
||||||
|
/**
|
||||||
|
* The available versions of the documentation.
|
||||||
|
*
|
||||||
|
* @format semver
|
||||||
|
*/
|
||||||
|
releases: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A dropdown that shows the available versions of the documentation.
|
||||||
|
*/
|
||||||
|
const VersionDropdown = memo<VersionDropdownProps>(({ hostname, prereleaseOrigin, releases }) => {
|
||||||
|
const prerelease = isPrerelease(hostname);
|
||||||
|
const parsedSemVer = !prerelease ? parseHostnameSemVer(hostname) : null;
|
||||||
|
|
||||||
|
const currentLabel = parsedSemVer || "Pre-Release";
|
||||||
|
|
||||||
|
const endIndex = parsedSemVer ? releases.indexOf(parsedSemVer) : -1;
|
||||||
|
|
||||||
|
const visibleReleases = releases.slice(0, endIndex === -1 ? 3 : endIndex + 3);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li className="navbar__item dropdown dropdown--hoverable dropdown--right ak-version-selector">
|
||||||
|
<div
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded="false"
|
||||||
|
role="button"
|
||||||
|
className="navbar__link menu__link"
|
||||||
|
>
|
||||||
|
Version: {currentLabel}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul className="dropdown__menu menu__list-item--collapsed">
|
||||||
|
{!prerelease ? (
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href={prereleaseOrigin}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="dropdown__link menu__link"
|
||||||
|
>
|
||||||
|
Pre-Release
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{visibleReleases.map((semVer, idx) => {
|
||||||
|
let label = semVer;
|
||||||
|
|
||||||
|
if (idx === 0) {
|
||||||
|
label += " (Current Release)";
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li key={idx}>
|
||||||
|
<a
|
||||||
|
href={createVersionURL(semVer)}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className={clsx("dropdown__link menu__link", {
|
||||||
|
"menu__link--active": semVer === currentLabel,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
interface VersionPickerLoaderProps {
|
||||||
|
pluginData: AKReleasesPluginData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A data-fetching component that loads available versions of the documentation.
|
||||||
|
*
|
||||||
|
* @see {@linkcode VersionPicker} for the component.
|
||||||
|
* @see {@linkcode AKReleasesPluginData} for the plugin data.
|
||||||
|
* @client
|
||||||
|
*/
|
||||||
|
const VersionPickerLoader: React.FC<VersionPickerLoaderProps> = ({ pluginData }) => {
|
||||||
|
const [releases, setReleases] = useState(pluginData.releases);
|
||||||
|
|
||||||
|
const browser = useIsBrowser();
|
||||||
|
|
||||||
|
const prereleaseOrigin = useMemo(() => {
|
||||||
|
if (browser && LocalhostAliases.has(window.location.hostname)) {
|
||||||
|
return window.location.origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ProductionURL.href;
|
||||||
|
}, [browser]);
|
||||||
|
|
||||||
|
const hostname = useMemo(() => {
|
||||||
|
if (!browser) return null;
|
||||||
|
|
||||||
|
const searchParams = new URLSearchParams(window.location.search);
|
||||||
|
|
||||||
|
// Query parameter used for debugging.
|
||||||
|
// Note that this doesn't synchronize with Docusaurus's router state.
|
||||||
|
const subdomain = searchParams.get("version");
|
||||||
|
|
||||||
|
if (subdomain) return subdomain;
|
||||||
|
|
||||||
|
return window.location.hostname;
|
||||||
|
}, [browser]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!browser || !prereleaseOrigin) return;
|
||||||
|
|
||||||
|
const controller = new AbortController();
|
||||||
|
const updateURL = new URL(pluginData.publicPath, prereleaseOrigin);
|
||||||
|
|
||||||
|
fetch(updateURL, {
|
||||||
|
signal: controller.signal,
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to fetch new releases: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then((data: unknown) => {
|
||||||
|
// We're extra cautious here to be ready if the API shape ever changes.
|
||||||
|
if (!data) throw new Error("Failed to parse releases");
|
||||||
|
|
||||||
|
if (!Array.isArray(data)) throw new Error("Releases must be an array");
|
||||||
|
|
||||||
|
if (!data.every((item) => typeof item === "string"))
|
||||||
|
throw new Error("Releases must be an array of strings");
|
||||||
|
|
||||||
|
setReleases(data);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.warn(`Failed to fetch new releases: ${error}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => controller.abort("unmount");
|
||||||
|
}, [browser, prereleaseOrigin]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<VersionDropdown
|
||||||
|
hostname={hostname}
|
||||||
|
prereleaseOrigin={prereleaseOrigin}
|
||||||
|
releases={releases}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A component that shows the available versions of the documentation.
|
||||||
|
*
|
||||||
|
* @see {@linkcode VersionPickerLoader} for the data-fetching component.
|
||||||
|
*/
|
||||||
|
export const VersionPicker: React.FC = () => {
|
||||||
|
const pluginData = usePluginData("ak-releases-plugin", undefined, {
|
||||||
|
failfast: true,
|
||||||
|
}) as AKReleasesPluginData;
|
||||||
|
|
||||||
|
if (!pluginData.releases.length) return null;
|
||||||
|
|
||||||
|
return <VersionPickerLoader pluginData={pluginData} />;
|
||||||
|
};
|
27
website/src/components/VersionPicker/styles.css
Normal file
27
website/src/components/VersionPicker/styles.css
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
.theme-doc-sidebar-menu .dropdown.ak-version-selector {
|
||||||
|
--ak-version-selector-padding: calc(var(--ifm-spacing-vertical) / 2);
|
||||||
|
|
||||||
|
width: calc(100% - (var(--ifm-spacing-horizontal) / 2));
|
||||||
|
border-block-end: var(--ifm-hr-height) solid var(--ifm-toc-border-color);
|
||||||
|
padding-block-start: calc(var(--ak-version-selector-padding) / 2);
|
||||||
|
padding-block-end: var(--ak-version-selector-padding);
|
||||||
|
margin-block-end: var(--ak-version-selector-padding);
|
||||||
|
|
||||||
|
.navbar__link.menu__link {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-weight: var(--ifm-font-weight-semibold);
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
color: var(--ifm-color-emphasis-400);
|
||||||
|
filter: var(--ifm-menu-link-sublist-icon-filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown__menu {
|
||||||
|
background: var(--ifm-dropdown-background-color);
|
||||||
|
box-shadow: var(--ifm-global-shadow-lw);
|
||||||
|
border: 1px solid var(--ifm-color-emphasis-200);
|
||||||
|
}
|
||||||
|
}
|
24
website/src/theme/DocSidebarItems/index.tsx
Normal file
24
website/src/theme/DocSidebarItems/index.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import {
|
||||||
|
DocSidebarItemsExpandedStateProvider,
|
||||||
|
useVisibleSidebarItems,
|
||||||
|
} from "@docusaurus/plugin-content-docs/client";
|
||||||
|
import { VersionPicker } from "@site/src/components/VersionPicker/index";
|
||||||
|
import DocSidebarItem from "@theme/DocSidebarItem";
|
||||||
|
import type { Props as DocSidebarItemsProps } from "@theme/DocSidebarItems";
|
||||||
|
import { memo } from "react";
|
||||||
|
|
||||||
|
const DocSidebarItems: React.FC<DocSidebarItemsProps> = ({ items, ...props }) => {
|
||||||
|
const visibleItems = useVisibleSidebarItems(items, props.activePath);
|
||||||
|
const includeVersionPicker = props.level === 1 && props.activePath.startsWith("/docs");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DocSidebarItemsExpandedStateProvider>
|
||||||
|
{includeVersionPicker ? <VersionPicker /> : null}
|
||||||
|
{visibleItems.map((item, index) => (
|
||||||
|
<DocSidebarItem key={index} item={item} index={index} {...props} />
|
||||||
|
))}
|
||||||
|
</DocSidebarItemsExpandedStateProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(DocSidebarItems);
|
3
website/types/docusaurus.d.ts
vendored
3
website/types/docusaurus.d.ts
vendored
@ -21,6 +21,9 @@ declare module "@docusaurus/plugin-content-docs/src/sidebars/types" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
declare module "@docusaurus/plugin-content-docs/client" {
|
declare module "@docusaurus/plugin-content-docs/client" {
|
||||||
|
export * from "@docusaurus/plugin-content-docs/lib/client/docSidebarItemsExpandedState.js";
|
||||||
|
export * from "@docusaurus/plugin-content-docs/lib/client/docsUtils.js";
|
||||||
|
|
||||||
import { DocContextValue as BaseDocContextValue } from "@docusaurus/plugin-content-docs/lib/client/doc.js";
|
import { DocContextValue as BaseDocContextValue } from "@docusaurus/plugin-content-docs/lib/client/doc.js";
|
||||||
import { DocFrontMatter as BaseDocFrontMatter } from "@docusaurus/plugin-content-docs";
|
import { DocFrontMatter as BaseDocFrontMatter } from "@docusaurus/plugin-content-docs";
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user