Compare commits
1 Commits
static-con
...
linter-fix
| Author | SHA1 | Date | |
|---|---|---|---|
| f9e129bed2 |
@ -1,5 +1,5 @@
|
|||||||
[bumpversion]
|
[bumpversion]
|
||||||
current_version = 2025.2.4
|
current_version = 2025.2.3
|
||||||
tag = True
|
tag = True
|
||||||
commit = True
|
commit = True
|
||||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-(?P<rc_t>[a-zA-Z-]+)(?P<rc_n>[1-9]\\d*))?
|
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-(?P<rc_t>[a-zA-Z-]+)(?P<rc_n>[1-9]\\d*))?
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -33,7 +33,6 @@ eggs/
|
|||||||
lib64/
|
lib64/
|
||||||
parts/
|
parts/
|
||||||
dist/
|
dist/
|
||||||
out/
|
|
||||||
sdist/
|
sdist/
|
||||||
var/
|
var/
|
||||||
wheels/
|
wheels/
|
||||||
|
|||||||
@ -94,7 +94,7 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
|
|||||||
/bin/sh -c "/usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
|
/bin/sh -c "/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.6.14 AS uv
|
FROM ghcr.io/astral-sh/uv:0.6.11 AS uv
|
||||||
# Stage 6: Base python image
|
# Stage 6: Base python image
|
||||||
FROM ghcr.io/goauthentik/fips-python:3.12.9-slim-bookworm-fips AS python-base
|
FROM ghcr.io/goauthentik/fips-python:3.12.9-slim-bookworm-fips AS python-base
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
from os import environ
|
from os import environ
|
||||||
|
|
||||||
__version__ = "2025.2.4"
|
__version__ = "2025.2.3"
|
||||||
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"
|
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -179,13 +179,10 @@ class UserSourceConnectionSerializer(SourceSerializer):
|
|||||||
"user",
|
"user",
|
||||||
"source",
|
"source",
|
||||||
"source_obj",
|
"source_obj",
|
||||||
"identifier",
|
|
||||||
"created",
|
"created",
|
||||||
"last_updated",
|
|
||||||
]
|
]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
"created": {"read_only": True},
|
"created": {"read_only": True},
|
||||||
"last_updated": {"read_only": True},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -202,7 +199,7 @@ class UserSourceConnectionViewSet(
|
|||||||
queryset = UserSourceConnection.objects.all()
|
queryset = UserSourceConnection.objects.all()
|
||||||
serializer_class = UserSourceConnectionSerializer
|
serializer_class = UserSourceConnectionSerializer
|
||||||
filterset_fields = ["user", "source__slug"]
|
filterset_fields = ["user", "source__slug"]
|
||||||
search_fields = ["user__username", "source__slug", "identifier"]
|
search_fields = ["source__slug"]
|
||||||
ordering = ["source__slug", "pk"]
|
ordering = ["source__slug", "pk"]
|
||||||
owner_field = "user"
|
owner_field = "user"
|
||||||
|
|
||||||
@ -221,11 +218,9 @@ class GroupSourceConnectionSerializer(SourceSerializer):
|
|||||||
"source_obj",
|
"source_obj",
|
||||||
"identifier",
|
"identifier",
|
||||||
"created",
|
"created",
|
||||||
"last_updated",
|
|
||||||
]
|
]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
"created": {"read_only": True},
|
"created": {"read_only": True},
|
||||||
"last_updated": {"read_only": True},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -242,5 +237,6 @@ class GroupSourceConnectionViewSet(
|
|||||||
queryset = GroupSourceConnection.objects.all()
|
queryset = GroupSourceConnection.objects.all()
|
||||||
serializer_class = GroupSourceConnectionSerializer
|
serializer_class = GroupSourceConnectionSerializer
|
||||||
filterset_fields = ["group", "source__slug"]
|
filterset_fields = ["group", "source__slug"]
|
||||||
search_fields = ["group__name", "source__slug", "identifier"]
|
search_fields = ["source__slug"]
|
||||||
ordering = ["source__slug", "pk"]
|
ordering = ["source__slug", "pk"]
|
||||||
|
owner_field = "user"
|
||||||
|
|||||||
@ -228,7 +228,6 @@ class UserSerializer(ModelSerializer):
|
|||||||
"name",
|
"name",
|
||||||
"is_active",
|
"is_active",
|
||||||
"last_login",
|
"last_login",
|
||||||
"date_joined",
|
|
||||||
"is_superuser",
|
"is_superuser",
|
||||||
"groups",
|
"groups",
|
||||||
"groups_obj",
|
"groups_obj",
|
||||||
@ -243,7 +242,6 @@ class UserSerializer(ModelSerializer):
|
|||||||
]
|
]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
"name": {"allow_blank": True},
|
"name": {"allow_blank": True},
|
||||||
"date_joined": {"read_only": True},
|
|
||||||
"password_change_date": {"read_only": True},
|
"password_change_date": {"read_only": True},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,19 +0,0 @@
|
|||||||
# Generated by Django 5.0.13 on 2025-04-07 14:04
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("authentik_core", "0043_alter_group_options"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name="usersourceconnection",
|
|
||||||
name="new_identifier",
|
|
||||||
field=models.TextField(default=""),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("authentik_core", "0044_usersourceconnection_new_identifier"),
|
|
||||||
("authentik_sources_kerberos", "0003_migrate_userkerberossourceconnection_identifier"),
|
|
||||||
("authentik_sources_oauth", "0009_migrate_useroauthsourceconnection_identifier"),
|
|
||||||
("authentik_sources_plex", "0005_migrate_userplexsourceconnection_identifier"),
|
|
||||||
("authentik_sources_saml", "0019_migrate_usersamlsourceconnection_identifier"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name="usersourceconnection",
|
|
||||||
old_name="new_identifier",
|
|
||||||
new_name="identifier",
|
|
||||||
),
|
|
||||||
migrations.AddIndex(
|
|
||||||
model_name="usersourceconnection",
|
|
||||||
index=models.Index(fields=["identifier"], name="authentik_c_identif_59226f_idx"),
|
|
||||||
),
|
|
||||||
migrations.AddIndex(
|
|
||||||
model_name="usersourceconnection",
|
|
||||||
index=models.Index(
|
|
||||||
fields=["source", "identifier"], name="authentik_c_source__649e04_idx"
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -824,7 +824,6 @@ class UserSourceConnection(SerializerModel, CreatedUpdatedModel):
|
|||||||
|
|
||||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
source = models.ForeignKey(Source, on_delete=models.CASCADE)
|
source = models.ForeignKey(Source, on_delete=models.CASCADE)
|
||||||
identifier = models.TextField()
|
|
||||||
|
|
||||||
objects = InheritanceManager()
|
objects = InheritanceManager()
|
||||||
|
|
||||||
@ -838,10 +837,6 @@ class UserSourceConnection(SerializerModel, CreatedUpdatedModel):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = (("user", "source"),)
|
unique_together = (("user", "source"),)
|
||||||
indexes = (
|
|
||||||
models.Index(fields=("identifier",)),
|
|
||||||
models.Index(fields=("source", "identifier")),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class GroupSourceConnection(SerializerModel, CreatedUpdatedModel):
|
class GroupSourceConnection(SerializerModel, CreatedUpdatedModel):
|
||||||
|
|||||||
@ -36,6 +36,7 @@ from authentik.flows.planner import (
|
|||||||
)
|
)
|
||||||
from authentik.flows.stage import StageView
|
from authentik.flows.stage import StageView
|
||||||
from authentik.flows.views.executor import NEXT_ARG_NAME, SESSION_KEY_GET
|
from authentik.flows.views.executor import NEXT_ARG_NAME, SESSION_KEY_GET
|
||||||
|
from authentik.lib.utils.urls import is_url_absolute
|
||||||
from authentik.lib.views import bad_request_message
|
from authentik.lib.views import bad_request_message
|
||||||
from authentik.policies.denied import AccessDeniedResponse
|
from authentik.policies.denied import AccessDeniedResponse
|
||||||
from authentik.policies.utils import delete_none_values
|
from authentik.policies.utils import delete_none_values
|
||||||
@ -209,6 +210,8 @@ class SourceFlowManager:
|
|||||||
final_redirect = self.request.session.get(SESSION_KEY_GET, {}).get(
|
final_redirect = self.request.session.get(SESSION_KEY_GET, {}).get(
|
||||||
NEXT_ARG_NAME, "authentik_core:if-user"
|
NEXT_ARG_NAME, "authentik_core:if-user"
|
||||||
)
|
)
|
||||||
|
if not is_url_absolute(final_redirect):
|
||||||
|
final_redirect = "authentik_core:if-user"
|
||||||
flow_context.update(
|
flow_context.update(
|
||||||
{
|
{
|
||||||
# Since we authenticate the user by their token, they have no backend set
|
# Since we authenticate the user by their token, they have no backend set
|
||||||
|
|||||||
@ -13,11 +13,7 @@ from authentik.core.api.devices import AdminDeviceViewSet, DeviceViewSet
|
|||||||
from authentik.core.api.groups import GroupViewSet
|
from authentik.core.api.groups import GroupViewSet
|
||||||
from authentik.core.api.property_mappings import PropertyMappingViewSet
|
from authentik.core.api.property_mappings import PropertyMappingViewSet
|
||||||
from authentik.core.api.providers import ProviderViewSet
|
from authentik.core.api.providers import ProviderViewSet
|
||||||
from authentik.core.api.sources import (
|
from authentik.core.api.sources import SourceViewSet, UserSourceConnectionViewSet
|
||||||
GroupSourceConnectionViewSet,
|
|
||||||
SourceViewSet,
|
|
||||||
UserSourceConnectionViewSet,
|
|
||||||
)
|
|
||||||
from authentik.core.api.tokens import TokenViewSet
|
from authentik.core.api.tokens import TokenViewSet
|
||||||
from authentik.core.api.transactional_applications import TransactionalApplicationView
|
from authentik.core.api.transactional_applications import TransactionalApplicationView
|
||||||
from authentik.core.api.users import UserViewSet
|
from authentik.core.api.users import UserViewSet
|
||||||
@ -85,7 +81,6 @@ api_urlpatterns = [
|
|||||||
("core/tokens", TokenViewSet),
|
("core/tokens", TokenViewSet),
|
||||||
("sources/all", SourceViewSet),
|
("sources/all", SourceViewSet),
|
||||||
("sources/user_connections/all", UserSourceConnectionViewSet),
|
("sources/user_connections/all", UserSourceConnectionViewSet),
|
||||||
("sources/group_connections/all", GroupSourceConnectionViewSet),
|
|
||||||
("providers/all", ProviderViewSet),
|
("providers/all", ProviderViewSet),
|
||||||
("propertymappings/all", PropertyMappingViewSet),
|
("propertymappings/all", PropertyMappingViewSet),
|
||||||
("authenticators/all", DeviceViewSet, "device"),
|
("authenticators/all", DeviceViewSet, "device"),
|
||||||
|
|||||||
@ -28,8 +28,8 @@ def pytest_report_header(*_, **__):
|
|||||||
|
|
||||||
|
|
||||||
def pytest_collection_modifyitems(config: pytest.Config, items: list[pytest.Item]) -> None:
|
def pytest_collection_modifyitems(config: pytest.Config, items: list[pytest.Item]) -> None:
|
||||||
current_id = int(environ.get("CI_RUN_ID", "0")) - 1
|
current_id = int(environ.get("CI_RUN_ID", 0)) - 1
|
||||||
total_ids = int(environ.get("CI_TOTAL_RUNS", "0"))
|
total_ids = int(environ.get("CI_TOTAL_RUNS", 0))
|
||||||
|
|
||||||
if total_ids:
|
if total_ids:
|
||||||
num_tests = len(items)
|
num_tests = len(items)
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
|
"""Kerberos Source Serializer"""
|
||||||
|
|
||||||
from rest_framework.viewsets import ModelViewSet
|
from rest_framework.viewsets import ModelViewSet
|
||||||
|
|
||||||
from authentik.core.api.sources import (
|
from authentik.core.api.sources import (
|
||||||
GroupSourceConnectionSerializer,
|
GroupSourceConnectionSerializer,
|
||||||
GroupSourceConnectionViewSet,
|
GroupSourceConnectionViewSet,
|
||||||
UserSourceConnectionSerializer,
|
UserSourceConnectionSerializer,
|
||||||
UserSourceConnectionViewSet,
|
|
||||||
)
|
)
|
||||||
|
from authentik.core.api.used_by import UsedByMixin
|
||||||
from authentik.sources.kerberos.models import (
|
from authentik.sources.kerberos.models import (
|
||||||
GroupKerberosSourceConnection,
|
GroupKerberosSourceConnection,
|
||||||
UserKerberosSourceConnection,
|
UserKerberosSourceConnection,
|
||||||
@ -13,20 +15,33 @@ from authentik.sources.kerberos.models import (
|
|||||||
|
|
||||||
|
|
||||||
class UserKerberosSourceConnectionSerializer(UserSourceConnectionSerializer):
|
class UserKerberosSourceConnectionSerializer(UserSourceConnectionSerializer):
|
||||||
class Meta(UserSourceConnectionSerializer.Meta):
|
"""Kerberos Source Serializer"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
model = UserKerberosSourceConnection
|
model = UserKerberosSourceConnection
|
||||||
|
fields = UserSourceConnectionSerializer.Meta.fields + ["identifier"]
|
||||||
|
|
||||||
|
|
||||||
class UserKerberosSourceConnectionViewSet(UserSourceConnectionViewSet, ModelViewSet):
|
class UserKerberosSourceConnectionViewSet(UsedByMixin, ModelViewSet):
|
||||||
|
"""Source Viewset"""
|
||||||
|
|
||||||
queryset = UserKerberosSourceConnection.objects.all()
|
queryset = UserKerberosSourceConnection.objects.all()
|
||||||
serializer_class = UserKerberosSourceConnectionSerializer
|
serializer_class = UserKerberosSourceConnectionSerializer
|
||||||
|
filterset_fields = ["source__slug"]
|
||||||
|
search_fields = ["source__slug"]
|
||||||
|
ordering = ["source__slug"]
|
||||||
|
owner_field = "user"
|
||||||
|
|
||||||
|
|
||||||
class GroupKerberosSourceConnectionSerializer(GroupSourceConnectionSerializer):
|
class GroupKerberosSourceConnectionSerializer(GroupSourceConnectionSerializer):
|
||||||
|
"""OAuth Group-Source connection Serializer"""
|
||||||
|
|
||||||
class Meta(GroupSourceConnectionSerializer.Meta):
|
class Meta(GroupSourceConnectionSerializer.Meta):
|
||||||
model = GroupKerberosSourceConnection
|
model = GroupKerberosSourceConnection
|
||||||
|
|
||||||
|
|
||||||
class GroupKerberosSourceConnectionViewSet(GroupSourceConnectionViewSet, ModelViewSet):
|
class GroupKerberosSourceConnectionViewSet(GroupSourceConnectionViewSet):
|
||||||
|
"""Group-source connection Viewset"""
|
||||||
|
|
||||||
queryset = GroupKerberosSourceConnection.objects.all()
|
queryset = GroupKerberosSourceConnection.objects.all()
|
||||||
serializer_class = GroupKerberosSourceConnectionSerializer
|
serializer_class = GroupKerberosSourceConnectionSerializer
|
||||||
|
|||||||
@ -1,28 +0,0 @@
|
|||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
def migrate_identifier(apps, schema_editor):
|
|
||||||
db_alias = schema_editor.connection.alias
|
|
||||||
UserKerberosSourceConnection = apps.get_model(
|
|
||||||
"authentik_sources_kerberos", "UserKerberosSourceConnection"
|
|
||||||
)
|
|
||||||
|
|
||||||
for connection in UserKerberosSourceConnection.objects.using(db_alias).all():
|
|
||||||
connection.new_identifier = connection.identifier
|
|
||||||
connection.save(using=db_alias)
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("authentik_sources_kerberos", "0002_kerberossource_kadmin_type"),
|
|
||||||
("authentik_core", "0044_usersourceconnection_new_identifier"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RunPython(code=migrate_identifier, reverse_code=migrations.RunPython.noop),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name="userkerberossourceconnection",
|
|
||||||
name="identifier",
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -372,6 +372,8 @@ class KerberosSourcePropertyMapping(PropertyMapping):
|
|||||||
class UserKerberosSourceConnection(UserSourceConnection):
|
class UserKerberosSourceConnection(UserSourceConnection):
|
||||||
"""Connection to configured Kerberos Sources."""
|
"""Connection to configured Kerberos Sources."""
|
||||||
|
|
||||||
|
identifier = models.TextField()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def serializer(self) -> type[Serializer]:
|
def serializer(self) -> type[Serializer]:
|
||||||
from authentik.sources.kerberos.api.source_connection import (
|
from authentik.sources.kerberos.api.source_connection import (
|
||||||
|
|||||||
@ -99,7 +99,6 @@ class LDAPSourceSerializer(SourceSerializer):
|
|||||||
"sync_groups",
|
"sync_groups",
|
||||||
"sync_parent_group",
|
"sync_parent_group",
|
||||||
"connectivity",
|
"connectivity",
|
||||||
"lookup_groups_from_user",
|
|
||||||
]
|
]
|
||||||
extra_kwargs = {"bind_password": {"write_only": True}}
|
extra_kwargs = {"bind_password": {"write_only": True}}
|
||||||
|
|
||||||
@ -135,7 +134,6 @@ class LDAPSourceViewSet(UsedByMixin, ModelViewSet):
|
|||||||
"sync_parent_group",
|
"sync_parent_group",
|
||||||
"user_property_mappings",
|
"user_property_mappings",
|
||||||
"group_property_mappings",
|
"group_property_mappings",
|
||||||
"lookup_groups_from_user",
|
|
||||||
]
|
]
|
||||||
search_fields = ["name", "slug"]
|
search_fields = ["name", "slug"]
|
||||||
ordering = ["name"]
|
ordering = ["name"]
|
||||||
|
|||||||
@ -1,24 +0,0 @@
|
|||||||
# Generated by Django 5.0.13 on 2025-03-26 17:06
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
(
|
|
||||||
"authentik_sources_ldap",
|
|
||||||
"0006_rename_ldappropertymapping_ldapsourcepropertymapping_and_more",
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name="ldapsource",
|
|
||||||
name="lookup_groups_from_user",
|
|
||||||
field=models.BooleanField(
|
|
||||||
default=False,
|
|
||||||
help_text="Lookup group membership based on a user attribute instead of a group attribute. This allows nested group resolution on systems like FreeIPA and Active Directory",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -123,14 +123,6 @@ class LDAPSource(Source):
|
|||||||
Group, blank=True, null=True, default=None, on_delete=models.SET_DEFAULT
|
Group, blank=True, null=True, default=None, on_delete=models.SET_DEFAULT
|
||||||
)
|
)
|
||||||
|
|
||||||
lookup_groups_from_user = models.BooleanField(
|
|
||||||
default=False,
|
|
||||||
help_text=_(
|
|
||||||
"Lookup group membership based on a user attribute instead of a group attribute. "
|
|
||||||
"This allows nested group resolution on systems like FreeIPA and Active Directory"
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def component(self) -> str:
|
def component(self) -> str:
|
||||||
return "ak-source-ldap-form"
|
return "ak-source-ldap-form"
|
||||||
|
|||||||
@ -28,17 +28,15 @@ class MembershipLDAPSynchronizer(BaseLDAPSynchronizer):
|
|||||||
if not self._source.sync_groups:
|
if not self._source.sync_groups:
|
||||||
self.message("Group syncing is disabled for this Source")
|
self.message("Group syncing is disabled for this Source")
|
||||||
return iter(())
|
return iter(())
|
||||||
|
|
||||||
# If we are looking up groups from users, we don't need to fetch the group membership field
|
|
||||||
attributes = [self._source.object_uniqueness_field, LDAP_DISTINGUISHED_NAME]
|
|
||||||
if not self._source.lookup_groups_from_user:
|
|
||||||
attributes.append(self._source.group_membership_field)
|
|
||||||
|
|
||||||
return self.search_paginator(
|
return self.search_paginator(
|
||||||
search_base=self.base_dn_groups,
|
search_base=self.base_dn_groups,
|
||||||
search_filter=self._source.group_object_filter,
|
search_filter=self._source.group_object_filter,
|
||||||
search_scope=SUBTREE,
|
search_scope=SUBTREE,
|
||||||
attributes=attributes,
|
attributes=[
|
||||||
|
self._source.group_membership_field,
|
||||||
|
self._source.object_uniqueness_field,
|
||||||
|
LDAP_DISTINGUISHED_NAME,
|
||||||
|
],
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -49,24 +47,9 @@ class MembershipLDAPSynchronizer(BaseLDAPSynchronizer):
|
|||||||
return -1
|
return -1
|
||||||
membership_count = 0
|
membership_count = 0
|
||||||
for group in page_data:
|
for group in page_data:
|
||||||
if self._source.lookup_groups_from_user:
|
if "attributes" not in group:
|
||||||
group_dn = group.get("dn", {})
|
continue
|
||||||
group_filter = f"({self._source.group_membership_field}={group_dn})"
|
members = group.get("attributes", {}).get(self._source.group_membership_field, [])
|
||||||
group_members = self._source.connection().extend.standard.paged_search(
|
|
||||||
search_base=self.base_dn_users,
|
|
||||||
search_filter=group_filter,
|
|
||||||
search_scope=SUBTREE,
|
|
||||||
attributes=[self._source.object_uniqueness_field],
|
|
||||||
)
|
|
||||||
members = []
|
|
||||||
for group_member in group_members:
|
|
||||||
group_member_dn = group_member.get("dn", {})
|
|
||||||
members.append(group_member_dn)
|
|
||||||
else:
|
|
||||||
if "attributes" not in group:
|
|
||||||
continue
|
|
||||||
members = group.get("attributes", {}).get(self._source.group_membership_field, [])
|
|
||||||
|
|
||||||
ak_group = self.get_group(group)
|
ak_group = self.get_group(group)
|
||||||
if not ak_group:
|
if not ak_group:
|
||||||
continue
|
continue
|
||||||
@ -85,7 +68,7 @@ class MembershipLDAPSynchronizer(BaseLDAPSynchronizer):
|
|||||||
"ak_groups__in": [ak_group],
|
"ak_groups__in": [ak_group],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
).distinct()
|
)
|
||||||
membership_count += 1
|
membership_count += 1
|
||||||
membership_count += users.count()
|
membership_count += users.count()
|
||||||
ak_group.users.set(users)
|
ak_group.users.set(users)
|
||||||
|
|||||||
@ -96,26 +96,6 @@ def mock_freeipa_connection(password: str) -> Connection:
|
|||||||
"objectClass": "posixAccount",
|
"objectClass": "posixAccount",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
# User with groups in memberOf attribute
|
|
||||||
connection.strategy.add_entry(
|
|
||||||
"cn=user4,ou=users,dc=goauthentik,dc=io",
|
|
||||||
{
|
|
||||||
"name": "user4_sn",
|
|
||||||
"uid": "user4_sn",
|
|
||||||
"objectClass": "person",
|
|
||||||
"memberOf": [
|
|
||||||
"cn=reverse-lookup-group,ou=groups,dc=goauthentik,dc=io",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
connection.strategy.add_entry(
|
|
||||||
"cn=reverse-lookup-group,ou=groups,dc=goauthentik,dc=io",
|
|
||||||
{
|
|
||||||
"cn": "reverse-lookup-group",
|
|
||||||
"uid": "reverse-lookup-group",
|
|
||||||
"objectClass": "groupOfNames",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
# Locked out user
|
# Locked out user
|
||||||
connection.strategy.add_entry(
|
connection.strategy.add_entry(
|
||||||
"cn=user-nsaccountlock,ou=users,dc=goauthentik,dc=io",
|
"cn=user-nsaccountlock,ou=users,dc=goauthentik,dc=io",
|
||||||
|
|||||||
@ -162,43 +162,6 @@ class LDAPSyncTests(TestCase):
|
|||||||
self.assertFalse(User.objects.filter(username="user1_sn").exists())
|
self.assertFalse(User.objects.filter(username="user1_sn").exists())
|
||||||
self.assertFalse(User.objects.get(username="user-nsaccountlock").is_active)
|
self.assertFalse(User.objects.get(username="user-nsaccountlock").is_active)
|
||||||
|
|
||||||
def test_sync_groups_freeipa_memberOf(self):
|
|
||||||
"""Test group sync when membership is derived from memberOf user attribute"""
|
|
||||||
self.source.object_uniqueness_field = "uid"
|
|
||||||
self.source.group_object_filter = "(objectClass=groupOfNames)"
|
|
||||||
self.source.lookup_groups_from_user = True
|
|
||||||
self.source.group_membership_field = "memberOf"
|
|
||||||
self.source.user_property_mappings.set(
|
|
||||||
LDAPSourcePropertyMapping.objects.filter(
|
|
||||||
Q(managed__startswith="goauthentik.io/sources/ldap/default")
|
|
||||||
| Q(managed__startswith="goauthentik.io/sources/ldap/openldap")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.source.group_property_mappings.set(
|
|
||||||
LDAPSourcePropertyMapping.objects.filter(
|
|
||||||
managed="goauthentik.io/sources/ldap/openldap-cn"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
connection = MagicMock(return_value=mock_freeipa_connection(LDAP_PASSWORD))
|
|
||||||
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
|
||||||
user_sync = UserLDAPSynchronizer(self.source)
|
|
||||||
user_sync.sync_full()
|
|
||||||
group_sync = GroupLDAPSynchronizer(self.source)
|
|
||||||
group_sync.sync_full()
|
|
||||||
membership_sync = MembershipLDAPSynchronizer(self.source)
|
|
||||||
membership_sync.sync_full()
|
|
||||||
|
|
||||||
self.assertTrue(
|
|
||||||
User.objects.filter(username="user4_sn").exists(), "User does not exist"
|
|
||||||
)
|
|
||||||
# Test if membership mapping based on memberOf works.
|
|
||||||
memberof_group = Group.objects.filter(name="reverse-lookup-group")
|
|
||||||
self.assertTrue(memberof_group.exists(), "Group does not exist")
|
|
||||||
self.assertTrue(
|
|
||||||
memberof_group.first().users.filter(username="user4_sn").exists(),
|
|
||||||
"User not a member of the group",
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_sync_groups_ad(self):
|
def test_sync_groups_ad(self):
|
||||||
"""Test group sync"""
|
"""Test group sync"""
|
||||||
self.source.user_property_mappings.set(
|
self.source.user_property_mappings.set(
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
"""OAuth Source Serializer"""
|
||||||
|
|
||||||
from rest_framework.viewsets import ModelViewSet
|
from rest_framework.viewsets import ModelViewSet
|
||||||
|
|
||||||
from authentik.core.api.sources import (
|
from authentik.core.api.sources import (
|
||||||
@ -10,9 +12,11 @@ from authentik.sources.oauth.models import GroupOAuthSourceConnection, UserOAuth
|
|||||||
|
|
||||||
|
|
||||||
class UserOAuthSourceConnectionSerializer(UserSourceConnectionSerializer):
|
class UserOAuthSourceConnectionSerializer(UserSourceConnectionSerializer):
|
||||||
|
"""OAuth Source Serializer"""
|
||||||
|
|
||||||
class Meta(UserSourceConnectionSerializer.Meta):
|
class Meta(UserSourceConnectionSerializer.Meta):
|
||||||
model = UserOAuthSourceConnection
|
model = UserOAuthSourceConnection
|
||||||
fields = UserSourceConnectionSerializer.Meta.fields + ["access_token"]
|
fields = UserSourceConnectionSerializer.Meta.fields + ["identifier", "access_token"]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
**UserSourceConnectionSerializer.Meta.extra_kwargs,
|
**UserSourceConnectionSerializer.Meta.extra_kwargs,
|
||||||
"access_token": {"write_only": True},
|
"access_token": {"write_only": True},
|
||||||
@ -20,15 +24,21 @@ class UserOAuthSourceConnectionSerializer(UserSourceConnectionSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class UserOAuthSourceConnectionViewSet(UserSourceConnectionViewSet, ModelViewSet):
|
class UserOAuthSourceConnectionViewSet(UserSourceConnectionViewSet, ModelViewSet):
|
||||||
|
"""Source Viewset"""
|
||||||
|
|
||||||
queryset = UserOAuthSourceConnection.objects.all()
|
queryset = UserOAuthSourceConnection.objects.all()
|
||||||
serializer_class = UserOAuthSourceConnectionSerializer
|
serializer_class = UserOAuthSourceConnectionSerializer
|
||||||
|
|
||||||
|
|
||||||
class GroupOAuthSourceConnectionSerializer(GroupSourceConnectionSerializer):
|
class GroupOAuthSourceConnectionSerializer(GroupSourceConnectionSerializer):
|
||||||
|
"""OAuth Group-Source connection Serializer"""
|
||||||
|
|
||||||
class Meta(GroupSourceConnectionSerializer.Meta):
|
class Meta(GroupSourceConnectionSerializer.Meta):
|
||||||
model = GroupOAuthSourceConnection
|
model = GroupOAuthSourceConnection
|
||||||
|
|
||||||
|
|
||||||
class GroupOAuthSourceConnectionViewSet(GroupSourceConnectionViewSet, ModelViewSet):
|
class GroupOAuthSourceConnectionViewSet(GroupSourceConnectionViewSet, ModelViewSet):
|
||||||
|
"""Group-source connection Viewset"""
|
||||||
|
|
||||||
queryset = GroupOAuthSourceConnection.objects.all()
|
queryset = GroupOAuthSourceConnection.objects.all()
|
||||||
serializer_class = GroupOAuthSourceConnectionSerializer
|
serializer_class = GroupOAuthSourceConnectionSerializer
|
||||||
|
|||||||
@ -1,28 +0,0 @@
|
|||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
def migrate_identifier(apps, schema_editor):
|
|
||||||
db_alias = schema_editor.connection.alias
|
|
||||||
UserOAuthSourceConnection = apps.get_model(
|
|
||||||
"authentik_sources_oauth", "UserOAuthSourceConnection"
|
|
||||||
)
|
|
||||||
|
|
||||||
for connection in UserOAuthSourceConnection.objects.using(db_alias).all():
|
|
||||||
connection.new_identifier = connection.identifier
|
|
||||||
connection.save(using=db_alias)
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("authentik_sources_oauth", "0008_groupoauthsourceconnection_and_more"),
|
|
||||||
("authentik_core", "0044_usersourceconnection_new_identifier"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RunPython(code=migrate_identifier, reverse_code=migrations.RunPython.noop),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name="useroauthsourceconnection",
|
|
||||||
name="identifier",
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -286,6 +286,7 @@ class OAuthSourcePropertyMapping(PropertyMapping):
|
|||||||
class UserOAuthSourceConnection(UserSourceConnection):
|
class UserOAuthSourceConnection(UserSourceConnection):
|
||||||
"""Authorized remote OAuth provider."""
|
"""Authorized remote OAuth provider."""
|
||||||
|
|
||||||
|
identifier = models.CharField(max_length=255)
|
||||||
access_token = models.TextField(blank=True, null=True, default=None)
|
access_token = models.TextField(blank=True, null=True, default=None)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
"""Plex Source connection Serializer"""
|
||||||
|
|
||||||
from rest_framework.viewsets import ModelViewSet
|
from rest_framework.viewsets import ModelViewSet
|
||||||
|
|
||||||
from authentik.core.api.sources import (
|
from authentik.core.api.sources import (
|
||||||
@ -10,9 +12,14 @@ from authentik.sources.plex.models import GroupPlexSourceConnection, UserPlexSou
|
|||||||
|
|
||||||
|
|
||||||
class UserPlexSourceConnectionSerializer(UserSourceConnectionSerializer):
|
class UserPlexSourceConnectionSerializer(UserSourceConnectionSerializer):
|
||||||
|
"""Plex Source connection Serializer"""
|
||||||
|
|
||||||
class Meta(UserSourceConnectionSerializer.Meta):
|
class Meta(UserSourceConnectionSerializer.Meta):
|
||||||
model = UserPlexSourceConnection
|
model = UserPlexSourceConnection
|
||||||
fields = UserSourceConnectionSerializer.Meta.fields + ["plex_token"]
|
fields = UserSourceConnectionSerializer.Meta.fields + [
|
||||||
|
"identifier",
|
||||||
|
"plex_token",
|
||||||
|
]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
**UserSourceConnectionSerializer.Meta.extra_kwargs,
|
**UserSourceConnectionSerializer.Meta.extra_kwargs,
|
||||||
"plex_token": {"write_only": True},
|
"plex_token": {"write_only": True},
|
||||||
@ -20,15 +27,21 @@ class UserPlexSourceConnectionSerializer(UserSourceConnectionSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class UserPlexSourceConnectionViewSet(UserSourceConnectionViewSet, ModelViewSet):
|
class UserPlexSourceConnectionViewSet(UserSourceConnectionViewSet, ModelViewSet):
|
||||||
|
"""Plex Source connection Serializer"""
|
||||||
|
|
||||||
queryset = UserPlexSourceConnection.objects.all()
|
queryset = UserPlexSourceConnection.objects.all()
|
||||||
serializer_class = UserPlexSourceConnectionSerializer
|
serializer_class = UserPlexSourceConnectionSerializer
|
||||||
|
|
||||||
|
|
||||||
class GroupPlexSourceConnectionSerializer(GroupSourceConnectionSerializer):
|
class GroupPlexSourceConnectionSerializer(GroupSourceConnectionSerializer):
|
||||||
|
"""Plex Group-Source connection Serializer"""
|
||||||
|
|
||||||
class Meta(GroupSourceConnectionSerializer.Meta):
|
class Meta(GroupSourceConnectionSerializer.Meta):
|
||||||
model = GroupPlexSourceConnection
|
model = GroupPlexSourceConnection
|
||||||
|
|
||||||
|
|
||||||
class GroupPlexSourceConnectionViewSet(GroupSourceConnectionViewSet, ModelViewSet):
|
class GroupPlexSourceConnectionViewSet(GroupSourceConnectionViewSet, ModelViewSet):
|
||||||
|
"""Group-source connection Viewset"""
|
||||||
|
|
||||||
queryset = GroupPlexSourceConnection.objects.all()
|
queryset = GroupPlexSourceConnection.objects.all()
|
||||||
serializer_class = GroupPlexSourceConnectionSerializer
|
serializer_class = GroupPlexSourceConnectionSerializer
|
||||||
|
|||||||
@ -1,29 +0,0 @@
|
|||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
def migrate_identifier(apps, schema_editor):
|
|
||||||
db_alias = schema_editor.connection.alias
|
|
||||||
UserPlexSourceConnection = apps.get_model("authentik_sources_plex", "UserPlexSourceConnection")
|
|
||||||
|
|
||||||
for connection in UserPlexSourceConnection.objects.using(db_alias).all():
|
|
||||||
connection.new_identifier = connection.identifier
|
|
||||||
connection.save(using=db_alias)
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
(
|
|
||||||
"authentik_sources_plex",
|
|
||||||
"0004_groupplexsourceconnection_plexsourcepropertymapping_and_more",
|
|
||||||
),
|
|
||||||
("authentik_core", "0044_usersourceconnection_new_identifier"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RunPython(code=migrate_identifier, reverse_code=migrations.RunPython.noop),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name="userplexsourceconnection",
|
|
||||||
name="identifier",
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -141,6 +141,7 @@ class UserPlexSourceConnection(UserSourceConnection):
|
|||||||
"""Connect user and plex source"""
|
"""Connect user and plex source"""
|
||||||
|
|
||||||
plex_token = models.TextField()
|
plex_token = models.TextField()
|
||||||
|
identifier = models.TextField()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def serializer(self) -> type[Serializer]:
|
def serializer(self) -> type[Serializer]:
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
"""SAML Source Serializer"""
|
||||||
|
|
||||||
from rest_framework.viewsets import ModelViewSet
|
from rest_framework.viewsets import ModelViewSet
|
||||||
|
|
||||||
from authentik.core.api.sources import (
|
from authentik.core.api.sources import (
|
||||||
@ -10,20 +12,29 @@ from authentik.sources.saml.models import GroupSAMLSourceConnection, UserSAMLSou
|
|||||||
|
|
||||||
|
|
||||||
class UserSAMLSourceConnectionSerializer(UserSourceConnectionSerializer):
|
class UserSAMLSourceConnectionSerializer(UserSourceConnectionSerializer):
|
||||||
|
"""SAML Source Serializer"""
|
||||||
|
|
||||||
class Meta(UserSourceConnectionSerializer.Meta):
|
class Meta(UserSourceConnectionSerializer.Meta):
|
||||||
model = UserSAMLSourceConnection
|
model = UserSAMLSourceConnection
|
||||||
|
fields = UserSourceConnectionSerializer.Meta.fields + ["identifier"]
|
||||||
|
|
||||||
|
|
||||||
class UserSAMLSourceConnectionViewSet(UserSourceConnectionViewSet, ModelViewSet):
|
class UserSAMLSourceConnectionViewSet(UserSourceConnectionViewSet, ModelViewSet):
|
||||||
|
"""Source Viewset"""
|
||||||
|
|
||||||
queryset = UserSAMLSourceConnection.objects.all()
|
queryset = UserSAMLSourceConnection.objects.all()
|
||||||
serializer_class = UserSAMLSourceConnectionSerializer
|
serializer_class = UserSAMLSourceConnectionSerializer
|
||||||
|
|
||||||
|
|
||||||
class GroupSAMLSourceConnectionSerializer(GroupSourceConnectionSerializer):
|
class GroupSAMLSourceConnectionSerializer(GroupSourceConnectionSerializer):
|
||||||
|
"""OAuth Group-Source connection Serializer"""
|
||||||
|
|
||||||
class Meta(GroupSourceConnectionSerializer.Meta):
|
class Meta(GroupSourceConnectionSerializer.Meta):
|
||||||
model = GroupSAMLSourceConnection
|
model = GroupSAMLSourceConnection
|
||||||
|
|
||||||
|
|
||||||
class GroupSAMLSourceConnectionViewSet(GroupSourceConnectionViewSet, ModelViewSet):
|
class GroupSAMLSourceConnectionViewSet(GroupSourceConnectionViewSet):
|
||||||
|
"""Group-source connection Viewset"""
|
||||||
|
|
||||||
queryset = GroupSAMLSourceConnection.objects.all()
|
queryset = GroupSAMLSourceConnection.objects.all()
|
||||||
serializer_class = GroupSAMLSourceConnectionSerializer
|
serializer_class = GroupSAMLSourceConnectionSerializer
|
||||||
|
|||||||
@ -1,26 +0,0 @@
|
|||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
def migrate_identifier(apps, schema_editor):
|
|
||||||
db_alias = schema_editor.connection.alias
|
|
||||||
UserSAMLSourceConnection = apps.get_model("authentik_sources_saml", "UserSAMLSourceConnection")
|
|
||||||
|
|
||||||
for connection in UserSAMLSourceConnection.objects.using(db_alias).all():
|
|
||||||
connection.new_identifier = connection.identifier
|
|
||||||
connection.save(using=db_alias)
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("authentik_sources_saml", "0018_alter_samlsource_slo_url_alter_samlsource_sso_url"),
|
|
||||||
("authentik_core", "0044_usersourceconnection_new_identifier"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RunPython(code=migrate_identifier, reverse_code=migrations.RunPython.noop),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name="usersamlsourceconnection",
|
|
||||||
name="identifier",
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -318,6 +318,8 @@ class SAMLSourcePropertyMapping(PropertyMapping):
|
|||||||
class UserSAMLSourceConnection(UserSourceConnection):
|
class UserSAMLSourceConnection(UserSourceConnection):
|
||||||
"""Connection to configured SAML Sources."""
|
"""Connection to configured SAML Sources."""
|
||||||
|
|
||||||
|
identifier = models.TextField()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def serializer(self) -> Serializer:
|
def serializer(self) -> Serializer:
|
||||||
from authentik.sources.saml.api.source_connection import UserSAMLSourceConnectionSerializer
|
from authentik.sources.saml.api.source_connection import UserSAMLSourceConnectionSerializer
|
||||||
|
|||||||
@ -33,6 +33,7 @@ from authentik.flows.planner import (
|
|||||||
)
|
)
|
||||||
from authentik.flows.stage import ChallengeStageView
|
from authentik.flows.stage import ChallengeStageView
|
||||||
from authentik.flows.views.executor import NEXT_ARG_NAME, SESSION_KEY_GET, SESSION_KEY_PLAN
|
from authentik.flows.views.executor import NEXT_ARG_NAME, SESSION_KEY_GET, SESSION_KEY_PLAN
|
||||||
|
from authentik.lib.utils.urls import is_url_absolute
|
||||||
from authentik.lib.views import bad_request_message
|
from authentik.lib.views import bad_request_message
|
||||||
from authentik.providers.saml.utils.encoding import nice64
|
from authentik.providers.saml.utils.encoding import nice64
|
||||||
from authentik.sources.saml.exceptions import MissingSAMLResponse, UnsupportedNameIDFormat
|
from authentik.sources.saml.exceptions import MissingSAMLResponse, UnsupportedNameIDFormat
|
||||||
@ -73,6 +74,8 @@ class InitiateView(View):
|
|||||||
final_redirect = self.request.session.get(SESSION_KEY_GET, {}).get(
|
final_redirect = self.request.session.get(SESSION_KEY_GET, {}).get(
|
||||||
NEXT_ARG_NAME, "authentik_core:if-user"
|
NEXT_ARG_NAME, "authentik_core:if-user"
|
||||||
)
|
)
|
||||||
|
if not is_url_absolute(final_redirect):
|
||||||
|
final_redirect = "authentik_core:if-user"
|
||||||
kwargs.update(
|
kwargs.update(
|
||||||
{
|
{
|
||||||
PLAN_CONTEXT_SSO: True,
|
PLAN_CONTEXT_SSO: True,
|
||||||
|
|||||||
@ -104,13 +104,6 @@ def send_mail(
|
|||||||
# can't be converted to json)
|
# can't be converted to json)
|
||||||
message_object.attach(logo_data())
|
message_object.attach(logo_data())
|
||||||
|
|
||||||
if (
|
|
||||||
message_object.to
|
|
||||||
and isinstance(message_object.to[0], str)
|
|
||||||
and "=?utf-8?" in message_object.to[0]
|
|
||||||
):
|
|
||||||
message_object.to = [message_object.to[0].split("<")[-1].replace(">", "")]
|
|
||||||
|
|
||||||
LOGGER.debug("Sending mail", to=message_object.to)
|
LOGGER.debug("Sending mail", to=message_object.to)
|
||||||
backend.send_messages([message_object])
|
backend.send_messages([message_object])
|
||||||
Event.new(
|
Event.new(
|
||||||
|
|||||||
@ -97,37 +97,6 @@ class TestEmailStageSending(FlowTestCase):
|
|||||||
self.assertEqual(mail.outbox[0].subject, "authentik")
|
self.assertEqual(mail.outbox[0].subject, "authentik")
|
||||||
self.assertEqual(mail.outbox[0].to, [f"Test User Many Words <{long_user.email}>"])
|
self.assertEqual(mail.outbox[0].to, [f"Test User Many Words <{long_user.email}>"])
|
||||||
|
|
||||||
def test_utf8_name(self):
|
|
||||||
"""Test with pending user"""
|
|
||||||
plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()])
|
|
||||||
utf8_user = create_test_user()
|
|
||||||
utf8_user.name = "Cirilo ЉМНЊ el cirilico И̂ӢЙӤ "
|
|
||||||
utf8_user.email = "cyrillic@authentik.local"
|
|
||||||
utf8_user.save()
|
|
||||||
plan.context[PLAN_CONTEXT_PENDING_USER] = utf8_user
|
|
||||||
session = self.client.session
|
|
||||||
session[SESSION_KEY_PLAN] = plan
|
|
||||||
session.save()
|
|
||||||
Event.objects.filter(action=EventAction.EMAIL_SENT).delete()
|
|
||||||
|
|
||||||
url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
|
||||||
with patch(
|
|
||||||
"authentik.stages.email.models.EmailStage.backend_class",
|
|
||||||
PropertyMock(return_value=EmailBackend),
|
|
||||||
):
|
|
||||||
response = self.client.post(url)
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
self.assertStageResponse(
|
|
||||||
response,
|
|
||||||
self.flow,
|
|
||||||
response_errors={
|
|
||||||
"non_field_errors": [{"string": "email-sent", "code": "email-sent"}]
|
|
||||||
},
|
|
||||||
)
|
|
||||||
self.assertEqual(len(mail.outbox), 1)
|
|
||||||
self.assertEqual(mail.outbox[0].subject, "authentik")
|
|
||||||
self.assertEqual(mail.outbox[0].to, [f"{utf8_user.email}"])
|
|
||||||
|
|
||||||
def test_pending_fake_user(self):
|
def test_pending_fake_user(self):
|
||||||
"""Test with pending (fake) user"""
|
"""Test with pending (fake) user"""
|
||||||
self.flow.designation = FlowDesignation.RECOVERY
|
self.flow.designation = FlowDesignation.RECOVERY
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
"$schema": "http://json-schema.org/draft-07/schema",
|
"$schema": "http://json-schema.org/draft-07/schema",
|
||||||
"$id": "https://goauthentik.io/blueprints/schema.json",
|
"$id": "https://goauthentik.io/blueprints/schema.json",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"title": "authentik 2025.2.4 Blueprint schema",
|
"title": "authentik 2025.2.3 Blueprint schema",
|
||||||
"required": [
|
"required": [
|
||||||
"version",
|
"version",
|
||||||
"entries"
|
"entries"
|
||||||
@ -7885,11 +7885,6 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "uuid",
|
"format": "uuid",
|
||||||
"title": "Sync parent group"
|
"title": "Sync parent group"
|
||||||
},
|
|
||||||
"lookup_groups_from_user": {
|
|
||||||
"type": "boolean",
|
|
||||||
"title": "Lookup groups from user",
|
|
||||||
"description": "Lookup group membership based on a user attribute instead of a group attribute. This allows nested group resolution on systems like FreeIPA and Active Directory"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": []
|
"required": []
|
||||||
@ -8236,6 +8231,7 @@
|
|||||||
},
|
},
|
||||||
"identifier": {
|
"identifier": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
"maxLength": 255,
|
||||||
"minLength": 1,
|
"minLength": 1,
|
||||||
"title": "Identifier"
|
"title": "Identifier"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -31,7 +31,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- redis:/data
|
- redis:/data
|
||||||
server:
|
server:
|
||||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.2.4}
|
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.2.3}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command: server
|
command: server
|
||||||
environment:
|
environment:
|
||||||
@ -54,7 +54,7 @@ services:
|
|||||||
redis:
|
redis:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
worker:
|
worker:
|
||||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.2.4}
|
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.2.3}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command: worker
|
command: worker
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
14
go.mod
14
go.mod
@ -1,10 +1,9 @@
|
|||||||
module goauthentik.io
|
module goauthentik.io
|
||||||
|
|
||||||
go 1.24.0
|
go 1.24.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
beryju.io/ldap v0.1.0
|
beryju.io/ldap v0.1.0
|
||||||
github.com/coreos/go-oidc/v3 v3.14.1
|
github.com/coreos/go-oidc/v3 v3.13.0
|
||||||
github.com/getsentry/sentry-go v0.31.1
|
github.com/getsentry/sentry-go v0.31.1
|
||||||
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
|
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
|
||||||
github.com/go-ldap/ldap/v3 v3.4.10
|
github.com/go-ldap/ldap/v3 v3.4.10
|
||||||
@ -20,17 +19,17 @@ require (
|
|||||||
github.com/mitchellh/mapstructure v1.5.0
|
github.com/mitchellh/mapstructure v1.5.0
|
||||||
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
|
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
|
||||||
github.com/pires/go-proxyproto v0.8.0
|
github.com/pires/go-proxyproto v0.8.0
|
||||||
github.com/prometheus/client_golang v1.22.0
|
github.com/prometheus/client_golang v1.21.1
|
||||||
github.com/redis/go-redis/v9 v9.7.3
|
github.com/redis/go-redis/v9 v9.7.3
|
||||||
github.com/sethvargo/go-envconfig v1.1.1
|
github.com/sethvargo/go-envconfig v1.1.1
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/spf13/cobra v1.9.1
|
github.com/spf13/cobra v1.9.1
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.10.0
|
||||||
github.com/wwt/guac v1.3.2
|
github.com/wwt/guac v1.3.2
|
||||||
goauthentik.io/api/v3 v3.2025024.1
|
goauthentik.io/api/v3 v3.2025023.2
|
||||||
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
||||||
golang.org/x/oauth2 v0.29.0
|
golang.org/x/oauth2 v0.28.0
|
||||||
golang.org/x/sync v0.13.0
|
golang.org/x/sync v0.12.0
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
layeh.com/radius v0.0.0-20210819152912-ad72663a72ab
|
layeh.com/radius v0.0.0-20210819152912-ad72663a72ab
|
||||||
)
|
)
|
||||||
@ -60,6 +59,7 @@ require (
|
|||||||
github.com/go-openapi/validate v0.24.0 // indirect
|
github.com/go-openapi/validate v0.24.0 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
|
github.com/klauspost/compress v1.17.11 // indirect
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/oklog/ulid v1.3.1 // indirect
|
github.com/oklog/ulid v1.3.1 // indirect
|
||||||
@ -76,6 +76,6 @@ require (
|
|||||||
golang.org/x/crypto v0.36.0 // indirect
|
golang.org/x/crypto v0.36.0 // indirect
|
||||||
golang.org/x/sys v0.31.0 // indirect
|
golang.org/x/sys v0.31.0 // indirect
|
||||||
golang.org/x/text v0.23.0 // indirect
|
golang.org/x/text v0.23.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.5 // indirect
|
google.golang.org/protobuf v1.36.1 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
31
go.sum
31
go.sum
@ -55,8 +55,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
|
|||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/coreos/go-oidc/v3 v3.14.1 h1:9ePWwfdwC4QKRlCXsJGou56adA/owXczOzwKdOumLqk=
|
github.com/coreos/go-oidc/v3 v3.13.0 h1:M66zd0pcc5VxvBNM4pB331Wrsanby+QomQYjN8HamW8=
|
||||||
github.com/coreos/go-oidc/v3 v3.14.1/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU=
|
github.com/coreos/go-oidc/v3 v3.13.0/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
@ -148,9 +148,8 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
|
||||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
@ -208,8 +207,8 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF
|
|||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
@ -240,8 +239,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk=
|
||||||
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||||
@ -300,8 +299,8 @@ go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y
|
|||||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
goauthentik.io/api/v3 v3.2025024.1 h1:wYmpbNW1XptrjS5dlnZj8CrCs+JUGEVJYStrFdWL9aA=
|
goauthentik.io/api/v3 v3.2025023.2 h1:4XHlnykN5jQH78liQ4cp2Jf8eigvQImIJp+A+bsq1nA=
|
||||||
goauthentik.io/api/v3 v3.2025024.1/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
goauthentik.io/api/v3 v3.2025023.2/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
@ -396,8 +395,8 @@ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4Iltr
|
|||||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
|
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
|
||||||
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@ -412,8 +411,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
|||||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -600,8 +599,8 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
|||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
|||||||
@ -29,4 +29,4 @@ func UserAgent() string {
|
|||||||
return fmt.Sprintf("authentik@%s", FullVersion())
|
return fmt.Sprintf("authentik@%s", FullVersion())
|
||||||
}
|
}
|
||||||
|
|
||||||
const VERSION = "2025.2.4"
|
const VERSION = "2025.2.3"
|
||||||
|
|||||||
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.1007.0",
|
"aws-cdk": "^2.1006.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.1007.0",
|
"version": "2.1006.0",
|
||||||
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1007.0.tgz",
|
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1006.0.tgz",
|
||||||
"integrity": "sha512-/UOYOTGWUm+pP9qxg03tID5tL6euC+pb+xo0RBue+xhnUWwj/Bbsw6DbqbpOPMrNzTUxmM723/uMEQmM6S26dw==",
|
"integrity": "sha512-6qYnCt4mBN+3i/5F+FC2yMETkDHY/IL7gt3EuqKVPcaAO4jU7oXfVSlR60CYRkZWL4fnAurUV14RkJuJyVG/IA==",
|
||||||
"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.1007.0",
|
"aws-cdk": "^2.1006.0",
|
||||||
"cross-env": "^7.0.3"
|
"cross-env": "^7.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,7 @@ Parameters:
|
|||||||
Description: authentik Docker image
|
Description: authentik Docker image
|
||||||
AuthentikVersion:
|
AuthentikVersion:
|
||||||
Type: String
|
Type: String
|
||||||
Default: 2025.2.4
|
Default: 2025.2.3
|
||||||
Description: authentik Docker image tag
|
Description: authentik Docker image tag
|
||||||
AuthentikServerCPU:
|
AuthentikServerCPU:
|
||||||
Type: Number
|
Type: Number
|
||||||
|
|||||||
@ -6,23 +6,23 @@
|
|||||||
# Translators:
|
# Translators:
|
||||||
# Dario Rigolin, 2022
|
# Dario Rigolin, 2022
|
||||||
# aoor9, 2023
|
# aoor9, 2023
|
||||||
|
# Matteo Piccina <altermatte@gmail.com>, 2024
|
||||||
# Enrico Campani, 2024
|
# Enrico Campani, 2024
|
||||||
# Marco Vitale, 2024
|
# Marco Vitale, 2024
|
||||||
|
# Kowalski Dragon (kowalski7cc) <kowalski.7cc@gmail.com>, 2024
|
||||||
# Nicola Mersi, 2024
|
# Nicola Mersi, 2024
|
||||||
# tmassimi, 2024
|
# tmassimi, 2024
|
||||||
# Marc Schmitt, 2024
|
# Marc Schmitt, 2024
|
||||||
# albanobattistella <albanobattistella@gmail.com>, 2024
|
# albanobattistella <albanobattistella@gmail.com>, 2024
|
||||||
# Matteo Piccina <altermatte@gmail.com>, 2025
|
|
||||||
# Kowalski Dragon (kowalski7cc) <kowalski.7cc@gmail.com>, 2025
|
|
||||||
#
|
#
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-03-31 00:10+0000\n"
|
"POT-Creation-Date: 2025-02-14 14:49+0000\n"
|
||||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||||
"Last-Translator: Kowalski Dragon (kowalski7cc) <kowalski.7cc@gmail.com>, 2025\n"
|
"Last-Translator: albanobattistella <albanobattistella@gmail.com>, 2024\n"
|
||||||
"Language-Team: Italian (https://app.transifex.com/authentik/teams/119923/it/)\n"
|
"Language-Team: Italian (https://app.transifex.com/authentik/teams/119923/it/)\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
@ -130,10 +130,6 @@ msgstr "L'utente non ha accesso all'applicazione."
|
|||||||
msgid "Extra description not available"
|
msgid "Extra description not available"
|
||||||
msgstr "Descrizione extra non disponibile"
|
msgstr "Descrizione extra non disponibile"
|
||||||
|
|
||||||
#: authentik/core/api/groups.py
|
|
||||||
msgid "Cannot set group as parent of itself."
|
|
||||||
msgstr "Impossibile impostare il gruppo come padre di se stesso."
|
|
||||||
|
|
||||||
#: authentik/core/api/providers.py
|
#: authentik/core/api/providers.py
|
||||||
msgid ""
|
msgid ""
|
||||||
"When not set all providers are returned. When set to true, only backchannel "
|
"When not set all providers are returned. When set to true, only backchannel "
|
||||||
@ -181,14 +177,6 @@ msgstr "Aggiungi utente al gruppo"
|
|||||||
msgid "Remove user from group"
|
msgid "Remove user from group"
|
||||||
msgstr "Rimuovi l'utente dal gruppo"
|
msgstr "Rimuovi l'utente dal gruppo"
|
||||||
|
|
||||||
#: authentik/core/models.py
|
|
||||||
msgid "Enable superuser status"
|
|
||||||
msgstr "Abilita stato di superutente"
|
|
||||||
|
|
||||||
#: authentik/core/models.py
|
|
||||||
msgid "Disable superuser status"
|
|
||||||
msgstr "Disabilita stato di superutente"
|
|
||||||
|
|
||||||
#: authentik/core/models.py
|
#: authentik/core/models.py
|
||||||
msgid "User's display name."
|
msgid "User's display name."
|
||||||
msgstr "Nome visualizzato dell'utente."
|
msgstr "Nome visualizzato dell'utente."
|
||||||
@ -272,11 +260,11 @@ msgstr "Applicazioni"
|
|||||||
|
|
||||||
#: authentik/core/models.py
|
#: authentik/core/models.py
|
||||||
msgid "Application Entitlement"
|
msgid "Application Entitlement"
|
||||||
msgstr "Entitlement Applicazione"
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py
|
#: authentik/core/models.py
|
||||||
msgid "Application Entitlements"
|
msgid "Application Entitlements"
|
||||||
msgstr "Entitlements Applicazione"
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py
|
#: authentik/core/models.py
|
||||||
msgid "Use the source-specific identifier"
|
msgid "Use the source-specific identifier"
|
||||||
@ -563,6 +551,62 @@ msgstr "Mappatura Microsoft Entra Provider"
|
|||||||
msgid "Microsoft Entra Provider Mappings"
|
msgid "Microsoft Entra Provider Mappings"
|
||||||
msgstr "Mappature Microsoft Entra Provider"
|
msgstr "Mappature Microsoft Entra Provider"
|
||||||
|
|
||||||
|
#: authentik/enterprise/providers/rac/models.py
|
||||||
|
#: authentik/stages/user_login/models.py
|
||||||
|
msgid ""
|
||||||
|
"Determines how long a session lasts. Default of 0 means that the sessions "
|
||||||
|
"lasts until the browser is closed. (Format: hours=-1;minutes=-2;seconds=-3)"
|
||||||
|
msgstr ""
|
||||||
|
"Determina quanto può durare una sessione. Se impostato a 0, la sessione "
|
||||||
|
"durerà fino alla chiusura del browser. (Formato: "
|
||||||
|
"hours=-1;minutes=-2;seconds=-3)"
|
||||||
|
|
||||||
|
#: authentik/enterprise/providers/rac/models.py
|
||||||
|
msgid "When set to true, connection tokens will be deleted upon disconnect."
|
||||||
|
msgstr ""
|
||||||
|
"Se impostato su vero, i token di connessione verranno eliminati alla "
|
||||||
|
"disconnessione."
|
||||||
|
|
||||||
|
#: authentik/enterprise/providers/rac/models.py
|
||||||
|
msgid "RAC Provider"
|
||||||
|
msgstr "Fornitore di controllo dell'accesso remoto"
|
||||||
|
|
||||||
|
#: authentik/enterprise/providers/rac/models.py
|
||||||
|
msgid "RAC Providers"
|
||||||
|
msgstr "Fornitori di controllo dell'accesso remoto"
|
||||||
|
|
||||||
|
#: authentik/enterprise/providers/rac/models.py
|
||||||
|
msgid "RAC Endpoint"
|
||||||
|
msgstr "Endpoint RAC"
|
||||||
|
|
||||||
|
#: authentik/enterprise/providers/rac/models.py
|
||||||
|
msgid "RAC Endpoints"
|
||||||
|
msgstr "Endpoints RAC"
|
||||||
|
|
||||||
|
#: authentik/enterprise/providers/rac/models.py
|
||||||
|
msgid "RAC Provider Property Mapping"
|
||||||
|
msgstr "Mappatura delle proprietà del provider RAC"
|
||||||
|
|
||||||
|
#: authentik/enterprise/providers/rac/models.py
|
||||||
|
msgid "RAC Provider Property Mappings"
|
||||||
|
msgstr "Mappature proprietà del provider RAC"
|
||||||
|
|
||||||
|
#: authentik/enterprise/providers/rac/models.py
|
||||||
|
msgid "RAC Connection token"
|
||||||
|
msgstr "RAC Connection token"
|
||||||
|
|
||||||
|
#: authentik/enterprise/providers/rac/models.py
|
||||||
|
msgid "RAC Connection tokens"
|
||||||
|
msgstr "RAC Connection tokens"
|
||||||
|
|
||||||
|
#: authentik/enterprise/providers/rac/views.py
|
||||||
|
msgid "Maximum connection limit reached."
|
||||||
|
msgstr "Limite massimo di connessioni raggiunto."
|
||||||
|
|
||||||
|
#: authentik/enterprise/providers/rac/views.py
|
||||||
|
msgid "(You are already connected in another tab/window)"
|
||||||
|
msgstr "(Sei già connesso in un'altra scheda/finestra)"
|
||||||
|
|
||||||
#: authentik/enterprise/providers/ssf/models.py
|
#: authentik/enterprise/providers/ssf/models.py
|
||||||
#: authentik/providers/oauth2/models.py
|
#: authentik/providers/oauth2/models.py
|
||||||
msgid "Signing Key"
|
msgid "Signing Key"
|
||||||
@ -570,39 +614,39 @@ msgstr "Chiave di firma"
|
|||||||
|
|
||||||
#: authentik/enterprise/providers/ssf/models.py
|
#: authentik/enterprise/providers/ssf/models.py
|
||||||
msgid "Key used to sign the SSF Events."
|
msgid "Key used to sign the SSF Events."
|
||||||
msgstr "Chiave utilizzata per firmare gli eventi SSF."
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/enterprise/providers/ssf/models.py
|
#: authentik/enterprise/providers/ssf/models.py
|
||||||
msgid "Shared Signals Framework Provider"
|
msgid "Shared Signals Framework Provider"
|
||||||
msgstr "Fornitore Shared Signals Framework"
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/enterprise/providers/ssf/models.py
|
#: authentik/enterprise/providers/ssf/models.py
|
||||||
msgid "Shared Signals Framework Providers"
|
msgid "Shared Signals Framework Providers"
|
||||||
msgstr "Fornitori Shared Signals Framework"
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/enterprise/providers/ssf/models.py
|
#: authentik/enterprise/providers/ssf/models.py
|
||||||
msgid "Add stream to SSF provider"
|
msgid "Add stream to SSF provider"
|
||||||
msgstr "Aggiungi Stream al provider SSF"
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/enterprise/providers/ssf/models.py
|
#: authentik/enterprise/providers/ssf/models.py
|
||||||
msgid "SSF Stream"
|
msgid "SSF Stream"
|
||||||
msgstr "SSF Stream"
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/enterprise/providers/ssf/models.py
|
#: authentik/enterprise/providers/ssf/models.py
|
||||||
msgid "SSF Streams"
|
msgid "SSF Streams"
|
||||||
msgstr "SSF Streams"
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/enterprise/providers/ssf/models.py
|
#: authentik/enterprise/providers/ssf/models.py
|
||||||
msgid "SSF Stream Event"
|
msgid "SSF Stream Event"
|
||||||
msgstr "Evento di Stream SSF"
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/enterprise/providers/ssf/models.py
|
#: authentik/enterprise/providers/ssf/models.py
|
||||||
msgid "SSF Stream Events"
|
msgid "SSF Stream Events"
|
||||||
msgstr "Eventi di Stream SSF"
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/enterprise/providers/ssf/tasks.py
|
#: authentik/enterprise/providers/ssf/tasks.py
|
||||||
msgid "Failed to send request"
|
msgid "Failed to send request"
|
||||||
msgstr "Impossibile inviare la richiesta"
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/models.py
|
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/models.py
|
||||||
msgid "Endpoint Authenticator Google Device Trust Connector Stage"
|
msgid "Endpoint Authenticator Google Device Trust Connector Stage"
|
||||||
@ -668,26 +712,9 @@ msgid "Slack Webhook (Slack/Discord)"
|
|||||||
msgstr "Slack Webhook (Slack/Discord)"
|
msgstr "Slack Webhook (Slack/Discord)"
|
||||||
|
|
||||||
#: authentik/events/models.py
|
#: authentik/events/models.py
|
||||||
#: authentik/stages/authenticator_validate/models.py
|
|
||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "Email"
|
msgstr "Email"
|
||||||
|
|
||||||
#: authentik/events/models.py
|
|
||||||
msgid ""
|
|
||||||
"Customize the body of the request. Mapping should return data that is JSON-"
|
|
||||||
"serializable."
|
|
||||||
msgstr ""
|
|
||||||
"Personalizza il corpo della richiesta. Il mapping dovrebbe restituire dati "
|
|
||||||
"serializzabili in JSON."
|
|
||||||
|
|
||||||
#: authentik/events/models.py
|
|
||||||
msgid ""
|
|
||||||
"Configure additional headers to be sent. Mapping should return a dictionary "
|
|
||||||
"of key-value pairs"
|
|
||||||
msgstr ""
|
|
||||||
"Configurare le intestazioni aggiuntive da inviare. Il mapping dovrebbe "
|
|
||||||
"restituire un dizionario di coppie chiave-valore."
|
|
||||||
|
|
||||||
#: authentik/events/models.py
|
#: authentik/events/models.py
|
||||||
msgid ""
|
msgid ""
|
||||||
"Only send notification once, for example when sending a webhook into a chat "
|
"Only send notification once, for example when sending a webhook into a chat "
|
||||||
@ -917,7 +944,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: authentik/flows/models.py
|
#: authentik/flows/models.py
|
||||||
msgid "Evaluate policies when the Stage is presented to the user."
|
msgid "Evaluate policies when the Stage is presented to the user."
|
||||||
msgstr "Valutare i criteri quando la fase viene presentata all'utente."
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/flows/models.py
|
#: authentik/flows/models.py
|
||||||
msgid ""
|
msgid ""
|
||||||
@ -959,14 +986,6 @@ msgstr "Tokens del flusso"
|
|||||||
msgid "Invalid next URL"
|
msgid "Invalid next URL"
|
||||||
msgstr "URL successivo non valido"
|
msgstr "URL successivo non valido"
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/models.py
|
|
||||||
msgid ""
|
|
||||||
"When enabled, provider will not modify or create objects in the remote "
|
|
||||||
"system."
|
|
||||||
msgstr ""
|
|
||||||
"Quando abilitato, il provider non modificherà o creerà oggetti nel sistema "
|
|
||||||
"remoto."
|
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
msgid "Starting full provider sync"
|
msgid "Starting full provider sync"
|
||||||
msgstr "Avvio della sincronizzazione completa del provider"
|
msgstr "Avvio della sincronizzazione completa del provider"
|
||||||
@ -981,10 +1000,6 @@ msgstr "Sincronizzando pagina {page} degli utenti"
|
|||||||
msgid "Syncing page {page} of groups"
|
msgid "Syncing page {page} of groups"
|
||||||
msgstr "Sincronizzando pagina {page} dei gruppi"
|
msgstr "Sincronizzando pagina {page} dei gruppi"
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
|
||||||
msgid "Dropping mutating request due to dry run"
|
|
||||||
msgstr "Richiesta di mutazione ignorata a causa della prova di funzionamento"
|
|
||||||
|
|
||||||
#: authentik/lib/sync/outgoing/tasks.py
|
#: authentik/lib/sync/outgoing/tasks.py
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Stopping sync due to error: {error}"
|
msgid "Stopping sync due to error: {error}"
|
||||||
@ -1197,14 +1212,6 @@ msgstr "GeoIP: indirizzo IP del client non trovato nel database della città."
|
|||||||
msgid "Client IP is not in an allowed country."
|
msgid "Client IP is not in an allowed country."
|
||||||
msgstr "L'IP del client non si trova in un paese consentito."
|
msgstr "L'IP del client non si trova in un paese consentito."
|
||||||
|
|
||||||
#: authentik/policies/geoip/models.py
|
|
||||||
msgid "Distance from previous authentication is larger than threshold."
|
|
||||||
msgstr "La distanza dall'autenticazione precedente è maggiore della soglia."
|
|
||||||
|
|
||||||
#: authentik/policies/geoip/models.py
|
|
||||||
msgid "Distance is further than possible."
|
|
||||||
msgstr "La distanza è maggiore del possibile."
|
|
||||||
|
|
||||||
#: authentik/policies/geoip/models.py
|
#: authentik/policies/geoip/models.py
|
||||||
msgid "GeoIP Policy"
|
msgid "GeoIP Policy"
|
||||||
msgstr "Criterio GeoIP"
|
msgstr "Criterio GeoIP"
|
||||||
@ -1337,22 +1344,6 @@ msgstr "Punteggio di reputazione"
|
|||||||
msgid "Reputation Scores"
|
msgid "Reputation Scores"
|
||||||
msgstr "Punteggi di reputazione"
|
msgstr "Punteggi di reputazione"
|
||||||
|
|
||||||
#: authentik/policies/templates/policies/buffer.html
|
|
||||||
msgid "Waiting for authentication..."
|
|
||||||
msgstr "In attesa di autenticazione..."
|
|
||||||
|
|
||||||
#: authentik/policies/templates/policies/buffer.html
|
|
||||||
msgid ""
|
|
||||||
"You're already authenticating in another tab. This page will refresh once "
|
|
||||||
"authentication is completed."
|
|
||||||
msgstr ""
|
|
||||||
"Ti stai già autenticando in un'altra scheda. Questa pagina si aggiornerà una"
|
|
||||||
" volta completata l'autenticazione."
|
|
||||||
|
|
||||||
#: authentik/policies/templates/policies/buffer.html
|
|
||||||
msgid "Authenticate in this tab"
|
|
||||||
msgstr "Autenticati in questa scheda"
|
|
||||||
|
|
||||||
#: authentik/policies/templates/policies/denied.html
|
#: authentik/policies/templates/policies/denied.html
|
||||||
msgid "Permission denied"
|
msgid "Permission denied"
|
||||||
msgstr "Permesso negato"
|
msgstr "Permesso negato"
|
||||||
@ -1540,14 +1531,6 @@ msgstr "RS256 (Crittografia Asimmetrica)"
|
|||||||
msgid "ES256 (Asymmetric Encryption)"
|
msgid "ES256 (Asymmetric Encryption)"
|
||||||
msgstr "ES256 (Crittografia Asimmetrica)"
|
msgstr "ES256 (Crittografia Asimmetrica)"
|
||||||
|
|
||||||
#: authentik/providers/oauth2/models.py
|
|
||||||
msgid "ES384 (Asymmetric Encryption)"
|
|
||||||
msgstr "ES384 (Crittografia Asimmetrica)"
|
|
||||||
|
|
||||||
#: authentik/providers/oauth2/models.py
|
|
||||||
msgid "ES512 (Asymmetric Encryption)"
|
|
||||||
msgstr "ES512 (Crittografia Asimmetrica)"
|
|
||||||
|
|
||||||
#: authentik/providers/oauth2/models.py
|
#: authentik/providers/oauth2/models.py
|
||||||
msgid "Scope used by the client"
|
msgid "Scope used by the client"
|
||||||
msgstr "Scope usato dall'utente"
|
msgstr "Scope usato dall'utente"
|
||||||
@ -1831,61 +1814,6 @@ msgstr "Provider Proxy"
|
|||||||
msgid "Proxy Providers"
|
msgid "Proxy Providers"
|
||||||
msgstr "Providers Proxy"
|
msgstr "Providers Proxy"
|
||||||
|
|
||||||
#: authentik/providers/rac/models.py authentik/stages/user_login/models.py
|
|
||||||
msgid ""
|
|
||||||
"Determines how long a session lasts. Default of 0 means that the sessions "
|
|
||||||
"lasts until the browser is closed. (Format: hours=-1;minutes=-2;seconds=-3)"
|
|
||||||
msgstr ""
|
|
||||||
"Determina quanto può durare una sessione. Se impostato a 0, la sessione "
|
|
||||||
"durerà fino alla chiusura del browser. (Formato: "
|
|
||||||
"hours=-1;minutes=-2;seconds=-3)"
|
|
||||||
|
|
||||||
#: authentik/providers/rac/models.py
|
|
||||||
msgid "When set to true, connection tokens will be deleted upon disconnect."
|
|
||||||
msgstr ""
|
|
||||||
"Se impostato su vero, i token di connessione verranno eliminati alla "
|
|
||||||
"disconnessione."
|
|
||||||
|
|
||||||
#: authentik/providers/rac/models.py
|
|
||||||
msgid "RAC Provider"
|
|
||||||
msgstr "Fornitore di controllo dell'accesso remoto"
|
|
||||||
|
|
||||||
#: authentik/providers/rac/models.py
|
|
||||||
msgid "RAC Providers"
|
|
||||||
msgstr "Fornitori di controllo dell'accesso remoto"
|
|
||||||
|
|
||||||
#: authentik/providers/rac/models.py
|
|
||||||
msgid "RAC Endpoint"
|
|
||||||
msgstr "Endpoint RAC"
|
|
||||||
|
|
||||||
#: authentik/providers/rac/models.py
|
|
||||||
msgid "RAC Endpoints"
|
|
||||||
msgstr "Endpoints RAC"
|
|
||||||
|
|
||||||
#: authentik/providers/rac/models.py
|
|
||||||
msgid "RAC Provider Property Mapping"
|
|
||||||
msgstr "Mappatura delle proprietà del provider RAC"
|
|
||||||
|
|
||||||
#: authentik/providers/rac/models.py
|
|
||||||
msgid "RAC Provider Property Mappings"
|
|
||||||
msgstr "Mappature proprietà del provider RAC"
|
|
||||||
|
|
||||||
#: authentik/providers/rac/models.py
|
|
||||||
msgid "RAC Connection token"
|
|
||||||
msgstr "RAC Connection token"
|
|
||||||
|
|
||||||
#: authentik/providers/rac/models.py
|
|
||||||
msgid "RAC Connection tokens"
|
|
||||||
msgstr "RAC Connection tokens"
|
|
||||||
|
|
||||||
#: authentik/providers/rac/views.py
|
|
||||||
msgid "Maximum connection limit reached."
|
|
||||||
msgstr "Limite massimo di connessioni raggiunto."
|
|
||||||
|
|
||||||
#: authentik/providers/rac/views.py
|
|
||||||
msgid "(You are already connected in another tab/window)"
|
|
||||||
msgstr "(Sei già connesso in un'altra scheda/finestra)"
|
|
||||||
|
|
||||||
#: authentik/providers/radius/models.py
|
#: authentik/providers/radius/models.py
|
||||||
msgid "Shared secret between clients and server to hash packets."
|
msgid "Shared secret between clients and server to hash packets."
|
||||||
msgstr "Segreto condiviso tra client e server per hashare i pacchetti."
|
msgstr "Segreto condiviso tra client e server per hashare i pacchetti."
|
||||||
@ -1973,20 +1901,6 @@ msgstr ""
|
|||||||
"Configura il modo in cui verrà creato il valore NameID. Se lasciato vuoto, "
|
"Configura il modo in cui verrà creato il valore NameID. Se lasciato vuoto, "
|
||||||
"verrà considerato il NameIDPolicy della richiesta in arrivo"
|
"verrà considerato il NameIDPolicy della richiesta in arrivo"
|
||||||
|
|
||||||
#: authentik/providers/saml/models.py
|
|
||||||
msgid "AuthnContextClassRef Property Mapping"
|
|
||||||
msgstr "Mapping delle proprietà AuthnContextClassRef"
|
|
||||||
|
|
||||||
#: authentik/providers/saml/models.py
|
|
||||||
msgid ""
|
|
||||||
"Configure how the AuthnContextClassRef value will be created. When left "
|
|
||||||
"empty, the AuthnContextClassRef will be set based on which authentication "
|
|
||||||
"methods the user used to authenticate."
|
|
||||||
msgstr ""
|
|
||||||
"Configura come verrà creato il valore AuthnContextClassRef. Se lasciato "
|
|
||||||
"vuoto, AuthnContextClassRef verrà impostato in base ai metodi di "
|
|
||||||
"autenticazione utilizzati dall'utente."
|
|
||||||
|
|
||||||
#: authentik/providers/saml/models.py
|
#: authentik/providers/saml/models.py
|
||||||
msgid ""
|
msgid ""
|
||||||
"Assertion valid not before current time + this value (Format: "
|
"Assertion valid not before current time + this value (Format: "
|
||||||
@ -2128,18 +2042,6 @@ msgstr "Provider SAML dai Metadati"
|
|||||||
msgid "SAML Providers from Metadata"
|
msgid "SAML Providers from Metadata"
|
||||||
msgstr "Providers SAML dai Metadati"
|
msgstr "Providers SAML dai Metadati"
|
||||||
|
|
||||||
#: authentik/providers/scim/models.py
|
|
||||||
msgid "Default"
|
|
||||||
msgstr "Predefinito"
|
|
||||||
|
|
||||||
#: authentik/providers/scim/models.py
|
|
||||||
msgid "AWS"
|
|
||||||
msgstr "AWS"
|
|
||||||
|
|
||||||
#: authentik/providers/scim/models.py
|
|
||||||
msgid "Slack"
|
|
||||||
msgstr "Slack"
|
|
||||||
|
|
||||||
#: authentik/providers/scim/models.py
|
#: authentik/providers/scim/models.py
|
||||||
msgid "Base URL to SCIM requests, usually ends in /v2"
|
msgid "Base URL to SCIM requests, usually ends in /v2"
|
||||||
msgstr "URL di base per le richieste SCIM, di solito termina con /v2"
|
msgstr "URL di base per le richieste SCIM, di solito termina con /v2"
|
||||||
@ -2148,16 +2050,6 @@ msgstr "URL di base per le richieste SCIM, di solito termina con /v2"
|
|||||||
msgid "Authentication token"
|
msgid "Authentication token"
|
||||||
msgstr "Token di autenticazione"
|
msgstr "Token di autenticazione"
|
||||||
|
|
||||||
#: authentik/providers/scim/models.py
|
|
||||||
msgid "SCIM Compatibility Mode"
|
|
||||||
msgstr "SCIM Modalità di Compatibilità"
|
|
||||||
|
|
||||||
#: authentik/providers/scim/models.py
|
|
||||||
msgid "Alter authentik behavior for vendor-specific SCIM implementations."
|
|
||||||
msgstr ""
|
|
||||||
"Modifica il comportamento di autenticazione per le implementazioni SCIM "
|
|
||||||
"specifiche del fornitore."
|
|
||||||
|
|
||||||
#: authentik/providers/scim/models.py
|
#: authentik/providers/scim/models.py
|
||||||
msgid "SCIM Provider"
|
msgid "SCIM Provider"
|
||||||
msgstr "Privider SCIM"
|
msgstr "Privider SCIM"
|
||||||
@ -2233,7 +2125,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: authentik/sources/kerberos/models.py
|
#: authentik/sources/kerberos/models.py
|
||||||
msgid "KAdmin server type"
|
msgid "KAdmin server type"
|
||||||
msgstr "Tipo server KAdmin"
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/sources/kerberos/models.py
|
#: authentik/sources/kerberos/models.py
|
||||||
msgid "Sync users from Kerberos into authentik"
|
msgid "Sync users from Kerberos into authentik"
|
||||||
@ -2838,117 +2730,6 @@ msgstr "Dispositivo Duo"
|
|||||||
msgid "Duo Devices"
|
msgid "Duo Devices"
|
||||||
msgstr "Dispositivi Duo"
|
msgstr "Dispositivi Duo"
|
||||||
|
|
||||||
#: authentik/stages/authenticator_email/models.py
|
|
||||||
msgid "Email OTP"
|
|
||||||
msgstr "Email OTP"
|
|
||||||
|
|
||||||
#: authentik/stages/authenticator_email/models.py
|
|
||||||
#: authentik/stages/email/models.py
|
|
||||||
msgid ""
|
|
||||||
"When enabled, global Email connection settings will be used and connection "
|
|
||||||
"settings below will be ignored."
|
|
||||||
msgstr ""
|
|
||||||
"Se abilitato, verranno utilizzate le impostazioni di connessione e-mail "
|
|
||||||
"globali e le impostazioni di connessione riportate di seguito verranno "
|
|
||||||
"ignorate."
|
|
||||||
|
|
||||||
#: authentik/stages/authenticator_email/models.py
|
|
||||||
#: authentik/stages/email/models.py
|
|
||||||
msgid "Time the token sent is valid (Format: hours=3,minutes=17,seconds=300)."
|
|
||||||
msgstr ""
|
|
||||||
"Tempo di validità del token inviato (formato: "
|
|
||||||
"hours=3,minutes=17,seconds=300)."
|
|
||||||
|
|
||||||
#: authentik/stages/authenticator_email/models.py
|
|
||||||
msgid "Email Authenticator Setup Stage"
|
|
||||||
msgstr "Fase di configurazione dell'autenticatore email"
|
|
||||||
|
|
||||||
#: authentik/stages/authenticator_email/models.py
|
|
||||||
msgid "Email Authenticator Setup Stages"
|
|
||||||
msgstr "Fasi di configurazione dell'autenticatore email"
|
|
||||||
|
|
||||||
#: authentik/stages/authenticator_email/models.py
|
|
||||||
#: authentik/stages/authenticator_email/stage.py
|
|
||||||
#: authentik/stages/email/stage.py
|
|
||||||
msgid "Exception occurred while rendering E-mail template"
|
|
||||||
msgstr ""
|
|
||||||
"Eccezione verificatasi durante la visualizzazione del modello di posta "
|
|
||||||
"elettronica"
|
|
||||||
|
|
||||||
#: authentik/stages/authenticator_email/models.py
|
|
||||||
msgid "Email Device"
|
|
||||||
msgstr "Dispositivo email"
|
|
||||||
|
|
||||||
#: authentik/stages/authenticator_email/models.py
|
|
||||||
msgid "Email Devices"
|
|
||||||
msgstr "Dispositivi email"
|
|
||||||
|
|
||||||
#: authentik/stages/authenticator_email/stage.py
|
|
||||||
#: authentik/stages/authenticator_sms/stage.py
|
|
||||||
#: authentik/stages/authenticator_totp/stage.py
|
|
||||||
msgid "Code does not match"
|
|
||||||
msgstr "Il codice non corrisponde"
|
|
||||||
|
|
||||||
#: authentik/stages/authenticator_email/stage.py
|
|
||||||
msgid "Invalid email"
|
|
||||||
msgstr "Email non valida"
|
|
||||||
|
|
||||||
#: authentik/stages/authenticator_email/templates/email/email_otp.html
|
|
||||||
#: authentik/stages/email/templates/email/password_reset.html
|
|
||||||
#, python-format
|
|
||||||
msgid ""
|
|
||||||
"\n"
|
|
||||||
" Hi %(username)s,\n"
|
|
||||||
" "
|
|
||||||
msgstr ""
|
|
||||||
"\n"
|
|
||||||
" Ciao %(username)s,\n"
|
|
||||||
" "
|
|
||||||
|
|
||||||
#: authentik/stages/authenticator_email/templates/email/email_otp.html
|
|
||||||
msgid ""
|
|
||||||
"\n"
|
|
||||||
" Email MFA code.\n"
|
|
||||||
" "
|
|
||||||
msgstr ""
|
|
||||||
"\n"
|
|
||||||
" Codice MFA via e-mail.\n"
|
|
||||||
" "
|
|
||||||
|
|
||||||
#: authentik/stages/authenticator_email/templates/email/email_otp.html
|
|
||||||
#, python-format
|
|
||||||
msgid ""
|
|
||||||
"\n"
|
|
||||||
" If you did not request this code, please ignore this email. The code above is valid for %(expires)s.\n"
|
|
||||||
" "
|
|
||||||
msgstr ""
|
|
||||||
"\n"
|
|
||||||
" Se non hai richiesto questo codice, ignora questa email. Il codice sopra riportato è valido per %(expires)s.\n"
|
|
||||||
" "
|
|
||||||
|
|
||||||
#: authentik/stages/authenticator_email/templates/email/email_otp.txt
|
|
||||||
#: authentik/stages/email/templates/email/password_reset.txt
|
|
||||||
#, python-format
|
|
||||||
msgid "Hi %(username)s,"
|
|
||||||
msgstr "Ciao %(username)s,"
|
|
||||||
|
|
||||||
#: authentik/stages/authenticator_email/templates/email/email_otp.txt
|
|
||||||
msgid ""
|
|
||||||
"\n"
|
|
||||||
"Email MFA code\n"
|
|
||||||
msgstr ""
|
|
||||||
"\n"
|
|
||||||
"Codice e-mail MFA\n"
|
|
||||||
|
|
||||||
#: authentik/stages/authenticator_email/templates/email/email_otp.txt
|
|
||||||
#, python-format
|
|
||||||
msgid ""
|
|
||||||
"\n"
|
|
||||||
"If you did not request this code, please ignore this email. The code above is valid for %(expires)s.\n"
|
|
||||||
msgstr ""
|
|
||||||
"\n"
|
|
||||||
"Se non hai richiesto questo codice, ignora questa email. Il codice sopra riportato è valido per %(expires)s.\n"
|
|
||||||
|
|
||||||
#: authentik/stages/authenticator_sms/models.py
|
#: authentik/stages/authenticator_sms/models.py
|
||||||
msgid ""
|
msgid ""
|
||||||
"When enabled, the Phone number is only used during enrollment to verify the "
|
"When enabled, the Phone number is only used during enrollment to verify the "
|
||||||
@ -2987,6 +2768,11 @@ msgstr "Dispositivo SMS"
|
|||||||
msgid "SMS Devices"
|
msgid "SMS Devices"
|
||||||
msgstr "Dispositivi SMS"
|
msgstr "Dispositivi SMS"
|
||||||
|
|
||||||
|
#: authentik/stages/authenticator_sms/stage.py
|
||||||
|
#: authentik/stages/authenticator_totp/stage.py
|
||||||
|
msgid "Code does not match"
|
||||||
|
msgstr "Il codice non corrisponde"
|
||||||
|
|
||||||
#: authentik/stages/authenticator_sms/stage.py
|
#: authentik/stages/authenticator_sms/stage.py
|
||||||
msgid "Invalid phone number"
|
msgid "Invalid phone number"
|
||||||
msgstr "Numero di telefono non valido"
|
msgstr "Numero di telefono non valido"
|
||||||
@ -3227,10 +3013,23 @@ msgstr "Ripristino password"
|
|||||||
msgid "Account Confirmation"
|
msgid "Account Confirmation"
|
||||||
msgstr "Conferma dell'account"
|
msgstr "Conferma dell'account"
|
||||||
|
|
||||||
|
#: authentik/stages/email/models.py
|
||||||
|
msgid ""
|
||||||
|
"When enabled, global Email connection settings will be used and connection "
|
||||||
|
"settings below will be ignored."
|
||||||
|
msgstr ""
|
||||||
|
"Se abilitato, verranno utilizzate le impostazioni di connessione e-mail "
|
||||||
|
"globali e le impostazioni di connessione riportate di seguito verranno "
|
||||||
|
"ignorate."
|
||||||
|
|
||||||
#: authentik/stages/email/models.py
|
#: authentik/stages/email/models.py
|
||||||
msgid "Activate users upon completion of stage."
|
msgid "Activate users upon completion of stage."
|
||||||
msgstr "Attiva gli utenti al completamento della fase."
|
msgstr "Attiva gli utenti al completamento della fase."
|
||||||
|
|
||||||
|
#: authentik/stages/email/models.py
|
||||||
|
msgid "Time in minutes the token sent is valid."
|
||||||
|
msgstr "Tempo in minuti in cui il token inviato è valido."
|
||||||
|
|
||||||
#: authentik/stages/email/models.py
|
#: authentik/stages/email/models.py
|
||||||
msgid "Email Stage"
|
msgid "Email Stage"
|
||||||
msgstr "Fase email"
|
msgstr "Fase email"
|
||||||
@ -3239,6 +3038,12 @@ msgstr "Fase email"
|
|||||||
msgid "Email Stages"
|
msgid "Email Stages"
|
||||||
msgstr "Fasi Email"
|
msgstr "Fasi Email"
|
||||||
|
|
||||||
|
#: authentik/stages/email/stage.py
|
||||||
|
msgid "Exception occurred while rendering E-mail template"
|
||||||
|
msgstr ""
|
||||||
|
"Eccezione verificatasi durante la visualizzazione del modello di posta "
|
||||||
|
"elettronica"
|
||||||
|
|
||||||
#: authentik/stages/email/stage.py
|
#: authentik/stages/email/stage.py
|
||||||
msgid "Successfully verified Email."
|
msgid "Successfully verified Email."
|
||||||
msgstr "Email verificato con successo."
|
msgstr "Email verificato con successo."
|
||||||
@ -3322,6 +3127,17 @@ msgstr ""
|
|||||||
"\n"
|
"\n"
|
||||||
"Questa email è stata inviata dal trasporto delle notifiche %(name)s.\n"
|
"Questa email è stata inviata dal trasporto delle notifiche %(name)s.\n"
|
||||||
|
|
||||||
|
#: authentik/stages/email/templates/email/password_reset.html
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" Hi %(username)s,\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
" Ciao %(username)s,\n"
|
||||||
|
" "
|
||||||
|
|
||||||
#: authentik/stages/email/templates/email/password_reset.html
|
#: authentik/stages/email/templates/email/password_reset.html
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
@ -3342,6 +3158,11 @@ msgstr ""
|
|||||||
" Se non hai richiesto una modifica della password, ignora questa e-mail. Il link sopra è valido per %(expires)s.\n"
|
" Se non hai richiesto una modifica della password, ignora questa e-mail. Il link sopra è valido per %(expires)s.\n"
|
||||||
" "
|
" "
|
||||||
|
|
||||||
|
#: authentik/stages/email/templates/email/password_reset.txt
|
||||||
|
#, python-format
|
||||||
|
msgid "Hi %(username)s,"
|
||||||
|
msgstr "Ciao %(username)s,"
|
||||||
|
|
||||||
#: authentik/stages/email/templates/email/password_reset.txt
|
#: authentik/stages/email/templates/email/password_reset.txt
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
@ -3671,7 +3492,6 @@ msgstr ""
|
|||||||
#: authentik/stages/redirect/api.py
|
#: authentik/stages/redirect/api.py
|
||||||
msgid "Target Flow should be present when mode is Flow."
|
msgid "Target Flow should be present when mode is Flow."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Il flusso target dovrebbe essere presente quando la modalità è Flusso."
|
|
||||||
|
|
||||||
#: authentik/stages/redirect/models.py
|
#: authentik/stages/redirect/models.py
|
||||||
msgid "Redirect Stage"
|
msgid "Redirect Stage"
|
||||||
|
|||||||
Binary file not shown.
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "@goauthentik/authentik",
|
"name": "@goauthentik/authentik",
|
||||||
"version": "2025.2.4",
|
"version": "2025.2.3",
|
||||||
"private": true
|
"private": true
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file TypeScript type definitions for eslint-plugin-react-hooks
|
|
||||||
*/
|
|
||||||
declare module "eslint-plugin-react-hooks" {
|
|
||||||
import { ESLint } from "eslint";
|
|
||||||
// We have to do this because ESLint aliases the namespace and class simultaneously.
|
|
||||||
type PluginInstance = ESLint.Plugin;
|
|
||||||
const Plugin: PluginInstance;
|
|
||||||
|
|
||||||
export default Plugin;
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file TypeScript type definitions for eslint-plugin-react
|
|
||||||
*/
|
|
||||||
declare module "eslint-plugin-react" {
|
|
||||||
import { ESLint } from "eslint";
|
|
||||||
// We have to do this because ESLint aliases the namespace and class simultaneously.
|
|
||||||
type PluginInstance = ESLint.Plugin;
|
|
||||||
const Plugin: PluginInstance;
|
|
||||||
|
|
||||||
export default Plugin;
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2025 Authentik Security, Inc.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
|
||||||
associated documentation files (the "Software"), to deal in the Software without restriction,
|
|
||||||
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
|
||||||
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
|
||||||
portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
|
||||||
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
|
||||||
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
# `@goauthentik/eslint-config`
|
|
||||||
|
|
||||||
This package contains the ESLint configuration used by authentik.
|
|
||||||
While it is possible to use this configuration outside of our projects,
|
|
||||||
you may find that it is not as useful as other popular configurations.
|
|
||||||
@ -1,72 +0,0 @@
|
|||||||
import eslint from "@eslint/js";
|
|
||||||
import { javaScriptConfig } from "@goauthentik/eslint-config/javascript-config";
|
|
||||||
import { reactConfig } from "@goauthentik/eslint-config/react-config";
|
|
||||||
import { typescriptConfig } from "@goauthentik/eslint-config/typescript-config";
|
|
||||||
import * as litconf from "eslint-plugin-lit";
|
|
||||||
import * as wcconf from "eslint-plugin-wc";
|
|
||||||
import tseslint from "typescript-eslint";
|
|
||||||
|
|
||||||
// @ts-check
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef ESLintPackageConfigOptions Options for creating package ESLint configuration.
|
|
||||||
* @property {string[]} [ignorePatterns] Override ignore patterns for ESLint.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {string[]} Default Ignore patterns for ESLint.
|
|
||||||
*/
|
|
||||||
export const DefaultIgnorePatterns = [
|
|
||||||
// ---
|
|
||||||
"**/*.md",
|
|
||||||
"**/out",
|
|
||||||
"**/dist",
|
|
||||||
"**/.wireit",
|
|
||||||
"website/build/**",
|
|
||||||
"website/.docusaurus/**",
|
|
||||||
"**/node_modules",
|
|
||||||
"**/coverage",
|
|
||||||
"**/storybook-static",
|
|
||||||
"**/locale-codes.ts",
|
|
||||||
"**/src/locales",
|
|
||||||
"**/gen-ts-api",
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a preferred package name, creates a ESLint configuration object.
|
|
||||||
*
|
|
||||||
* @param {ESLintPackageConfigOptions} options The preferred package configuration options.
|
|
||||||
*
|
|
||||||
* @returns The ESLint configuration object.
|
|
||||||
*/
|
|
||||||
export function createESLintPackageConfig({ ignorePatterns = DefaultIgnorePatterns } = {}) {
|
|
||||||
return tseslint.config(
|
|
||||||
{
|
|
||||||
ignores: ignorePatterns,
|
|
||||||
},
|
|
||||||
|
|
||||||
eslint.configs.recommended,
|
|
||||||
javaScriptConfig,
|
|
||||||
|
|
||||||
wcconf.configs["flat/recommended"],
|
|
||||||
litconf.configs["flat/recommended"],
|
|
||||||
|
|
||||||
...tseslint.configs.recommended,
|
|
||||||
|
|
||||||
...typescriptConfig,
|
|
||||||
|
|
||||||
...reactConfig,
|
|
||||||
|
|
||||||
{
|
|
||||||
rules: {
|
|
||||||
"no-console": "off",
|
|
||||||
},
|
|
||||||
files: [
|
|
||||||
// ---
|
|
||||||
"**/scripts/**/*",
|
|
||||||
"**/test/**/*",
|
|
||||||
"**/tests/**/*",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,143 +0,0 @@
|
|||||||
// @ts-check
|
|
||||||
import tseslint from "typescript-eslint";
|
|
||||||
|
|
||||||
const MAX_DEPTH = 4;
|
|
||||||
const MAX_NESTED_CALLBACKS = 4;
|
|
||||||
const MAX_PARAMS = 5;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ESLint configuration for JavaScript authentik projects.
|
|
||||||
*/
|
|
||||||
export const javaScriptConfig = tseslint.config({
|
|
||||||
rules: {
|
|
||||||
// TODO: Clean up before enabling.
|
|
||||||
"accessor-pairs": "off",
|
|
||||||
"array-callback-return": "error",
|
|
||||||
"block-scoped-var": "error",
|
|
||||||
"consistent-return": ["error", { treatUndefinedAsUnspecified: false }],
|
|
||||||
"consistent-this": ["error", "that"],
|
|
||||||
"curly": "off",
|
|
||||||
"dot-notation": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
allowKeywords: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"eqeqeq": "error",
|
|
||||||
"func-names": ["error", "as-needed"],
|
|
||||||
"guard-for-in": "error",
|
|
||||||
"max-depth": ["error", MAX_DEPTH],
|
|
||||||
"max-nested-callbacks": ["error", MAX_NESTED_CALLBACKS],
|
|
||||||
"max-params": ["error", MAX_PARAMS],
|
|
||||||
// TODO: Clean up before enabling.
|
|
||||||
// "new-cap": "error",
|
|
||||||
"no-alert": "error",
|
|
||||||
"no-array-constructor": "error",
|
|
||||||
"no-bitwise": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
allow: ["~"],
|
|
||||||
int32Hint: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"no-caller": "error",
|
|
||||||
"no-case-declarations": "error",
|
|
||||||
"no-class-assign": "error",
|
|
||||||
"no-cond-assign": "error",
|
|
||||||
"no-const-assign": "error",
|
|
||||||
"no-constant-condition": "error",
|
|
||||||
"no-control-regex": "error",
|
|
||||||
"no-debugger": "error",
|
|
||||||
"no-delete-var": "error",
|
|
||||||
"no-div-regex": "error",
|
|
||||||
"no-dupe-args": "error",
|
|
||||||
"no-dupe-keys": "error",
|
|
||||||
"no-duplicate-case": "error",
|
|
||||||
"no-else-return": "error",
|
|
||||||
"no-empty": "error",
|
|
||||||
"no-empty-character-class": "error",
|
|
||||||
"no-empty-function": ["error", { allow: ["constructors"] }],
|
|
||||||
"no-labels": "error",
|
|
||||||
"no-eq-null": "error",
|
|
||||||
"no-eval": "error",
|
|
||||||
"no-ex-assign": "error",
|
|
||||||
"no-extend-native": "error",
|
|
||||||
"no-extra-bind": "error",
|
|
||||||
"no-extra-boolean-cast": "error",
|
|
||||||
"no-extra-label": "error",
|
|
||||||
"no-fallthrough": "error",
|
|
||||||
"no-func-assign": "error",
|
|
||||||
"no-implied-eval": "error",
|
|
||||||
"no-implicit-coercion": "error",
|
|
||||||
"no-implicit-globals": "error",
|
|
||||||
"no-inner-declarations": ["error", "functions"],
|
|
||||||
"no-invalid-regexp": "error",
|
|
||||||
"no-irregular-whitespace": "error",
|
|
||||||
"no-iterator": "error",
|
|
||||||
"no-label-var": "error",
|
|
||||||
"no-lone-blocks": "error",
|
|
||||||
"no-lonely-if": "error",
|
|
||||||
"no-loop-func": "error",
|
|
||||||
"no-multi-str": "error",
|
|
||||||
// TODO: Clean up before enabling.
|
|
||||||
"no-negated-condition": "off",
|
|
||||||
"no-new": "error",
|
|
||||||
"no-new-func": "error",
|
|
||||||
"no-new-wrappers": "error",
|
|
||||||
"no-obj-calls": "error",
|
|
||||||
"no-octal": "error",
|
|
||||||
"no-octal-escape": "error",
|
|
||||||
"no-param-reassign": ["error", { props: false }],
|
|
||||||
"no-proto": "error",
|
|
||||||
"no-redeclare": "error",
|
|
||||||
"no-regex-spaces": "error",
|
|
||||||
"no-restricted-syntax": ["error", "WithStatement"],
|
|
||||||
"no-script-url": "error",
|
|
||||||
"no-self-assign": "error",
|
|
||||||
"no-self-compare": "error",
|
|
||||||
"no-sequences": "error",
|
|
||||||
// TODO: Clean up before enabling.
|
|
||||||
// "no-shadow": "error",
|
|
||||||
"no-shadow-restricted-names": "error",
|
|
||||||
"no-sparse-arrays": "error",
|
|
||||||
"no-this-before-super": "error",
|
|
||||||
"no-throw-literal": "error",
|
|
||||||
"no-trailing-spaces": "off", // Handled by Prettier.
|
|
||||||
"no-undef": "off",
|
|
||||||
"no-undef-init": "off",
|
|
||||||
"no-unexpected-multiline": "error",
|
|
||||||
"no-useless-constructor": "error",
|
|
||||||
"no-unmodified-loop-condition": "error",
|
|
||||||
"no-unneeded-ternary": "error",
|
|
||||||
"no-unreachable": "error",
|
|
||||||
"no-unused-expressions": "error",
|
|
||||||
"no-unused-labels": "error",
|
|
||||||
"no-use-before-define": "error",
|
|
||||||
"no-useless-call": "error",
|
|
||||||
"no-dupe-class-members": "error",
|
|
||||||
"no-var": "error",
|
|
||||||
"no-void": "error",
|
|
||||||
"no-with": "error",
|
|
||||||
"prefer-arrow-callback": "error",
|
|
||||||
"prefer-const": "error",
|
|
||||||
"prefer-rest-params": "error",
|
|
||||||
"prefer-spread": "error",
|
|
||||||
"prefer-template": "error",
|
|
||||||
"radix": "error",
|
|
||||||
"require-yield": "error",
|
|
||||||
"strict": ["error", "global"],
|
|
||||||
"use-isnan": "error",
|
|
||||||
"valid-typeof": "error",
|
|
||||||
"vars-on-top": "error",
|
|
||||||
"yoda": ["error", "never"],
|
|
||||||
|
|
||||||
"no-console": ["error", { allow: ["debug", "warn", "error"] }],
|
|
||||||
// SonarJS is not yet compatible with ESLint 9. Commenting these out
|
|
||||||
// until it is.
|
|
||||||
// "sonarjs/cognitive-complexity": ["off", MAX_COGNITIVE_COMPLEXITY],
|
|
||||||
// "sonarjs/no-duplicate-string": "off",
|
|
||||||
// "sonarjs/no-nested-template-literals": "off",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default javaScriptConfig;
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@goauthentik/eslint-config",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "authentik's ESLint config",
|
|
||||||
"license": "MIT",
|
|
||||||
"type": "module",
|
|
||||||
"exports": {
|
|
||||||
"./package.json": "./package.json",
|
|
||||||
".": {
|
|
||||||
"import": "./index.js",
|
|
||||||
"types": "./out/index.d.ts"
|
|
||||||
},
|
|
||||||
"./react-config": {
|
|
||||||
"import": "./react-config.js",
|
|
||||||
"types": "./out/react-config.d.ts"
|
|
||||||
},
|
|
||||||
"./javascript-config": {
|
|
||||||
"import": "./javascript-config.js",
|
|
||||||
"types": "./out/javascript-config.d.ts"
|
|
||||||
},
|
|
||||||
"./typescript-config": {
|
|
||||||
"import": "./typescript-config.js",
|
|
||||||
"types": "./out/typescript-config.d.ts"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"eslint": "^9.23.0",
|
|
||||||
"eslint-plugin-import": "^2.31.0",
|
|
||||||
"eslint-plugin-react": "^7.37.4",
|
|
||||||
"eslint-plugin-react-hooks": "^5.2.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@goauthentik/tsconfig": "1.0.0",
|
|
||||||
"@types/eslint": "^9.6.1",
|
|
||||||
"typescript": "^5.8.2",
|
|
||||||
"typescript-eslint": "^8.29.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"typescript": "^5.8.2",
|
|
||||||
"typescript-eslint": "^8.29.0"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"react": "^18.3.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=20.11"
|
|
||||||
},
|
|
||||||
"types": "./out/index.d.ts",
|
|
||||||
"prettier": "@goauthentik/prettier-config",
|
|
||||||
"publishConfig": {
|
|
||||||
"access": "public"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
34
packages/eslint-config/react-config.js
vendored
34
packages/eslint-config/react-config.js
vendored
@ -1,34 +0,0 @@
|
|||||||
import reactPlugin from "eslint-plugin-react";
|
|
||||||
import hooksPlugin from "eslint-plugin-react-hooks";
|
|
||||||
import tseslint from "typescript-eslint";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ESLint configuration for React authentik projects.
|
|
||||||
*/
|
|
||||||
export const reactConfig = tseslint.config({
|
|
||||||
settings: {
|
|
||||||
react: {
|
|
||||||
version: "detect",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
plugins: {
|
|
||||||
"react": reactPlugin,
|
|
||||||
"react-hooks": hooksPlugin,
|
|
||||||
},
|
|
||||||
|
|
||||||
rules: {
|
|
||||||
"react-hooks/rules-of-hooks": "error",
|
|
||||||
"react-hooks/exhaustive-deps": "warn",
|
|
||||||
|
|
||||||
"react/jsx-uses-react": 0,
|
|
||||||
|
|
||||||
"react/display-name": "off",
|
|
||||||
"react/jsx-curly-brace-presence": "error",
|
|
||||||
"react/jsx-no-leaked-render": "error",
|
|
||||||
"react/prop-types": "off",
|
|
||||||
"react/react-in-jsx-scope": "off",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default reactConfig;
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "@goauthentik/tsconfig",
|
|
||||||
"compilerOptions": {
|
|
||||||
"baseUrl": ".",
|
|
||||||
"checkJs": true,
|
|
||||||
"emitDeclarationOnly": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
// @ts-check
|
|
||||||
import tseslint from "typescript-eslint";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ESLint configuration for TypeScript authentik projects.
|
|
||||||
*/
|
|
||||||
export const typescriptConfig = tseslint.config({
|
|
||||||
rules: {
|
|
||||||
"@typescript-eslint/ban-ts-comment": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"ts-expect-error": "allow-with-description",
|
|
||||||
"ts-ignore": true,
|
|
||||||
"ts-nocheck": "allow-with-description",
|
|
||||||
"ts-check": false,
|
|
||||||
"minimumDescriptionLength": 5,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"no-use-before-define": "off",
|
|
||||||
"@typescript-eslint/no-use-before-define": "error",
|
|
||||||
"no-invalid-this": "off",
|
|
||||||
"no-unused-vars": "off",
|
|
||||||
"@typescript-eslint/no-namespace": "off",
|
|
||||||
"@typescript-eslint/no-unused-vars": [
|
|
||||||
"warn",
|
|
||||||
{
|
|
||||||
argsIgnorePattern: "^_",
|
|
||||||
varsIgnorePattern: "^_",
|
|
||||||
caughtErrorsIgnorePattern: "^_",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default typescriptConfig;
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2025 Authentik Security, Inc.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
|
||||||
associated documentation files (the "Software"), to deal in the Software without restriction,
|
|
||||||
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
|
||||||
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
|
||||||
portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
|
||||||
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
|
||||||
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
# `@goauthentik/monorepo`
|
|
||||||
|
|
||||||
This package contains utility scripts common to all TypeScript and JavaScript packages in the
|
|
||||||
`@goauthentik` monorepo.
|
|
||||||
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file Constants for JavaScript and TypeScript files.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current Node.js environment, defaulting to "development" when not set.
|
|
||||||
*
|
|
||||||
* Note, this should only be used during the build process.
|
|
||||||
*
|
|
||||||
* If you need to check the environment at runtime, use `process.env.NODE_ENV` to
|
|
||||||
* ensure that module tree-shaking works correctly.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export const NodeEnvironment = /** @type {'development' | 'production'} */ (
|
|
||||||
process.env.NODE_ENV || "development"
|
|
||||||
);
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
export * from "./paths.js";
|
|
||||||
export * from "./constants.js";
|
|
||||||
export * from "./version.js";
|
|
||||||
export * from "./scripting.js";
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@goauthentik/monorepo",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "Utilities for the authentik monorepo.",
|
|
||||||
"private": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"type": "module",
|
|
||||||
"exports": {
|
|
||||||
"./package.json": "./package.json",
|
|
||||||
".": {
|
|
||||||
"import": "./index.js",
|
|
||||||
"types": "./out/index.d.ts"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"types": "./out/index.d.ts",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=20.11"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
import { createRequire } from "node:module";
|
|
||||||
import { dirname, join, resolve } from "node:path";
|
|
||||||
import { fileURLToPath } from "node:url";
|
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {'~authentik'} MonoRepoRoot
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The root of the authentik monorepo.
|
|
||||||
*/
|
|
||||||
export const MonoRepoRoot = /** @type {MonoRepoRoot} */ (resolve(__dirname, "..", ".."));
|
|
||||||
|
|
||||||
const require = createRequire(import.meta.url);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve a package name to its location in the monorepo to the single node_modules directory.
|
|
||||||
* @param {string} packageName
|
|
||||||
* @returns {string} The resolved path to the package.
|
|
||||||
* @throws {Error} If the package cannot be resolved.
|
|
||||||
*/
|
|
||||||
export function resolvePackage(packageName) {
|
|
||||||
const packageJSONPath = require.resolve(join(packageName, "package.json"), {
|
|
||||||
paths: [MonoRepoRoot],
|
|
||||||
});
|
|
||||||
|
|
||||||
return dirname(packageJSONPath);
|
|
||||||
}
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
import { createRequire } from "node:module";
|
|
||||||
import * as path from "node:path";
|
|
||||||
import * as process from "node:process";
|
|
||||||
import { fileURLToPath } from "node:url";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Predicate to determine if a module was run directly, i.e. not imported.
|
|
||||||
*
|
|
||||||
* @param {ImportMeta} meta The `import.meta` object of the module.
|
|
||||||
*
|
|
||||||
* @return {boolean} Whether the module was run directly.
|
|
||||||
*/
|
|
||||||
export function isMain(meta) {
|
|
||||||
// Are we not in a module context?
|
|
||||||
if (!meta) return false;
|
|
||||||
|
|
||||||
const relativeScriptPath = process.argv[1];
|
|
||||||
|
|
||||||
if (!relativeScriptPath) return false;
|
|
||||||
|
|
||||||
const require = createRequire(meta.url);
|
|
||||||
const absoluteScriptPath = require.resolve(relativeScriptPath);
|
|
||||||
|
|
||||||
const modulePath = fileURLToPath(meta.url);
|
|
||||||
|
|
||||||
const scriptExtension = path.extname(absoluteScriptPath);
|
|
||||||
|
|
||||||
if (scriptExtension) {
|
|
||||||
return modulePath === absoluteScriptPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
const moduleExtension = path.extname(modulePath);
|
|
||||||
|
|
||||||
if (moduleExtension) {
|
|
||||||
return absoluteScriptPath === modulePath.slice(0, -moduleExtension.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If both are without extension, compare them directly.
|
|
||||||
return modulePath === absoluteScriptPath;
|
|
||||||
}
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "@goauthentik/tsconfig",
|
|
||||||
"compilerOptions": {
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"baseUrl": ".",
|
|
||||||
"checkJs": true,
|
|
||||||
"emitDeclarationOnly": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
import { execSync } from "node:child_process";
|
|
||||||
|
|
||||||
import PackageJSON from "../../package.json" with { type: "json" };
|
|
||||||
import { MonoRepoRoot } from "./paths.js";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current version of authentik in SemVer format.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export const AuthentikVersion = /**@type {`${number}.${number}.${number}`} */ (PackageJSON.version);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the last commit hash from the current git repository.
|
|
||||||
*/
|
|
||||||
export function readGitBuildHash() {
|
|
||||||
try {
|
|
||||||
const commit = execSync("git rev-parse HEAD", {
|
|
||||||
encoding: "utf8",
|
|
||||||
cwd: MonoRepoRoot,
|
|
||||||
})
|
|
||||||
.toString()
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
return commit;
|
|
||||||
} catch (_error) {
|
|
||||||
console.debug("Git commit could not be read.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return process.env.GIT_BUILD_HASH || "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the build identifier for the current environment.
|
|
||||||
*
|
|
||||||
* This must match the behavior defined in authentik's server-side `get_full_version` function.
|
|
||||||
*
|
|
||||||
* @see {@link "authentik\_\_init\_\_.py"}
|
|
||||||
*/
|
|
||||||
export function readBuildIdentifier() {
|
|
||||||
const { GIT_BUILD_HASH } = process.env;
|
|
||||||
|
|
||||||
if (!GIT_BUILD_HASH) return AuthentikVersion;
|
|
||||||
|
|
||||||
return [AuthentikVersion, GIT_BUILD_HASH].join("+");
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2025 Authentik Security, Inc.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
|
||||||
associated documentation files (the "Software"), to deal in the Software without restriction,
|
|
||||||
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
|
||||||
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
|
||||||
portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
|
||||||
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
|
||||||
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
# `@goauthentik/prettier-config`
|
|
||||||
|
|
||||||
This package contains the Prettier configuration used by authentik.
|
|
||||||
While it is possible to use this configuration outside of our projects,
|
|
||||||
you may find that it is not as useful as other popular configurations.
|
|
||||||
@ -1,80 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file Prettier configuration for authentik.
|
|
||||||
*
|
|
||||||
* @import { Config as PrettierConfig } from "prettier";
|
|
||||||
* @import { PluginConfig as SortPluginConfig } from "@trivago/prettier-plugin-sort-imports";
|
|
||||||
*
|
|
||||||
* @typedef {object} PackageJSONPluginConfig
|
|
||||||
* @property {string[]} [packageSortOrder] Custom ordering array.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* authentik Prettier configuration.
|
|
||||||
*
|
|
||||||
* @type {PrettierConfig & SortPluginConfig & PackageJSONPluginConfig}
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
export const AuthentikPrettierConfig = {
|
|
||||||
arrowParens: "always",
|
|
||||||
bracketSpacing: true,
|
|
||||||
embeddedLanguageFormatting: "auto",
|
|
||||||
htmlWhitespaceSensitivity: "css",
|
|
||||||
insertPragma: false,
|
|
||||||
jsxSingleQuote: false,
|
|
||||||
printWidth: 100,
|
|
||||||
proseWrap: "preserve",
|
|
||||||
quoteProps: "consistent",
|
|
||||||
requirePragma: false,
|
|
||||||
semi: true,
|
|
||||||
singleQuote: false,
|
|
||||||
tabWidth: 4,
|
|
||||||
trailingComma: "all",
|
|
||||||
useTabs: false,
|
|
||||||
vueIndentScriptAndStyle: false,
|
|
||||||
plugins: ["prettier-plugin-packagejson", "@trivago/prettier-plugin-sort-imports"],
|
|
||||||
importOrder: ["^(@?)lit(.*)$", "\\.css$", "^@goauthentik/api$", "^[./]"],
|
|
||||||
importOrderSeparation: true,
|
|
||||||
importOrderSortSpecifiers: true,
|
|
||||||
importOrderParserPlugins: ["typescript", "jsx", "classProperties", "decorators-legacy"],
|
|
||||||
overrides: [
|
|
||||||
{
|
|
||||||
files: "schemas/**/*.json",
|
|
||||||
options: {
|
|
||||||
tabWidth: 2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: "tsconfig.json",
|
|
||||||
options: {
|
|
||||||
trailingComma: "none",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: "package.json",
|
|
||||||
options: {
|
|
||||||
packageSortOrder: [
|
|
||||||
// ---
|
|
||||||
"name",
|
|
||||||
"version",
|
|
||||||
"description",
|
|
||||||
"license",
|
|
||||||
"private",
|
|
||||||
"author",
|
|
||||||
"authors",
|
|
||||||
"scripts",
|
|
||||||
"main",
|
|
||||||
"type",
|
|
||||||
"exports",
|
|
||||||
"imports",
|
|
||||||
"dependencies",
|
|
||||||
"devDependencies",
|
|
||||||
"peerDependencies",
|
|
||||||
"optionalDependencies",
|
|
||||||
"wireit",
|
|
||||||
"resolutions",
|
|
||||||
"engines",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
import { format } from "prettier";
|
|
||||||
|
|
||||||
import { AuthentikPrettierConfig } from "./config.js";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Format using Prettier.
|
|
||||||
*
|
|
||||||
* Defaults to using the TypeScript parser.
|
|
||||||
*
|
|
||||||
* @category Formatting
|
|
||||||
* @param {string} fileContents The contents of the file to format.
|
|
||||||
*
|
|
||||||
* @returns {Promise<string>} The formatted file contents.
|
|
||||||
*/
|
|
||||||
export function formatWithPrettier(fileContents) {
|
|
||||||
return format(fileContents, {
|
|
||||||
...AuthentikPrettierConfig,
|
|
||||||
parser: "typescript",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
import { AuthentikPrettierConfig } from "./config.js";
|
|
||||||
|
|
||||||
export * from "./config.js";
|
|
||||||
export * from "./format.js";
|
|
||||||
|
|
||||||
export default AuthentikPrettierConfig;
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@goauthentik/prettier-config",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "authentik's Prettier config",
|
|
||||||
"license": "MIT",
|
|
||||||
"type": "module",
|
|
||||||
"exports": {
|
|
||||||
"./package.json": "./package.json",
|
|
||||||
".": {
|
|
||||||
"import": "./index.js",
|
|
||||||
"types": "./out/index.d.ts"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"types": "./out/index.d.ts",
|
|
||||||
"peerDependencies": {
|
|
||||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
|
||||||
"prettier": "^3.5.3",
|
|
||||||
"prettier-plugin-organize-imports": "^4.1.0",
|
|
||||||
"prettier-plugin-packagejson": "^2.5.10"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=20.11"
|
|
||||||
},
|
|
||||||
"publishConfig": {
|
|
||||||
"access": "public"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "@goauthentik/tsconfig",
|
|
||||||
"compilerOptions": {
|
|
||||||
"baseUrl": ".",
|
|
||||||
"checkJs": true,
|
|
||||||
"emitDeclarationOnly": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2025 Authentik Security, Inc.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
|
||||||
associated documentation files (the "Software"), to deal in the Software without restriction,
|
|
||||||
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
|
||||||
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
|
||||||
portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
|
||||||
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
|
||||||
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
# `@goauthentik/tsconfig`
|
|
||||||
|
|
||||||
This package contains the TypeScript configuration used by authentik TypeScript projects.
|
|
||||||
|
|
||||||
While it is possible to use this configuration outside of our projects,
|
|
||||||
you may find that it is not as useful as other popular configurations.
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@goauthentik/tsconfig",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "authentik's s base TypeScript configuration.",
|
|
||||||
"keywords": [
|
|
||||||
"tsconfig",
|
|
||||||
"typescript"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"type": "module",
|
|
||||||
"main": "tsconfig.json",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=20.11"
|
|
||||||
},
|
|
||||||
"publishConfig": {
|
|
||||||
"access": "public"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://json.schemastore.org/tsconfig",
|
|
||||||
"compilerOptions": {
|
|
||||||
"alwaysStrict": true,
|
|
||||||
"baseUrl": ".",
|
|
||||||
"composite": true,
|
|
||||||
"declaration": true,
|
|
||||||
"declarationMap": true,
|
|
||||||
"esModuleInterop": false,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"incremental": true,
|
|
||||||
"jsx": "react-jsx",
|
|
||||||
"lib": ["ESNext"],
|
|
||||||
"module": "NodeNext",
|
|
||||||
"moduleResolution": "NodeNext",
|
|
||||||
"newLine": "lf",
|
|
||||||
"noFallthroughCasesInSwitch": true,
|
|
||||||
"noImplicitOverride": false,
|
|
||||||
"outDir": "${configDir}/out",
|
|
||||||
"pretty": true,
|
|
||||||
"skipDefaultLibCheck": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"sourceMap": true,
|
|
||||||
"strict": true,
|
|
||||||
"noUncheckedIndexedAccess": true,
|
|
||||||
"target": "ESNext",
|
|
||||||
"useUnknownInCatchVariables": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "authentik"
|
name = "authentik"
|
||||||
version = "2025.2.4"
|
version = "2025.2.3"
|
||||||
description = ""
|
description = ""
|
||||||
authors = [{ name = "authentik Team", email = "hello@goauthentik.io" }]
|
authors = [{ name = "authentik Team", email = "hello@goauthentik.io" }]
|
||||||
requires-python = "==3.12.*"
|
requires-python = "==3.12.*"
|
||||||
@ -52,7 +52,7 @@ dependencies = [
|
|||||||
"pydantic-scim",
|
"pydantic-scim",
|
||||||
"pyjwt",
|
"pyjwt",
|
||||||
"pyrad",
|
"pyrad",
|
||||||
"python-kadmin-rs ==0.6.0",
|
"python-kadmin-rs ==0.5.3",
|
||||||
"pyyaml",
|
"pyyaml",
|
||||||
"requests-oauthlib",
|
"requests-oauthlib",
|
||||||
"scim2-filter-parser",
|
"scim2-filter-parser",
|
||||||
|
|||||||
571
schema.yml
571
schema.yml
File diff suppressed because it is too large
Load Diff
8
web/package-lock.json
generated
8
web/package-lock.json
generated
@ -24,7 +24,7 @@
|
|||||||
"@floating-ui/dom": "^1.6.11",
|
"@floating-ui/dom": "^1.6.11",
|
||||||
"@formatjs/intl-listformat": "^7.5.7",
|
"@formatjs/intl-listformat": "^7.5.7",
|
||||||
"@fortawesome/fontawesome-free": "^6.6.0",
|
"@fortawesome/fontawesome-free": "^6.6.0",
|
||||||
"@goauthentik/api": "^2025.2.4-1744139776",
|
"@goauthentik/api": "^2025.2.3-1743464496",
|
||||||
"@lit-labs/ssr": "^3.2.2",
|
"@lit-labs/ssr": "^3.2.2",
|
||||||
"@lit/context": "^1.1.2",
|
"@lit/context": "^1.1.2",
|
||||||
"@lit/localize": "^0.12.2",
|
"@lit/localize": "^0.12.2",
|
||||||
@ -1835,9 +1835,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@goauthentik/api": {
|
"node_modules/@goauthentik/api": {
|
||||||
"version": "2025.2.4-1744139776",
|
"version": "2025.2.3-1743464496",
|
||||||
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2025.2.4-1744139776.tgz",
|
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2025.2.3-1743464496.tgz",
|
||||||
"integrity": "sha512-4CLjeW19Zc5/WhxOX/welwAI+YvyKp8cR4uBqLCScvyy/fTwB8dH2NnRj4eDv5U+OQpAwdKXZCBY16fWGg0/kg=="
|
"integrity": "sha512-35+SqFNoBZ+WNpyG2Xv/VKYKIIxjwRmIbgX5WZSpc9IlJVv7yyckUYvLpU2F0hZVUMDnxAUE5bsiNn7K4EQslw=="
|
||||||
},
|
},
|
||||||
"node_modules/@goauthentik/web": {
|
"node_modules/@goauthentik/web": {
|
||||||
"resolved": "",
|
"resolved": "",
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
"@floating-ui/dom": "^1.6.11",
|
"@floating-ui/dom": "^1.6.11",
|
||||||
"@formatjs/intl-listformat": "^7.5.7",
|
"@formatjs/intl-listformat": "^7.5.7",
|
||||||
"@fortawesome/fontawesome-free": "^6.6.0",
|
"@fortawesome/fontawesome-free": "^6.6.0",
|
||||||
"@goauthentik/api": "^2025.2.4-1744139776",
|
"@goauthentik/api": "^2025.2.3-1743464496",
|
||||||
"@lit-labs/ssr": "^3.2.2",
|
"@lit-labs/ssr": "^3.2.2",
|
||||||
"@lit/context": "^1.1.2",
|
"@lit/context": "^1.1.2",
|
||||||
"@lit/localize": "^0.12.2",
|
"@lit/localize": "^0.12.2",
|
||||||
|
|||||||
@ -13,6 +13,7 @@ const MAX_PARAMS = 5;
|
|||||||
// const MAX_COGNITIVE_COMPLEXITY = 9;
|
// const MAX_COGNITIVE_COMPLEXITY = 9;
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
|
"no-param-reassign": "error",
|
||||||
"accessor-pairs": "error",
|
"accessor-pairs": "error",
|
||||||
"array-callback-return": "error",
|
"array-callback-return": "error",
|
||||||
"block-scoped-var": "error",
|
"block-scoped-var": "error",
|
||||||
@ -84,7 +85,6 @@ const rules = {
|
|||||||
"no-obj-calls": "error",
|
"no-obj-calls": "error",
|
||||||
"no-octal": "error",
|
"no-octal": "error",
|
||||||
"no-octal-escape": "error",
|
"no-octal-escape": "error",
|
||||||
"no-param-reassign": "error",
|
|
||||||
"no-proto": "error",
|
"no-proto": "error",
|
||||||
"no-redeclare": "error",
|
"no-redeclare": "error",
|
||||||
"no-regex-spaces": "error",
|
"no-regex-spaces": "error",
|
||||||
@ -134,6 +134,7 @@ const rules = {
|
|||||||
// "sonarjs/cognitive-complexity": ["off", MAX_COGNITIVE_COMPLEXITY],
|
// "sonarjs/cognitive-complexity": ["off", MAX_COGNITIVE_COMPLEXITY],
|
||||||
// "sonarjs/no-duplicate-string": "off",
|
// "sonarjs/no-duplicate-string": "off",
|
||||||
// "sonarjs/no-nested-template-literals": "off",
|
// "sonarjs/no-nested-template-literals": "off",
|
||||||
|
" @typescript-eslint/no-namespace": "off",
|
||||||
"@typescript-eslint/ban-ts-comment": "off",
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
"@typescript-eslint/no-unused-vars": [
|
"@typescript-eslint/no-unused-vars": [
|
||||||
"error",
|
"error",
|
||||||
|
|||||||
@ -48,6 +48,7 @@ export default [
|
|||||||
// "sonarjs/no-duplicate-string": "off",
|
// "sonarjs/no-duplicate-string": "off",
|
||||||
// "sonarjs/no-nested-template-literals": "off",
|
// "sonarjs/no-nested-template-literals": "off",
|
||||||
"@typescript-eslint/ban-ts-comment": "off",
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
|
|
||||||
"@typescript-eslint/no-unused-vars": [
|
"@typescript-eslint/no-unused-vars": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||||
import { VERSION } from "@goauthentik/common/constants";
|
import { VERSION } from "@goauthentik/common/constants";
|
||||||
import { BrandConfig, ServerConfig } from "@goauthentik/common/global";
|
import { globalAK } from "@goauthentik/common/global";
|
||||||
import "@goauthentik/elements/EmptyState";
|
import "@goauthentik/elements/EmptyState";
|
||||||
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
|
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
|
||||||
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider";
|
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider";
|
||||||
@ -33,7 +33,7 @@ export class AboutModal extends WithLicenseSummary(WithBrandConfig(ModalButton))
|
|||||||
const status = await new AdminApi(DEFAULT_CONFIG).adminSystemRetrieve();
|
const status = await new AdminApi(DEFAULT_CONFIG).adminSystemRetrieve();
|
||||||
const version = await new AdminApi(DEFAULT_CONFIG).adminVersionRetrieve();
|
const version = await new AdminApi(DEFAULT_CONFIG).adminVersionRetrieve();
|
||||||
let build: string | TemplateResult = msg("Release");
|
let build: string | TemplateResult = msg("Release");
|
||||||
if (ServerConfig.capabilities.includes(CapabilitiesEnum.CanDebug)) {
|
if (globalAK().config.capabilities.includes(CapabilitiesEnum.CanDebug)) {
|
||||||
build = msg("Development");
|
build = msg("Development");
|
||||||
} else if (version.buildHash !== "") {
|
} else if (version.buildHash !== "") {
|
||||||
build = html`<a
|
build = html`<a
|
||||||
@ -58,8 +58,8 @@ export class AboutModal extends WithLicenseSummary(WithBrandConfig(ModalButton))
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderModal() {
|
renderModal() {
|
||||||
let product = BrandConfig.brandingTitle || DefaultBrand.brandingTitle;
|
let product = globalAK().brand.brandingTitle || DefaultBrand.brandingTitle;
|
||||||
if (this.licenseSummary.status != LicenseSummaryStatusEnum.Unlicensed) {
|
if (this.licenseSummary.status !== LicenseSummaryStatusEnum.Unlicensed) {
|
||||||
product += ` ${msg("Enterprise")}`;
|
product += ` ${msg("Enterprise")}`;
|
||||||
}
|
}
|
||||||
return html`<div
|
return html`<div
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||||
import { parseAPIResponseError, pluckErrorDetail } from "@goauthentik/common/errors/network";
|
|
||||||
import { MessageLevel } from "@goauthentik/common/messages";
|
import { MessageLevel } from "@goauthentik/common/messages";
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
import "@goauthentik/elements/PageHeader";
|
import "@goauthentik/elements/PageHeader";
|
||||||
@ -55,12 +54,10 @@ export class DebugPage extends AKElement {
|
|||||||
message: "Success",
|
message: "Success",
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(async (error) => {
|
.catch((exc) => {
|
||||||
const parsedError = await parseAPIResponseError(error);
|
|
||||||
|
|
||||||
showMessage({
|
showMessage({
|
||||||
level: MessageLevel.error,
|
level: MessageLevel.error,
|
||||||
message: pluckErrorDetail(parsedError),
|
message: exc,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -1,16 +1,13 @@
|
|||||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||||
import { PFSize } from "@goauthentik/common/enums.js";
|
import { PFSize } from "@goauthentik/common/enums.js";
|
||||||
import {
|
|
||||||
APIError,
|
|
||||||
parseAPIResponseError,
|
|
||||||
pluckErrorDetail,
|
|
||||||
} from "@goauthentik/common/errors/network";
|
|
||||||
import { AggregateCard } from "@goauthentik/elements/cards/AggregateCard";
|
import { AggregateCard } from "@goauthentik/elements/cards/AggregateCard";
|
||||||
|
|
||||||
import { msg } from "@lit/localize";
|
import { msg } from "@lit/localize";
|
||||||
import { PropertyValues, TemplateResult, html, nothing } from "lit";
|
import { PropertyValues, TemplateResult, html, nothing } from "lit";
|
||||||
import { state } from "lit/decorators.js";
|
import { state } from "lit/decorators.js";
|
||||||
|
|
||||||
|
import { ResponseError } from "@goauthentik/api";
|
||||||
|
|
||||||
export interface AdminStatus {
|
export interface AdminStatus {
|
||||||
icon: string;
|
icon: string;
|
||||||
message?: TemplateResult;
|
message?: TemplateResult;
|
||||||
@ -32,7 +29,7 @@ export abstract class AdminStatusCard<T> extends AggregateCard {
|
|||||||
|
|
||||||
// Current error state if any request fails
|
// Current error state if any request fails
|
||||||
@state()
|
@state()
|
||||||
protected error?: APIError;
|
protected error?: string;
|
||||||
|
|
||||||
// Abstract methods to be implemented by subclasses
|
// Abstract methods to be implemented by subclasses
|
||||||
abstract getPrimaryValue(): Promise<T>;
|
abstract getPrimaryValue(): Promise<T>;
|
||||||
@ -62,9 +59,9 @@ export abstract class AdminStatusCard<T> extends AggregateCard {
|
|||||||
this.value = value; // Triggers shouldUpdate
|
this.value = value; // Triggers shouldUpdate
|
||||||
this.error = undefined;
|
this.error = undefined;
|
||||||
})
|
})
|
||||||
.catch(async (error: unknown) => {
|
.catch((err: ResponseError) => {
|
||||||
this.status = undefined;
|
this.status = undefined;
|
||||||
this.error = await parseAPIResponseError(error);
|
this.error = err?.response?.statusText ?? msg("Unknown error");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,9 +79,9 @@ export abstract class AdminStatusCard<T> extends AggregateCard {
|
|||||||
this.status = status;
|
this.status = status;
|
||||||
this.error = undefined;
|
this.error = undefined;
|
||||||
})
|
})
|
||||||
.catch(async (error: unknown) => {
|
.catch((err: ResponseError) => {
|
||||||
this.status = undefined;
|
this.status = undefined;
|
||||||
this.error = await parseAPIResponseError(error);
|
this.error = err?.response?.statusText ?? msg("Unknown error");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Prevent immediate re-render if only value changed
|
// Prevent immediate re-render if only value changed
|
||||||
@ -123,8 +120,8 @@ export abstract class AdminStatusCard<T> extends AggregateCard {
|
|||||||
*/
|
*/
|
||||||
private renderError(error: string): TemplateResult {
|
private renderError(error: string): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<p><i class="fa fa-times"></i> ${msg("Failed to fetch")}</p>
|
<p><i class="fa fa-times"></i> ${error}</p>
|
||||||
<p class="subtext">${error}</p>
|
<p class="subtext">${msg("Failed to fetch")}</p>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,7 +146,7 @@ export abstract class AdminStatusCard<T> extends AggregateCard {
|
|||||||
this.status
|
this.status
|
||||||
? this.renderStatus(this.status) // Status available
|
? this.renderStatus(this.status) // Status available
|
||||||
: this.error
|
: this.error
|
||||||
? this.renderError(pluckErrorDetail(this.error)) // Error state
|
? this.renderError(this.error) // Error state
|
||||||
: this.renderLoading() // Loading state
|
: this.renderLoading() // Loading state
|
||||||
}
|
}
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { EventGeo, EventUser } from "@goauthentik/admin/events/utils";
|
|||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||||
import { EventWithContext } from "@goauthentik/common/events";
|
import { EventWithContext } from "@goauthentik/common/events";
|
||||||
import { actionToLabel } from "@goauthentik/common/labels";
|
import { actionToLabel } from "@goauthentik/common/labels";
|
||||||
import { formatElapsedTime } from "@goauthentik/common/temporal";
|
import { getRelativeTime } from "@goauthentik/common/utils";
|
||||||
import "@goauthentik/components/ak-event-info";
|
import "@goauthentik/components/ak-event-info";
|
||||||
import "@goauthentik/elements/Tabs";
|
import "@goauthentik/elements/Tabs";
|
||||||
import "@goauthentik/elements/buttons/Dropdown";
|
import "@goauthentik/elements/buttons/Dropdown";
|
||||||
@ -10,7 +10,6 @@ import "@goauthentik/elements/buttons/ModalButton";
|
|||||||
import "@goauthentik/elements/buttons/SpinnerButton";
|
import "@goauthentik/elements/buttons/SpinnerButton";
|
||||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
|
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
|
||||||
import { Table, TableColumn } from "@goauthentik/elements/table/Table";
|
import { Table, TableColumn } from "@goauthentik/elements/table/Table";
|
||||||
import { SlottedTemplateResult } from "@goauthentik/elements/types";
|
|
||||||
|
|
||||||
import { msg } from "@lit/localize";
|
import { msg } from "@lit/localize";
|
||||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||||
@ -69,12 +68,12 @@ export class RecentEventsCard extends Table<Event> {
|
|||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
row(item: EventWithContext): SlottedTemplateResult[] {
|
row(item: EventWithContext): TemplateResult[] {
|
||||||
return [
|
return [
|
||||||
html`<div><a href="${`#/events/log/${item.pk}`}">${actionToLabel(item.action)}</a></div>
|
html`<div><a href="${`#/events/log/${item.pk}`}">${actionToLabel(item.action)}</a></div>
|
||||||
<small>${item.app}</small>`,
|
<small>${item.app}</small>`,
|
||||||
EventUser(item),
|
EventUser(item),
|
||||||
html`<div>${formatElapsedTime(item.created)}</div>
|
html`<div>${getRelativeTime(item.created)}</div>
|
||||||
<small>${item.created.toLocaleString()}</small>`,
|
<small>${item.created.toLocaleString()}</small>`,
|
||||||
html` <div>${item.clientIp || msg("-")}</div>
|
html` <div>${item.clientIp || msg("-")}</div>
|
||||||
<small>${EventGeo(item)}</small>`,
|
<small>${EventGeo(item)}</small>`,
|
||||||
@ -82,11 +81,7 @@ export class RecentEventsCard extends Table<Event> {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
renderEmpty(inner?: SlottedTemplateResult): TemplateResult {
|
renderEmpty(): TemplateResult {
|
||||||
if (this.error) {
|
|
||||||
return super.renderEmpty(inner);
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.renderEmpty(
|
return super.renderEmpty(
|
||||||
html`<ak-empty-state header=${msg("No Events found.")}>
|
html`<ak-empty-state header=${msg("No Events found.")}>
|
||||||
<div slot="body">${msg("No matching events could be found.")}</div>
|
<div slot="body">${msg("No matching events could be found.")}</div>
|
||||||
|
|||||||
@ -46,7 +46,7 @@ export class SystemStatusCard extends AdminStatusCard<SystemInfo> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const outpost = outposts.results[0];
|
const outpost = outposts.results[0];
|
||||||
outpost.config["authentik_host"] = window.location.origin;
|
outpost.config.authentik_host = window.location.origin;
|
||||||
await new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesUpdate({
|
await new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesUpdate({
|
||||||
uuid: outpost.pk,
|
uuid: outpost.pk,
|
||||||
outpostRequest: outpost,
|
outpostRequest: outpost,
|
||||||
|
|||||||
@ -28,16 +28,18 @@ export class WorkersStatusCard extends AdminStatusCard<Worker[]> {
|
|||||||
icon: "fa fa-times-circle pf-m-danger",
|
icon: "fa fa-times-circle pf-m-danger",
|
||||||
message: html`${msg("No workers connected. Background tasks will not run.")}`,
|
message: html`${msg("No workers connected. Background tasks will not run.")}`,
|
||||||
});
|
});
|
||||||
} else if (value.filter((w) => !w.versionMatching).length > 0) {
|
}
|
||||||
|
|
||||||
|
if (value.filter((w) => !w.versionMatching).length > 0) {
|
||||||
return Promise.resolve<AdminStatus>({
|
return Promise.resolve<AdminStatus>({
|
||||||
icon: "fa fa-times-circle pf-m-danger",
|
icon: "fa fa-times-circle pf-m-danger",
|
||||||
message: html`${msg("Worker with incorrect version connected.")}`,
|
message: html`${msg("Worker with incorrect version connected.")}`,
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
return Promise.resolve<AdminStatus>({
|
|
||||||
icon: "fa fa-check-circle pf-m-success",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Promise.resolve<AdminStatus>({
|
||||||
|
icon: "fa fa-check-circle pf-m-success",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
renderValue() {
|
renderValue() {
|
||||||
|
|||||||
@ -127,7 +127,7 @@ export class SyncStatusChart extends AKChart<SummarizedSyncStatus[]> {
|
|||||||
msg("LDAP Source"),
|
msg("LDAP Source"),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
this.centerText = statuses.reduce((total, el) => (total += el.total), 0).toString();
|
this.centerText = statuses.reduce((total, el) => total + el.total, 0).toString();
|
||||||
return statuses;
|
return statuses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,26 +6,26 @@ import { html } from "lit";
|
|||||||
import "../AdminSettingsFooterLinks.js";
|
import "../AdminSettingsFooterLinks.js";
|
||||||
|
|
||||||
describe("ak-admin-settings-footer-link", () => {
|
describe("ak-admin-settings-footer-link", () => {
|
||||||
afterEach(async () => {
|
afterEach(() => {
|
||||||
await browser.execute(async () => {
|
return browser.execute(() => {
|
||||||
await document.body.querySelector("ak-admin-settings-footer-link")?.remove();
|
document.body.querySelector("ak-admin-settings-footer-link")?.remove();
|
||||||
if (document.body["_$litPart$"]) {
|
|
||||||
// @ts-expect-error expression of type '"_$litPart$"' is added by Lit
|
if ("_$litPart$" in document.body) {
|
||||||
await delete document.body["_$litPart$"];
|
delete document.body._$litPart$;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render an empty control", async () => {
|
it("should render an empty control", async () => {
|
||||||
render(html`<ak-admin-settings-footer-link name="link"></ak-admin-settings-footer-link>`);
|
render(html`<ak-admin-settings-footer-link name="link"></ak-admin-settings-footer-link>`);
|
||||||
const link = await $("ak-admin-settings-footer-link");
|
const link = $("ak-admin-settings-footer-link");
|
||||||
await expect(await link.getProperty("isValid")).toStrictEqual(false);
|
await expect(await link.getProperty("isValid")).toStrictEqual(false);
|
||||||
await expect(await link.getProperty("toJson")).toEqual({ name: "", href: "" });
|
await expect(await link.getProperty("toJson")).toEqual({ name: "", href: "" });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not be valid if just a name is filled in", async () => {
|
it("should not be valid if just a name is filled in", async () => {
|
||||||
render(html`<ak-admin-settings-footer-link name="link"></ak-admin-settings-footer-link>`);
|
render(html`<ak-admin-settings-footer-link name="link"></ak-admin-settings-footer-link>`);
|
||||||
const link = await $("ak-admin-settings-footer-link");
|
const link = $("ak-admin-settings-footer-link");
|
||||||
await link.$('input[name="name"]').setValue("foo");
|
await link.$('input[name="name"]').setValue("foo");
|
||||||
await expect(await link.getProperty("isValid")).toStrictEqual(false);
|
await expect(await link.getProperty("isValid")).toStrictEqual(false);
|
||||||
await expect(await link.getProperty("toJson")).toEqual({ name: "foo", href: "" });
|
await expect(await link.getProperty("toJson")).toEqual({ name: "foo", href: "" });
|
||||||
@ -33,7 +33,7 @@ describe("ak-admin-settings-footer-link", () => {
|
|||||||
|
|
||||||
it("should be valid if just a URL is filled in", async () => {
|
it("should be valid if just a URL is filled in", async () => {
|
||||||
render(html`<ak-admin-settings-footer-link name="link"></ak-admin-settings-footer-link>`);
|
render(html`<ak-admin-settings-footer-link name="link"></ak-admin-settings-footer-link>`);
|
||||||
const link = await $("ak-admin-settings-footer-link");
|
const link = $("ak-admin-settings-footer-link");
|
||||||
await link.$('input[name="href"]').setValue("https://foo.com");
|
await link.$('input[name="href"]').setValue("https://foo.com");
|
||||||
await expect(await link.getProperty("isValid")).toStrictEqual(true);
|
await expect(await link.getProperty("isValid")).toStrictEqual(true);
|
||||||
await expect(await link.getProperty("toJson")).toEqual({
|
await expect(await link.getProperty("toJson")).toEqual({
|
||||||
@ -44,7 +44,7 @@ describe("ak-admin-settings-footer-link", () => {
|
|||||||
|
|
||||||
it("should be valid if both are filled in", async () => {
|
it("should be valid if both are filled in", async () => {
|
||||||
render(html`<ak-admin-settings-footer-link name="link"></ak-admin-settings-footer-link>`);
|
render(html`<ak-admin-settings-footer-link name="link"></ak-admin-settings-footer-link>`);
|
||||||
const link = await $("ak-admin-settings-footer-link");
|
const link = $("ak-admin-settings-footer-link");
|
||||||
await link.$('input[name="name"]').setValue("foo");
|
await link.$('input[name="name"]').setValue("foo");
|
||||||
await link.$('input[name="href"]').setValue("https://foo.com");
|
await link.$('input[name="href"]').setValue("https://foo.com");
|
||||||
await expect(await link.getProperty("isValid")).toStrictEqual(true);
|
await expect(await link.getProperty("isValid")).toStrictEqual(true);
|
||||||
@ -56,7 +56,7 @@ describe("ak-admin-settings-footer-link", () => {
|
|||||||
|
|
||||||
it("should not be valid if the URL is not valid", async () => {
|
it("should not be valid if the URL is not valid", async () => {
|
||||||
render(html`<ak-admin-settings-footer-link name="link"></ak-admin-settings-footer-link>`);
|
render(html`<ak-admin-settings-footer-link name="link"></ak-admin-settings-footer-link>`);
|
||||||
const link = await $("ak-admin-settings-footer-link");
|
const link = $("ak-admin-settings-footer-link");
|
||||||
await link.$('input[name="name"]').setValue("foo");
|
await link.$('input[name="name"]').setValue("foo");
|
||||||
await link.$('input[name="href"]').setValue("never://foo.com");
|
await link.$('input[name="href"]').setValue("never://foo.com");
|
||||||
await expect(await link.getProperty("toJson")).toEqual({
|
await expect(await link.getProperty("toJson")).toEqual({
|
||||||
|
|||||||
@ -79,7 +79,7 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (this.can(CapabilitiesEnum.CanSaveMedia)) {
|
if (this.can(CapabilitiesEnum.CanSaveMedia)) {
|
||||||
const icon = this.getFormFiles()["metaIcon"];
|
const icon = this.getFormFiles().metaIcon;
|
||||||
if (icon || this.clearIcon) {
|
if (icon || this.clearIcon) {
|
||||||
await new CoreApi(DEFAULT_CONFIG).coreApplicationsSetIconCreate({
|
await new CoreApi(DEFAULT_CONFIG).coreApplicationsSetIconCreate({
|
||||||
slug: app.slug,
|
slug: app.slug,
|
||||||
@ -117,7 +117,7 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
|
|||||||
if (!(ev instanceof InputEvent) || !ev.target) {
|
if (!(ev instanceof InputEvent) || !ev.target) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.clearIcon = !!(ev.target as HTMLInputElement).checked;
|
this.clearIcon = Boolean((ev.target as HTMLInputElement).checked);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderForm(): TemplateResult {
|
renderForm(): TemplateResult {
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import "@goauthentik/elements/forms/HorizontalFormElement";
|
|||||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||||
import "@goauthentik/elements/forms/Radio";
|
import "@goauthentik/elements/forms/Radio";
|
||||||
import "@goauthentik/elements/forms/SearchSelect";
|
import "@goauthentik/elements/forms/SearchSelect";
|
||||||
import YAML from "yaml";
|
import * as YAML from "yaml";
|
||||||
|
|
||||||
import { msg } from "@lit/localize";
|
import { msg } from "@lit/localize";
|
||||||
import { CSSResult } from "lit";
|
import { CSSResult } from "lit";
|
||||||
@ -31,9 +31,8 @@ export class ApplicationEntitlementForm extends ModelForm<ApplicationEntitlement
|
|||||||
getSuccessMessage(): string {
|
getSuccessMessage(): string {
|
||||||
if (this.instance?.pbmUuid) {
|
if (this.instance?.pbmUuid) {
|
||||||
return msg("Successfully updated entitlement.");
|
return msg("Successfully updated entitlement.");
|
||||||
} else {
|
|
||||||
return msg("Successfully created entitlement.");
|
|
||||||
}
|
}
|
||||||
|
return msg("Successfully created entitlement.");
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult[] {
|
||||||
@ -49,11 +48,10 @@ export class ApplicationEntitlementForm extends ModelForm<ApplicationEntitlement
|
|||||||
pbmUuid: this.instance.pbmUuid || "",
|
pbmUuid: this.instance.pbmUuid || "",
|
||||||
applicationEntitlementRequest: data,
|
applicationEntitlementRequest: data,
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
return new CoreApi(DEFAULT_CONFIG).coreApplicationEntitlementsCreate({
|
|
||||||
applicationEntitlementRequest: data,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
return new CoreApi(DEFAULT_CONFIG).coreApplicationEntitlementsCreate({
|
||||||
|
applicationEntitlementRequest: data,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
renderForm(): TemplateResult {
|
renderForm(): TemplateResult {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { styles } from "@goauthentik/admin/applications/wizard/ApplicationWizardFormStepStyles.css.js";
|
import { styles } from "@goauthentik/admin/applications/wizard/ApplicationWizardFormStepStyles.css.js";
|
||||||
import { WizardStep } from "@goauthentik/components/ak-wizard/WizardStep.js";
|
import { WizardStep } from "@goauthentik/components/ak-wizard/WizardStep.js";
|
||||||
import {
|
import {
|
||||||
NavigationEventInit,
|
NavigationUpdate,
|
||||||
WizardNavigationEvent,
|
WizardNavigationEvent,
|
||||||
WizardUpdateEvent,
|
WizardUpdateEvent,
|
||||||
} from "@goauthentik/components/ak-wizard/events";
|
} from "@goauthentik/components/ak-wizard/events";
|
||||||
@ -14,9 +14,9 @@ import { property, query } from "lit/decorators.js";
|
|||||||
import { ValidationError } from "@goauthentik/api";
|
import { ValidationError } from "@goauthentik/api";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ApplicationTransactionValidationError,
|
|
||||||
type ApplicationWizardState,
|
type ApplicationWizardState,
|
||||||
type ApplicationWizardStateUpdate,
|
type ApplicationWizardStateUpdate,
|
||||||
|
ExtendedValidationError,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
export class ApplicationWizardStep extends WizardStep {
|
export class ApplicationWizardStep extends WizardStep {
|
||||||
@ -48,7 +48,7 @@ export class ApplicationWizardStep extends WizardStep {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected removeErrors(
|
protected removeErrors(
|
||||||
keyToDelete: keyof ApplicationTransactionValidationError,
|
keyToDelete: keyof ExtendedValidationError,
|
||||||
): ValidationError | undefined {
|
): ValidationError | undefined {
|
||||||
if (!this.wizard.errors) {
|
if (!this.wizard.errors) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@ -71,7 +71,7 @@ export class ApplicationWizardStep extends WizardStep {
|
|||||||
public handleUpdate(
|
public handleUpdate(
|
||||||
update?: ApplicationWizardStateUpdate,
|
update?: ApplicationWizardStateUpdate,
|
||||||
destination?: string,
|
destination?: string,
|
||||||
enable?: NavigationEventInit,
|
enable?: NavigationUpdate,
|
||||||
) {
|
) {
|
||||||
// Inform ApplicationWizard of content state
|
// Inform ApplicationWizard of content state
|
||||||
if (update) {
|
if (update) {
|
||||||
|
|||||||
@ -15,7 +15,6 @@ import {
|
|||||||
ProviderModelEnum,
|
ProviderModelEnum,
|
||||||
ProxyMode,
|
ProxyMode,
|
||||||
ProxyProvider,
|
ProxyProvider,
|
||||||
RACProvider,
|
|
||||||
RadiusProvider,
|
RadiusProvider,
|
||||||
RedirectURI,
|
RedirectURI,
|
||||||
SAMLProvider,
|
SAMLProvider,
|
||||||
@ -51,9 +50,8 @@ function renderRadiusOverview(rawProvider: OneOfProvider) {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderRACOverview(rawProvider: OneOfProvider) {
|
function renderRACOverview(_rawProvider: OneOfProvider) {
|
||||||
// @ts-expect-error TS6133
|
// const _provider = rawProvider as RACProvider;
|
||||||
const _provider = rawProvider as RACProvider;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatRedirectUris(uris: RedirectURI[] = []) {
|
function formatRedirectUris(uris: RedirectURI[] = []) {
|
||||||
|
|||||||
@ -1,14 +1,13 @@
|
|||||||
import { policyOptions } from "@goauthentik/admin/applications/PolicyOptions.js";
|
import { policyOptions } from "@goauthentik/admin/applications/PolicyOptions.js";
|
||||||
import { ApplicationWizardStep } from "@goauthentik/admin/applications/wizard/ApplicationWizardStep.js";
|
import { ApplicationWizardStep } from "@goauthentik/admin/applications/wizard/ApplicationWizardStep.js";
|
||||||
import "@goauthentik/admin/applications/wizard/ak-wizard-title.js";
|
import "@goauthentik/admin/applications/wizard/ak-wizard-title.js";
|
||||||
import { isSlug } from "@goauthentik/common/utils.js";
|
import { isSlug, isURLInput } from "@goauthentik/common/utils.js";
|
||||||
import { camelToSnake } from "@goauthentik/common/utils.js";
|
import { camelToSnake } from "@goauthentik/common/utils.js";
|
||||||
import "@goauthentik/components/ak-radio-input";
|
import "@goauthentik/components/ak-radio-input";
|
||||||
import "@goauthentik/components/ak-slug-input";
|
import "@goauthentik/components/ak-slug-input";
|
||||||
import "@goauthentik/components/ak-switch-input";
|
import "@goauthentik/components/ak-switch-input";
|
||||||
import "@goauthentik/components/ak-text-input";
|
import "@goauthentik/components/ak-text-input";
|
||||||
import { type NavigableButton, type WizardButton } from "@goauthentik/components/ak-wizard/types";
|
import { type NavigableButton, type WizardButton } from "@goauthentik/components/ak-wizard/types";
|
||||||
import { type KeyUnknown } from "@goauthentik/elements/forms/Form";
|
|
||||||
import "@goauthentik/elements/forms/FormGroup";
|
import "@goauthentik/elements/forms/FormGroup";
|
||||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||||
|
|
||||||
@ -21,13 +20,25 @@ import { type ApplicationRequest } from "@goauthentik/api";
|
|||||||
|
|
||||||
import { ApplicationWizardStateUpdate, ValidationRecord } from "../types";
|
import { ApplicationWizardStateUpdate, ValidationRecord } from "../types";
|
||||||
|
|
||||||
const autoTrim = (v: unknown) => (typeof v === "string" ? v.trim() : v);
|
/**
|
||||||
|
* Plucks the specified keys from an object, trimming their values if they are strings.
|
||||||
|
*
|
||||||
|
* @template T - The type of the input object.
|
||||||
|
* @template K - The keys to be plucked from the input object.
|
||||||
|
*
|
||||||
|
* @param {T} input - The input object.
|
||||||
|
* @param {Array<K>} keys - The keys to be plucked from the input object.
|
||||||
|
*/
|
||||||
|
function trimMany<T extends object, K extends keyof T>(input: T, keys: Array<K>): Pick<T, K> {
|
||||||
|
const result: Partial<T> = {};
|
||||||
|
|
||||||
const trimMany = (o: KeyUnknown, vs: string[]) =>
|
for (const key of keys) {
|
||||||
Object.fromEntries(vs.map((v) => [v, autoTrim(o[v])]));
|
const value = input[key];
|
||||||
|
result[key] = (typeof value === "string" ? value.trim() : value) as T[K];
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
return result as Pick<T, K>;
|
||||||
const isStr = (v: any): v is string => typeof v === "string";
|
}
|
||||||
|
|
||||||
@customElement("ak-application-wizard-application-step")
|
@customElement("ak-application-wizard-application-step")
|
||||||
export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
|
export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
|
||||||
@ -37,7 +48,7 @@ export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
|
|||||||
errors = new Map<string, string>();
|
errors = new Map<string, string>();
|
||||||
|
|
||||||
@query("form#applicationform")
|
@query("form#applicationform")
|
||||||
form!: HTMLFormElement;
|
declare form: HTMLFormElement;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
@ -54,27 +65,34 @@ export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get buttons(): WizardButton[] {
|
get buttons(): WizardButton[] {
|
||||||
return [{ kind: "next", destination: "provider-choice" }, { kind: "cancel" }];
|
return [
|
||||||
|
// ---
|
||||||
|
{ kind: "next", destination: "provider-choice" },
|
||||||
|
{ kind: "cancel" },
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
get valid() {
|
get valid() {
|
||||||
this.errors = new Map();
|
this.errors = new Map();
|
||||||
const values = trimMany(this.formValues ?? {}, ["metaLaunchUrl", "name", "slug"]);
|
|
||||||
|
|
||||||
if (values["name"] === "") {
|
const trimmed = trimMany((this.formValues || {}) as Partial<ApplicationRequest>, [
|
||||||
|
"name",
|
||||||
|
"slug",
|
||||||
|
"metaLaunchUrl",
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!trimmed.name) {
|
||||||
this.errors.set("name", msg("An application name is required"));
|
this.errors.set("name", msg("An application name is required"));
|
||||||
}
|
}
|
||||||
if (
|
|
||||||
!(
|
if (!isURLInput(trimmed.metaLaunchUrl)) {
|
||||||
isStr(values["metaLaunchUrl"]) &&
|
|
||||||
(values["metaLaunchUrl"] === "" || URL.canParse(values["metaLaunchUrl"]))
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
this.errors.set("metaLaunchUrl", msg("Not a valid URL"));
|
this.errors.set("metaLaunchUrl", msg("Not a valid URL"));
|
||||||
}
|
}
|
||||||
if (!(isStr(values["slug"]) && values["slug"] !== "" && isSlug(values["slug"]))) {
|
|
||||||
|
if (!isSlug(trimmed.slug)) {
|
||||||
this.errors.set("slug", msg("Not a valid slug"));
|
this.errors.set("slug", msg("Not a valid slug"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.errors.size === 0;
|
return this.errors.size === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,27 +100,39 @@ export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
|
|||||||
if (button.kind === "next") {
|
if (button.kind === "next") {
|
||||||
if (!this.valid) {
|
if (!this.valid) {
|
||||||
this.handleEnabling({
|
this.handleEnabling({
|
||||||
disabled: ["provider-choice", "provider", "bindings", "submit"],
|
disabled: [
|
||||||
|
// ---
|
||||||
|
"provider-choice",
|
||||||
|
"provider",
|
||||||
|
"bindings",
|
||||||
|
"submit",
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const app: Partial<ApplicationRequest> = this.formValues as Partial<ApplicationRequest>;
|
const app: Partial<ApplicationRequest> = this.formValues as Partial<ApplicationRequest>;
|
||||||
|
|
||||||
let payload: ApplicationWizardStateUpdate = {
|
let payload: ApplicationWizardStateUpdate = {
|
||||||
app: this.formValues,
|
app: this.formValues,
|
||||||
errors: this.removeErrors("app"),
|
errors: this.removeErrors("app"),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (app.name && (this.wizard.provider?.name ?? "").trim() === "") {
|
if (app.name && (this.wizard.provider?.name ?? "").trim() === "") {
|
||||||
payload = {
|
payload = {
|
||||||
...payload,
|
...payload,
|
||||||
provider: { name: `Provider for ${app.name}` },
|
provider: { name: `Provider for ${app.name}` },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handleUpdate(payload, button.destination, {
|
this.handleUpdate(payload, button.destination, {
|
||||||
enable: "provider-choice",
|
enable: "provider-choice",
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
super.handleButton(button);
|
super.handleButton(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,6 +211,7 @@ export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
|
|||||||
if (!(this.wizard.app && this.wizard.errors)) {
|
if (!(this.wizard.app && this.wizard.errors)) {
|
||||||
throw new Error("Application Step received uninitialized wizard context.");
|
throw new Error("Application Step received uninitialized wizard context.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.renderForm(
|
return this.renderForm(
|
||||||
this.wizard.app as ApplicationRequest,
|
this.wizard.app as ApplicationRequest,
|
||||||
this.wizard.errors?.app ?? {},
|
this.wizard.errors?.app ?? {},
|
||||||
|
|||||||
@ -45,7 +45,7 @@ export class ApplicationWizardEditBindingStep extends ApplicationWizardStep {
|
|||||||
hide = true;
|
hide = true;
|
||||||
|
|
||||||
@query("form#bindingform")
|
@query("form#bindingform")
|
||||||
form!: HTMLFormElement;
|
declare form: HTMLFormElement;
|
||||||
|
|
||||||
@query(".policy-search-select")
|
@query(".policy-search-select")
|
||||||
searchSelect!: SearchSelectBase<Policy> | SearchSelectBase<Group> | SearchSelectBase<User>;
|
searchSelect!: SearchSelectBase<Policy> | SearchSelectBase<Group> | SearchSelectBase<User>;
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
import "@goauthentik/admin/applications/wizard/ak-wizard-title.js";
|
import "@goauthentik/admin/applications/wizard/ak-wizard-title.js";
|
||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||||
import { parseAPIResponseError } from "@goauthentik/common/errors/network";
|
import { parseAPIError } from "@goauthentik/common/errors";
|
||||||
import { WizardNavigationEvent } from "@goauthentik/components/ak-wizard/events.js";
|
import { WizardNavigationEvent } from "@goauthentik/components/ak-wizard/events.js";
|
||||||
import { type WizardButton } from "@goauthentik/components/ak-wizard/types";
|
import { type WizardButton } from "@goauthentik/components/ak-wizard/types";
|
||||||
import { showAPIErrorMessage } from "@goauthentik/elements/messages/MessageContainer";
|
|
||||||
import { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter";
|
import { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter";
|
||||||
import { P, match } from "ts-pattern";
|
import { P, match } from "ts-pattern";
|
||||||
|
|
||||||
@ -31,11 +30,10 @@ import {
|
|||||||
type TransactionApplicationRequest,
|
type TransactionApplicationRequest,
|
||||||
type TransactionApplicationResponse,
|
type TransactionApplicationResponse,
|
||||||
type TransactionPolicyBindingRequest,
|
type TransactionPolicyBindingRequest,
|
||||||
instanceOfValidationError,
|
|
||||||
} from "@goauthentik/api";
|
} from "@goauthentik/api";
|
||||||
|
|
||||||
import { ApplicationWizardStep } from "../ApplicationWizardStep.js";
|
import { ApplicationWizardStep } from "../ApplicationWizardStep.js";
|
||||||
import { OneOfProvider, isApplicationTransactionValidationError } from "../types.js";
|
import { ExtendedValidationError, OneOfProvider } from "../types.js";
|
||||||
import { providerRenderers } from "./SubmitStepOverviewRenderers.js";
|
import { providerRenderers } from "./SubmitStepOverviewRenderers.js";
|
||||||
|
|
||||||
const _submitStates = ["reviewing", "running", "submitted"] as const;
|
const _submitStates = ["reviewing", "running", "submitted"] as const;
|
||||||
@ -133,46 +131,39 @@ export class ApplicationWizardSubmitStep extends CustomEmitterElement(Applicatio
|
|||||||
|
|
||||||
this.state = "running";
|
this.state = "running";
|
||||||
|
|
||||||
return new CoreApi(DEFAULT_CONFIG)
|
return (
|
||||||
.coreTransactionalApplicationsUpdate({
|
new CoreApi(DEFAULT_CONFIG)
|
||||||
transactionApplicationRequest: request,
|
.coreTransactionalApplicationsUpdate({
|
||||||
})
|
transactionApplicationRequest: request,
|
||||||
.then((_response: TransactionApplicationResponse) => {
|
})
|
||||||
this.dispatchCustomEvent(EVENT_REFRESH);
|
.then((_response: TransactionApplicationResponse) => {
|
||||||
this.state = "submitted";
|
this.dispatchCustomEvent(EVENT_REFRESH);
|
||||||
})
|
this.state = "submitted";
|
||||||
|
})
|
||||||
|
|
||||||
.catch(async (error) => {
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const parsedError = await parseAPIResponseError(error);
|
.catch(async (resolution: any) => {
|
||||||
|
const errors = (await parseAPIError(
|
||||||
|
await resolution,
|
||||||
|
)) as ExtendedValidationError;
|
||||||
|
|
||||||
if (!instanceOfValidationError(parsedError)) {
|
// THIS is a really gross special case; if the user is duplicating the name of
|
||||||
showAPIErrorMessage(parsedError);
|
// an existing provider, the error appears on the `app` (!) error object. We
|
||||||
|
// have to move that to the `provider.name` error field so it shows up in the
|
||||||
return;
|
// right place.
|
||||||
}
|
if (Array.isArray(errors?.app?.provider)) {
|
||||||
|
const providerError = errors.app.provider;
|
||||||
if (isApplicationTransactionValidationError(parsedError)) {
|
errors.provider = errors.provider ?? {};
|
||||||
// THIS is a really gross special case; if the user is duplicating the name of an existing provider, the error appears on the `app` (!) error object.
|
errors.provider.name = providerError;
|
||||||
// We have to move that to the `provider.name` error field so it shows up in the right place.
|
delete errors.app.provider;
|
||||||
if (Array.isArray(parsedError.app?.provider)) {
|
if (Object.keys(errors.app).length === 0) {
|
||||||
const providerError = parsedError.app.provider;
|
delete errors.app;
|
||||||
|
|
||||||
parsedError.provider = {
|
|
||||||
...parsedError.provider,
|
|
||||||
name: providerError,
|
|
||||||
};
|
|
||||||
|
|
||||||
delete parsedError.app.provider;
|
|
||||||
|
|
||||||
if (Object.keys(parsedError.app).length === 0) {
|
|
||||||
delete parsedError.app;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
this.handleUpdate({ errors });
|
||||||
|
this.state = "reviewing";
|
||||||
this.handleUpdate({ errors: parsedError });
|
})
|
||||||
this.state = "reviewing";
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override handleButton(button: WizardButton) {
|
override handleButton(button: WizardButton) {
|
||||||
@ -234,20 +225,22 @@ export class ApplicationWizardSubmitStep extends CustomEmitterElement(Applicatio
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderError() {
|
renderError() {
|
||||||
const { errors } = this.wizard;
|
if (Object.keys(this.wizard.errors).length === 0) {
|
||||||
|
return nothing;
|
||||||
if (Object.keys(errors).length === 0) return nothing;
|
}
|
||||||
|
|
||||||
|
const navTo = (step: string) => () => this.dispatchEvent(new WizardNavigationEvent(step));
|
||||||
|
const errors = this.wizard.errors;
|
||||||
return html` <hr class="pf-c-divider" />
|
return html` <hr class="pf-c-divider" />
|
||||||
${match(errors)
|
${match(errors as ExtendedValidationError)
|
||||||
.with(
|
.with(
|
||||||
{ app: P.nonNullable },
|
{ app: P.nonNullable },
|
||||||
() =>
|
() =>
|
||||||
html`<p>${msg("There was an error in the application.")}</p>
|
html`<p>${msg("There was an error in the application.")}</p>
|
||||||
<p>
|
<p>
|
||||||
<a @click=${WizardNavigationEvent.toListener(this, "application")}>
|
<a @click=${navTo("application")}
|
||||||
${msg("Review the application.")}
|
>${msg("Review the application.")}</a
|
||||||
</a>
|
>
|
||||||
</p>`,
|
</p>`,
|
||||||
)
|
)
|
||||||
.with(
|
.with(
|
||||||
@ -255,20 +248,13 @@ export class ApplicationWizardSubmitStep extends CustomEmitterElement(Applicatio
|
|||||||
() =>
|
() =>
|
||||||
html`<p>${msg("There was an error in the provider.")}</p>
|
html`<p>${msg("There was an error in the provider.")}</p>
|
||||||
<p>
|
<p>
|
||||||
<a @click=${WizardNavigationEvent.toListener(this, "provider")}
|
<a @click=${navTo("provider")}>${msg("Review the provider.")}</a>
|
||||||
>${msg("Review the provider.")}</a
|
|
||||||
>
|
|
||||||
</p>`,
|
</p>`,
|
||||||
)
|
)
|
||||||
.with(
|
.with(
|
||||||
{ detail: P.nonNullable },
|
{ detail: P.nonNullable },
|
||||||
() =>
|
() =>
|
||||||
html`<p>
|
`<p>${msg("There was an error. Please go back and review the application.")}: ${errors.detail}</p>`,
|
||||||
${msg(
|
|
||||||
"There was an error. Please go back and review the application.",
|
|
||||||
)}:
|
|
||||||
${errors.detail}
|
|
||||||
</p>`,
|
|
||||||
)
|
)
|
||||||
.with(
|
.with(
|
||||||
{
|
{
|
||||||
@ -278,7 +264,7 @@ export class ApplicationWizardSubmitStep extends CustomEmitterElement(Applicatio
|
|||||||
html`<p>${msg("There was an error:")}:</p>
|
html`<p>${msg("There was an error:")}:</p>
|
||||||
<ul>
|
<ul>
|
||||||
${(errors.nonFieldErrors ?? []).map(
|
${(errors.nonFieldErrors ?? []).map(
|
||||||
(reason) => html`<li>${reason}</li>`,
|
(e: string) => html`<li>${e}</li>`,
|
||||||
)}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
<p>${msg("Please go back and review the application.")}</p>`,
|
<p>${msg("Please go back and review the application.")}</p>`,
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import { customElement, state } from "lit/decorators.js";
|
|||||||
import { OAuth2ProviderRequest, SourcesApi } from "@goauthentik/api";
|
import { OAuth2ProviderRequest, SourcesApi } from "@goauthentik/api";
|
||||||
import { type OAuth2Provider, type PaginatedOAuthSourceList } from "@goauthentik/api";
|
import { type OAuth2Provider, type PaginatedOAuthSourceList } from "@goauthentik/api";
|
||||||
|
|
||||||
import { ApplicationTransactionValidationError } from "../../types.js";
|
import { ExtendedValidationError } from "../../types.js";
|
||||||
import { ApplicationWizardProviderForm } from "./ApplicationWizardProviderForm.js";
|
import { ApplicationWizardProviderForm } from "./ApplicationWizardProviderForm.js";
|
||||||
|
|
||||||
@customElement("ak-application-wizard-provider-for-oauth")
|
@customElement("ak-application-wizard-provider-for-oauth")
|
||||||
@ -34,7 +34,7 @@ export class ApplicationWizardOauth2ProviderForm extends ApplicationWizardProvid
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
renderForm(provider: OAuth2Provider, errors: ApplicationTransactionValidationError) {
|
renderForm(provider: OAuth2Provider, errors: ExtendedValidationError) {
|
||||||
const showClientSecretCallback = (show: boolean) => {
|
const showClientSecretCallback = (show: boolean) => {
|
||||||
this.showClientSecret = show;
|
this.showClientSecret = show;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -22,7 +22,7 @@ export class ApplicationWizardProviderSamlForm extends ApplicationWizardProvider
|
|||||||
const setHasSigningKp = (ev: InputEvent) => {
|
const setHasSigningKp = (ev: InputEvent) => {
|
||||||
const target = ev.target as AkCryptoCertificateSearch;
|
const target = ev.target as AkCryptoCertificateSearch;
|
||||||
if (!target) return;
|
if (!target) return;
|
||||||
this.hasSigningKp = !!target.selectedKeypair;
|
this.hasSigningKp = Boolean(target.selectedKeypair);
|
||||||
};
|
};
|
||||||
|
|
||||||
return html` <ak-wizard-title>${this.label}</ak-wizard-title>
|
return html` <ak-wizard-title>${this.label}</ak-wizard-title>
|
||||||
|
|||||||
@ -25,30 +25,16 @@ export type OneOfProvider =
|
|||||||
|
|
||||||
export type ValidationRecord = { [key: string]: string[] };
|
export type ValidationRecord = { [key: string]: string[] };
|
||||||
|
|
||||||
/**
|
// TODO: Elf, extend this type and apply it to every object in the wizard. Then run
|
||||||
* An error that occurs during the creation or modification of an application.
|
// the type-checker again.
|
||||||
*
|
|
||||||
* @todo (Elf) Extend this type to include all possible errors that can occur during the creation or modification of an application.
|
export type ExtendedValidationError = ValidationError & {
|
||||||
*/
|
|
||||||
export interface ApplicationTransactionValidationError extends ValidationError {
|
|
||||||
app?: ValidationRecord;
|
app?: ValidationRecord;
|
||||||
provider?: ValidationRecord;
|
provider?: ValidationRecord;
|
||||||
bindings?: ValidationRecord;
|
bindings?: ValidationRecord;
|
||||||
detail?: unknown;
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
}
|
detail?: any;
|
||||||
|
};
|
||||||
/**
|
|
||||||
* Type-guard to determine if an API response is shaped like an {@linkcode ApplicationTransactionValidationError}.
|
|
||||||
*/
|
|
||||||
export function isApplicationTransactionValidationError(
|
|
||||||
error: ValidationError,
|
|
||||||
): error is ApplicationTransactionValidationError {
|
|
||||||
if ("app" in error) return true;
|
|
||||||
if ("provider" in error) return true;
|
|
||||||
if ("bindings" in error) return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We use the PolicyBinding instead of the PolicyBindingRequest here, because that gives us a slot
|
// We use the PolicyBinding instead of the PolicyBindingRequest here, because that gives us a slot
|
||||||
// in which to preserve the retrieved policy, group, or user object from the SearchSelect used to
|
// in which to preserve the retrieved policy, group, or user object from the SearchSelect used to
|
||||||
@ -63,7 +49,7 @@ export interface ApplicationWizardState {
|
|||||||
proxyMode: ProxyMode;
|
proxyMode: ProxyMode;
|
||||||
bindings: PolicyBinding[];
|
bindings: PolicyBinding[];
|
||||||
currentBinding: number;
|
currentBinding: number;
|
||||||
errors: ValidationError | ApplicationTransactionValidationError;
|
errors: ExtendedValidationError;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApplicationWizardStateUpdate {
|
export interface ApplicationWizardStateUpdate {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user