Compare commits
103 Commits
healthchec
...
version/20
Author | SHA1 | Date | |
---|---|---|---|
35cd126406 | |||
f89a4fc276 | |||
4d7f380b2d | |||
cb8379031a | |||
0c604ceba4 | |||
30e39c75ff | |||
6d7bebbcc3 | |||
dc332ec7b0 | |||
31e94a2814 | |||
eb08214f0e | |||
a5ab8a618e | |||
b8cbdcae22 | |||
ae86184511 | |||
b704388c2f | |||
a35f9fdd7b | |||
d95220be0e | |||
ba1b86efa1 | |||
cd93de1141 | |||
cc148bd552 | |||
8f82dac84e | |||
89c08f2588 | |||
113d6cc45f | |||
05cfbca5f2 | |||
385f9bcbac | |||
86bc5b4cdb | |||
8d5d9f35ef | |||
439169d5e7 | |||
e405b33f9f | |||
a9c13d4d10 | |||
12b16b17a2 | |||
3473abee32 | |||
b5a0b3a521 | |||
837a0325ca | |||
b1050e8825 | |||
7bb90b1661 | |||
8f755785ea | |||
adc0ec8ee4 | |||
9379b3d178 | |||
18eef8ae05 | |||
f0e22fd08b | |||
ce5297a6cd | |||
74c8df8782 | |||
2d897fac48 | |||
2eb47c9efa | |||
9852041f38 | |||
7f74936212 | |||
4f40b1e27c | |||
041e407153 | |||
31b891428e | |||
4ab8247847 | |||
af24edf8c1 | |||
f8bfd12e31 | |||
993fc4b77b | |||
3ee2be09bf | |||
b3e3948f44 | |||
117a5cd88d | |||
342a40212e | |||
15ae11d9d5 | |||
e11df56f21 | |||
3771af5282 | |||
0718053c56 | |||
3e5014bfea | |||
78f49ddc04 | |||
744bc54231 | |||
44ba30ad75 | |||
63b991f137 | |||
7f9acb8a2b | |||
ce3ba32044 | |||
25e6a69331 | |||
78d07cc355 | |||
3c4df47fe3 | |||
8ed1805cb8 | |||
4d23db73ca | |||
72783953fb | |||
3b0fdb3dbd | |||
23161eed12 | |||
8918427588 | |||
5d858020f6 | |||
198e8b98a8 | |||
88e9c9b669 | |||
0c652a210d | |||
105a90d2e7 | |||
68f5abe318 | |||
59d4c18636 | |||
b67e2a1144 | |||
fc025651ce | |||
7cedc840b0 | |||
5ba731e48b | |||
c792534a50 | |||
a136fd8b54 | |||
fb63c1f3e9 | |||
0b15ab3f27 | |||
f9fd67c2b8 | |||
4ac19f9d44 | |||
74d3e92bac | |||
207fa13405 | |||
208e4a8bed | |||
ee245ab390 | |||
60c8837082 | |||
6cf418a37e | |||
254761e930 | |||
d85f8758fe | |||
84bfb37b60 |
@ -1,5 +1,5 @@
|
||||
[bumpversion]
|
||||
current_version = 2024.4.2
|
||||
current_version = 2024.6.0-rc1
|
||||
tag = True
|
||||
commit = True
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-(?P<rc_t>[a-zA-Z-]+)(?P<rc_n>[1-9]\\d*))?
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
from os import environ
|
||||
|
||||
__version__ = "2024.4.2"
|
||||
__version__ = "2024.6.0"
|
||||
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"
|
||||
|
||||
|
||||
|
@ -16,6 +16,7 @@ from rest_framework.views import APIView
|
||||
|
||||
from authentik import get_full_version
|
||||
from authentik.core.api.utils import PassiveSerializer
|
||||
from authentik.enterprise.license import LicenseKey
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.utils.reflection import get_env
|
||||
from authentik.outposts.apps import MANAGED_OUTPOST
|
||||
@ -32,7 +33,7 @@ class RuntimeDict(TypedDict):
|
||||
platform: str
|
||||
uname: str
|
||||
openssl_version: str
|
||||
openssl_fips_mode: bool
|
||||
openssl_fips_enabled: bool | None
|
||||
authentik_version: str
|
||||
|
||||
|
||||
@ -71,7 +72,9 @@ class SystemInfoSerializer(PassiveSerializer):
|
||||
"architecture": platform.machine(),
|
||||
"authentik_version": get_full_version(),
|
||||
"environment": get_env(),
|
||||
"openssl_fips_enabled": backend._fips_enabled,
|
||||
"openssl_fips_enabled": (
|
||||
backend._fips_enabled if LicenseKey.get_total().is_valid() else None
|
||||
),
|
||||
"openssl_version": OPENSSL_VERSION,
|
||||
"platform": platform.platform(),
|
||||
"python_version": python_version,
|
||||
|
@ -58,7 +58,7 @@ from authentik.outposts.models import OutpostServiceConnection
|
||||
from authentik.policies.models import Policy, PolicyBindingModel
|
||||
from authentik.policies.reputation.models import Reputation
|
||||
from authentik.providers.oauth2.models import AccessToken, AuthorizationCode, RefreshToken
|
||||
from authentik.providers.scim.models import SCIMGroup, SCIMUser
|
||||
from authentik.providers.scim.models import SCIMProviderGroup, SCIMProviderUser
|
||||
from authentik.sources.scim.models import SCIMSourceGroup, SCIMSourceUser
|
||||
from authentik.stages.authenticator_webauthn.models import WebAuthnDeviceType
|
||||
from authentik.tenants.models import Tenant
|
||||
@ -97,8 +97,8 @@ def excluded_models() -> list[type[Model]]:
|
||||
# FIXME: these shouldn't need to be explicitly listed, but rather based off of a mixin
|
||||
FlowToken,
|
||||
LicenseUsage,
|
||||
SCIMGroup,
|
||||
SCIMUser,
|
||||
SCIMProviderGroup,
|
||||
SCIMProviderUser,
|
||||
Tenant,
|
||||
SystemTask,
|
||||
ConnectionToken,
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
from json import loads
|
||||
|
||||
from django.db.models import Prefetch
|
||||
from django.http import Http404
|
||||
from django_filters.filters import CharFilter, ModelMultipleChoiceFilter
|
||||
from django_filters.filterset import FilterSet
|
||||
@ -166,8 +167,14 @@ class GroupViewSet(UsedByMixin, ModelViewSet):
|
||||
|
||||
def get_queryset(self):
|
||||
base_qs = Group.objects.all().select_related("parent").prefetch_related("roles")
|
||||
|
||||
if self.serializer_class(context={"request": self.request})._should_include_users:
|
||||
base_qs = base_qs.prefetch_related("users")
|
||||
else:
|
||||
base_qs = base_qs.prefetch_related(
|
||||
Prefetch("users", queryset=User.objects.all().only("id"))
|
||||
)
|
||||
|
||||
return base_qs
|
||||
|
||||
@extend_schema(
|
||||
|
@ -80,8 +80,10 @@ class PropertyMappingViewSet(
|
||||
class PropertyMappingTestSerializer(PolicyTestSerializer):
|
||||
"""Test property mapping execution for a user/group with context"""
|
||||
|
||||
user = PrimaryKeyRelatedField(queryset=User.objects.all(), required=False)
|
||||
group = PrimaryKeyRelatedField(queryset=Group.objects.all(), required=False)
|
||||
user = PrimaryKeyRelatedField(queryset=User.objects.all(), required=False, allow_null=True)
|
||||
group = PrimaryKeyRelatedField(
|
||||
queryset=Group.objects.all(), required=False, allow_null=True
|
||||
)
|
||||
|
||||
queryset = PropertyMapping.objects.select_subclasses()
|
||||
serializer_class = PropertyMappingSerializer
|
||||
|
@ -39,12 +39,12 @@ def get_delete_action(manager: Manager) -> str:
|
||||
"""Get the delete action from the Foreign key, falls back to cascade"""
|
||||
if hasattr(manager, "field"):
|
||||
if manager.field.remote_field.on_delete.__name__ == SET_NULL.__name__:
|
||||
return DeleteAction.SET_NULL.name
|
||||
return DeleteAction.SET_NULL.value
|
||||
if manager.field.remote_field.on_delete.__name__ == SET_DEFAULT.__name__:
|
||||
return DeleteAction.SET_DEFAULT.name
|
||||
return DeleteAction.SET_DEFAULT.value
|
||||
if hasattr(manager, "source_field"):
|
||||
return DeleteAction.CASCADE_MANY.name
|
||||
return DeleteAction.CASCADE.name
|
||||
return DeleteAction.CASCADE_MANY.value
|
||||
return DeleteAction.CASCADE.value
|
||||
|
||||
|
||||
class UsedByMixin:
|
||||
|
@ -241,7 +241,7 @@ class TestCrypto(APITestCase):
|
||||
"model_name": "oauth2provider",
|
||||
"pk": str(provider.pk),
|
||||
"name": str(provider),
|
||||
"action": DeleteAction.SET_NULL.name,
|
||||
"action": DeleteAction.SET_NULL.value,
|
||||
}
|
||||
],
|
||||
)
|
||||
|
@ -7,6 +7,7 @@ from rest_framework.viewsets import GenericViewSet
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.core.api.users import UserGroupSerializer
|
||||
from authentik.enterprise.providers.google_workspace.models import GoogleWorkspaceProviderGroup
|
||||
from authentik.lib.sync.outgoing.api import OutgoingSyncConnectionCreateMixin
|
||||
|
||||
|
||||
class GoogleWorkspaceProviderGroupSerializer(ModelSerializer):
|
||||
@ -19,6 +20,7 @@ class GoogleWorkspaceProviderGroupSerializer(ModelSerializer):
|
||||
model = GoogleWorkspaceProviderGroup
|
||||
fields = [
|
||||
"id",
|
||||
"google_id",
|
||||
"group",
|
||||
"group_obj",
|
||||
"provider",
|
||||
@ -29,6 +31,7 @@ class GoogleWorkspaceProviderGroupSerializer(ModelSerializer):
|
||||
|
||||
class GoogleWorkspaceProviderGroupViewSet(
|
||||
mixins.CreateModelMixin,
|
||||
OutgoingSyncConnectionCreateMixin,
|
||||
mixins.RetrieveModelMixin,
|
||||
mixins.DestroyModelMixin,
|
||||
UsedByMixin,
|
||||
|
@ -7,6 +7,7 @@ from rest_framework.viewsets import GenericViewSet
|
||||
from authentik.core.api.groups import GroupMemberSerializer
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.enterprise.providers.google_workspace.models import GoogleWorkspaceProviderUser
|
||||
from authentik.lib.sync.outgoing.api import OutgoingSyncConnectionCreateMixin
|
||||
|
||||
|
||||
class GoogleWorkspaceProviderUserSerializer(ModelSerializer):
|
||||
@ -19,6 +20,7 @@ class GoogleWorkspaceProviderUserSerializer(ModelSerializer):
|
||||
model = GoogleWorkspaceProviderUser
|
||||
fields = [
|
||||
"id",
|
||||
"google_id",
|
||||
"user",
|
||||
"user_obj",
|
||||
"provider",
|
||||
@ -29,6 +31,7 @@ class GoogleWorkspaceProviderUserSerializer(ModelSerializer):
|
||||
|
||||
class GoogleWorkspaceProviderUserViewSet(
|
||||
mixins.CreateModelMixin,
|
||||
OutgoingSyncConnectionCreateMixin,
|
||||
mixins.RetrieveModelMixin,
|
||||
mixins.DestroyModelMixin,
|
||||
UsedByMixin,
|
||||
|
@ -92,12 +92,14 @@ class GoogleWorkspaceGroupClient(
|
||||
google_group = self.to_schema(group, connection)
|
||||
self.check_email_valid(google_group["email"])
|
||||
try:
|
||||
return self._request(
|
||||
response = self._request(
|
||||
self.directory_service.groups().update(
|
||||
groupKey=connection.google_id,
|
||||
body=google_group,
|
||||
)
|
||||
)
|
||||
connection.attributes = response
|
||||
connection.save()
|
||||
except NotFoundSyncException:
|
||||
# Resource missing is handled by self.write, which will re-create the group
|
||||
raise
|
||||
@ -212,3 +214,7 @@ class GoogleWorkspaceGroupClient(
|
||||
google_id=google_id,
|
||||
attributes=group,
|
||||
)
|
||||
|
||||
def update_single_attribute(self, connection: GoogleWorkspaceProviderUser):
|
||||
group = self.directory_service.groups().get(connection.google_id)
|
||||
connection.attributes = group
|
||||
|
@ -88,9 +88,11 @@ class GoogleWorkspaceUserClient(GoogleWorkspaceSyncClient[User, GoogleWorkspaceP
|
||||
self.check_email_valid(
|
||||
google_user["primaryEmail"], *[x["address"] for x in google_user.get("emails", [])]
|
||||
)
|
||||
self._request(
|
||||
response = self._request(
|
||||
self.directory_service.users().update(userKey=connection.google_id, body=google_user)
|
||||
)
|
||||
connection.attributes = response
|
||||
connection.save()
|
||||
|
||||
def discover(self):
|
||||
"""Iterate through all users and connect them with authentik users if possible"""
|
||||
@ -117,3 +119,7 @@ class GoogleWorkspaceUserClient(GoogleWorkspaceSyncClient[User, GoogleWorkspaceP
|
||||
google_id=email,
|
||||
attributes=user,
|
||||
)
|
||||
|
||||
def update_single_attribute(self, connection: GoogleWorkspaceProviderUser):
|
||||
user = self.directory_service.users().get(connection.google_id)
|
||||
connection.attributes = user
|
||||
|
@ -31,6 +31,58 @@ def default_scopes() -> list[str]:
|
||||
]
|
||||
|
||||
|
||||
class GoogleWorkspaceProviderUser(SerializerModel):
|
||||
"""Mapping of a user and provider to a Google user ID"""
|
||||
|
||||
id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
|
||||
google_id = models.TextField()
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
provider = models.ForeignKey("GoogleWorkspaceProvider", on_delete=models.CASCADE)
|
||||
attributes = models.JSONField(default=dict)
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
from authentik.enterprise.providers.google_workspace.api.users import (
|
||||
GoogleWorkspaceProviderUserSerializer,
|
||||
)
|
||||
|
||||
return GoogleWorkspaceProviderUserSerializer
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Google Workspace Provider User")
|
||||
verbose_name_plural = _("Google Workspace Provider Users")
|
||||
unique_together = (("google_id", "user", "provider"),)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Google Workspace Provider User {self.user_id} to {self.provider_id}"
|
||||
|
||||
|
||||
class GoogleWorkspaceProviderGroup(SerializerModel):
|
||||
"""Mapping of a group and provider to a Google group ID"""
|
||||
|
||||
id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
|
||||
google_id = models.TextField()
|
||||
group = models.ForeignKey(Group, on_delete=models.CASCADE)
|
||||
provider = models.ForeignKey("GoogleWorkspaceProvider", on_delete=models.CASCADE)
|
||||
attributes = models.JSONField(default=dict)
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
from authentik.enterprise.providers.google_workspace.api.groups import (
|
||||
GoogleWorkspaceProviderGroupSerializer,
|
||||
)
|
||||
|
||||
return GoogleWorkspaceProviderGroupSerializer
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Google Workspace Provider Group")
|
||||
verbose_name_plural = _("Google Workspace Provider Groups")
|
||||
unique_together = (("google_id", "group", "provider"),)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Google Workspace Provider Group {self.group_id} to {self.provider_id}"
|
||||
|
||||
|
||||
class GoogleWorkspaceProvider(OutgoingSyncProvider, BackchannelProvider):
|
||||
"""Sync users from authentik into Google Workspace."""
|
||||
|
||||
@ -59,15 +111,16 @@ class GoogleWorkspaceProvider(OutgoingSyncProvider, BackchannelProvider):
|
||||
)
|
||||
|
||||
def client_for_model(
|
||||
self, model: type[User | Group]
|
||||
self,
|
||||
model: type[User | Group | GoogleWorkspaceProviderUser | GoogleWorkspaceProviderGroup],
|
||||
) -> BaseOutgoingSyncClient[User | Group, Any, Any, Self]:
|
||||
if issubclass(model, User):
|
||||
if issubclass(model, User | GoogleWorkspaceProviderUser):
|
||||
from authentik.enterprise.providers.google_workspace.clients.users import (
|
||||
GoogleWorkspaceUserClient,
|
||||
)
|
||||
|
||||
return GoogleWorkspaceUserClient(self)
|
||||
if issubclass(model, Group):
|
||||
if issubclass(model, Group | GoogleWorkspaceProviderGroup):
|
||||
from authentik.enterprise.providers.google_workspace.clients.groups import (
|
||||
GoogleWorkspaceGroupClient,
|
||||
)
|
||||
@ -144,55 +197,3 @@ class GoogleWorkspaceProviderMapping(PropertyMapping):
|
||||
class Meta:
|
||||
verbose_name = _("Google Workspace Provider Mapping")
|
||||
verbose_name_plural = _("Google Workspace Provider Mappings")
|
||||
|
||||
|
||||
class GoogleWorkspaceProviderUser(SerializerModel):
|
||||
"""Mapping of a user and provider to a Google user ID"""
|
||||
|
||||
id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
|
||||
google_id = models.TextField()
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
provider = models.ForeignKey(GoogleWorkspaceProvider, on_delete=models.CASCADE)
|
||||
attributes = models.JSONField(default=dict)
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
from authentik.enterprise.providers.google_workspace.api.users import (
|
||||
GoogleWorkspaceProviderUserSerializer,
|
||||
)
|
||||
|
||||
return GoogleWorkspaceProviderUserSerializer
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Google Workspace Provider User")
|
||||
verbose_name_plural = _("Google Workspace Provider Users")
|
||||
unique_together = (("google_id", "user", "provider"),)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Google Workspace Provider User {self.user_id} to {self.provider_id}"
|
||||
|
||||
|
||||
class GoogleWorkspaceProviderGroup(SerializerModel):
|
||||
"""Mapping of a group and provider to a Google group ID"""
|
||||
|
||||
id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
|
||||
google_id = models.TextField()
|
||||
group = models.ForeignKey(Group, on_delete=models.CASCADE)
|
||||
provider = models.ForeignKey(GoogleWorkspaceProvider, on_delete=models.CASCADE)
|
||||
attributes = models.JSONField(default=dict)
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
from authentik.enterprise.providers.google_workspace.api.groups import (
|
||||
GoogleWorkspaceProviderGroupSerializer,
|
||||
)
|
||||
|
||||
return GoogleWorkspaceProviderGroupSerializer
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Google Workspace Provider Group")
|
||||
verbose_name_plural = _("Google Workspace Provider Groups")
|
||||
unique_together = (("google_id", "group", "provider"),)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Google Workspace Provider Group {self.group_id} to {self.provider_id}"
|
||||
|
@ -7,6 +7,7 @@ from rest_framework.viewsets import GenericViewSet
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.core.api.users import UserGroupSerializer
|
||||
from authentik.enterprise.providers.microsoft_entra.models import MicrosoftEntraProviderGroup
|
||||
from authentik.lib.sync.outgoing.api import OutgoingSyncConnectionCreateMixin
|
||||
|
||||
|
||||
class MicrosoftEntraProviderGroupSerializer(ModelSerializer):
|
||||
@ -19,6 +20,7 @@ class MicrosoftEntraProviderGroupSerializer(ModelSerializer):
|
||||
model = MicrosoftEntraProviderGroup
|
||||
fields = [
|
||||
"id",
|
||||
"microsoft_id",
|
||||
"group",
|
||||
"group_obj",
|
||||
"provider",
|
||||
@ -29,6 +31,7 @@ class MicrosoftEntraProviderGroupSerializer(ModelSerializer):
|
||||
|
||||
class MicrosoftEntraProviderGroupViewSet(
|
||||
mixins.CreateModelMixin,
|
||||
OutgoingSyncConnectionCreateMixin,
|
||||
mixins.RetrieveModelMixin,
|
||||
mixins.DestroyModelMixin,
|
||||
UsedByMixin,
|
||||
|
@ -7,6 +7,7 @@ from rest_framework.viewsets import GenericViewSet
|
||||
from authentik.core.api.groups import GroupMemberSerializer
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.enterprise.providers.microsoft_entra.models import MicrosoftEntraProviderUser
|
||||
from authentik.lib.sync.outgoing.api import OutgoingSyncConnectionCreateMixin
|
||||
|
||||
|
||||
class MicrosoftEntraProviderUserSerializer(ModelSerializer):
|
||||
@ -19,6 +20,7 @@ class MicrosoftEntraProviderUserSerializer(ModelSerializer):
|
||||
model = MicrosoftEntraProviderUser
|
||||
fields = [
|
||||
"id",
|
||||
"microsoft_id",
|
||||
"user",
|
||||
"user_obj",
|
||||
"provider",
|
||||
@ -28,6 +30,7 @@ class MicrosoftEntraProviderUserSerializer(ModelSerializer):
|
||||
|
||||
|
||||
class MicrosoftEntraProviderUserViewSet(
|
||||
OutgoingSyncConnectionCreateMixin,
|
||||
mixins.CreateModelMixin,
|
||||
mixins.RetrieveModelMixin,
|
||||
mixins.DestroyModelMixin,
|
||||
|
@ -23,6 +23,7 @@ from msgraph.graph_service_client import GraphServiceClient
|
||||
from msgraph_core import GraphClientFactory
|
||||
|
||||
from authentik.enterprise.providers.microsoft_entra.models import MicrosoftEntraProvider
|
||||
from authentik.events.utils import sanitize_item
|
||||
from authentik.lib.sync.outgoing import HTTP_CONFLICT
|
||||
from authentik.lib.sync.outgoing.base import BaseOutgoingSyncClient
|
||||
from authentik.lib.sync.outgoing.exceptions import (
|
||||
@ -106,4 +107,4 @@ class MicrosoftEntraSyncClient[TModel: Model, TConnection: Model, TSchema: dict]
|
||||
we can't JSON serialize"""
|
||||
raw_data = asdict(entity)
|
||||
raw_data.pop("backing_store", None)
|
||||
return raw_data
|
||||
return sanitize_item(raw_data)
|
||||
|
@ -1,3 +1,4 @@
|
||||
from deepmerge import always_merger
|
||||
from django.db import transaction
|
||||
from msgraph.generated.groups.groups_request_builder import GroupsRequestBuilder
|
||||
from msgraph.generated.models.group import Group as MSGroup
|
||||
@ -104,9 +105,12 @@ class MicrosoftEntraGroupClient(
|
||||
microsoft_group = self.to_schema(group, connection)
|
||||
microsoft_group.id = connection.microsoft_id
|
||||
try:
|
||||
return self._request(
|
||||
response = self._request(
|
||||
self.client.groups.by_group_id(connection.microsoft_id).patch(microsoft_group)
|
||||
)
|
||||
if response:
|
||||
always_merger.merge(connection.attributes, self.entity_as_dict(response))
|
||||
connection.save()
|
||||
except NotFoundSyncException:
|
||||
# Resource missing is handled by self.write, which will re-create the group
|
||||
raise
|
||||
@ -222,3 +226,7 @@ class MicrosoftEntraGroupClient(
|
||||
microsoft_id=group.id,
|
||||
attributes=self.entity_as_dict(group),
|
||||
)
|
||||
|
||||
def update_single_attribute(self, connection: MicrosoftEntraProviderGroup):
|
||||
data = self._request(self.client.groups.by_group_id(connection.microsoft_id).get())
|
||||
connection.attributes = self.entity_as_dict(data)
|
||||
|
@ -1,3 +1,4 @@
|
||||
from deepmerge import always_merger
|
||||
from django.db import transaction
|
||||
from msgraph.generated.models.user import User as MSUser
|
||||
from msgraph.generated.users.users_request_builder import UsersRequestBuilder
|
||||
@ -65,6 +66,26 @@ class MicrosoftEntraUserClient(MicrosoftEntraSyncClient[User, MicrosoftEntraProv
|
||||
microsoft_user.delete()
|
||||
return response
|
||||
|
||||
def get_select_fields(self) -> list[str]:
|
||||
"""All fields that should be selected when we fetch user data."""
|
||||
# TODO: Make this customizable in the future
|
||||
return [
|
||||
# Default fields
|
||||
"businessPhones",
|
||||
"displayName",
|
||||
"givenName",
|
||||
"jobTitle",
|
||||
"mail",
|
||||
"mobilePhone",
|
||||
"officeLocation",
|
||||
"preferredLanguage",
|
||||
"surname",
|
||||
"userPrincipalName",
|
||||
"id",
|
||||
# Required for logging into M365 using authentik
|
||||
"onPremisesImmutableId",
|
||||
]
|
||||
|
||||
def create(self, user: User):
|
||||
"""Create user from scratch and create a connection object"""
|
||||
microsoft_user = self.to_schema(user, None)
|
||||
@ -74,12 +95,12 @@ class MicrosoftEntraUserClient(MicrosoftEntraSyncClient[User, MicrosoftEntraProv
|
||||
response = self._request(self.client.users.post(microsoft_user))
|
||||
except ObjectExistsSyncException:
|
||||
# user already exists in microsoft entra, so we can connect them manually
|
||||
query_params = UsersRequestBuilder.UsersRequestBuilderGetQueryParameters()(
|
||||
filter=f"mail eq '{microsoft_user.mail}'",
|
||||
)
|
||||
request_configuration = (
|
||||
UsersRequestBuilder.UsersRequestBuilderGetRequestConfiguration(
|
||||
query_parameters=query_params,
|
||||
query_parameters=UsersRequestBuilder.UsersRequestBuilderGetQueryParameters(
|
||||
filter=f"mail eq '{microsoft_user.mail}'",
|
||||
select=self.get_select_fields(),
|
||||
),
|
||||
)
|
||||
)
|
||||
user_data = self._request(self.client.users.get(request_configuration))
|
||||
@ -98,7 +119,6 @@ class MicrosoftEntraUserClient(MicrosoftEntraSyncClient[User, MicrosoftEntraProv
|
||||
except TransientSyncException as exc:
|
||||
raise exc
|
||||
else:
|
||||
print(self.entity_as_dict(response))
|
||||
return MicrosoftEntraProviderUser.objects.create(
|
||||
provider=self.provider,
|
||||
user=user,
|
||||
@ -110,11 +130,21 @@ class MicrosoftEntraUserClient(MicrosoftEntraSyncClient[User, MicrosoftEntraProv
|
||||
"""Update existing user"""
|
||||
microsoft_user = self.to_schema(user, connection)
|
||||
self.check_email_valid(microsoft_user.user_principal_name)
|
||||
self._request(self.client.users.by_user_id(connection.microsoft_id).patch(microsoft_user))
|
||||
response = self._request(
|
||||
self.client.users.by_user_id(connection.microsoft_id).patch(microsoft_user)
|
||||
)
|
||||
if response:
|
||||
always_merger.merge(connection.attributes, self.entity_as_dict(response))
|
||||
connection.save()
|
||||
|
||||
def discover(self):
|
||||
"""Iterate through all users and connect them with authentik users if possible"""
|
||||
users = self._request(self.client.users.get())
|
||||
request_configuration = UsersRequestBuilder.UsersRequestBuilderGetRequestConfiguration(
|
||||
query_parameters=UsersRequestBuilder.UsersRequestBuilderGetQueryParameters(
|
||||
select=self.get_select_fields(),
|
||||
),
|
||||
)
|
||||
users = self._request(self.client.users.get(request_configuration))
|
||||
next_link = True
|
||||
while next_link:
|
||||
for user in users.value:
|
||||
@ -135,3 +165,14 @@ class MicrosoftEntraUserClient(MicrosoftEntraSyncClient[User, MicrosoftEntraProv
|
||||
microsoft_id=user.id,
|
||||
attributes=self.entity_as_dict(user),
|
||||
)
|
||||
|
||||
def update_single_attribute(self, connection: MicrosoftEntraProviderUser):
|
||||
request_configuration = UsersRequestBuilder.UsersRequestBuilderGetRequestConfiguration(
|
||||
query_parameters=UsersRequestBuilder.UsersRequestBuilderGetQueryParameters(
|
||||
select=self.get_select_fields(),
|
||||
),
|
||||
)
|
||||
data = self._request(
|
||||
self.client.users.by_user_id(connection.microsoft_id).get(request_configuration)
|
||||
)
|
||||
connection.attributes = self.entity_as_dict(data)
|
||||
|
@ -22,6 +22,58 @@ from authentik.lib.sync.outgoing.base import BaseOutgoingSyncClient
|
||||
from authentik.lib.sync.outgoing.models import OutgoingSyncDeleteAction, OutgoingSyncProvider
|
||||
|
||||
|
||||
class MicrosoftEntraProviderUser(SerializerModel):
|
||||
"""Mapping of a user and provider to a Microsoft user ID"""
|
||||
|
||||
id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
|
||||
microsoft_id = models.TextField()
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
provider = models.ForeignKey("MicrosoftEntraProvider", on_delete=models.CASCADE)
|
||||
attributes = models.JSONField(default=dict)
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
from authentik.enterprise.providers.microsoft_entra.api.users import (
|
||||
MicrosoftEntraProviderUserSerializer,
|
||||
)
|
||||
|
||||
return MicrosoftEntraProviderUserSerializer
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Microsoft Entra Provider User")
|
||||
verbose_name_plural = _("Microsoft Entra Provider User")
|
||||
unique_together = (("microsoft_id", "user", "provider"),)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Microsoft Entra Provider User {self.user_id} to {self.provider_id}"
|
||||
|
||||
|
||||
class MicrosoftEntraProviderGroup(SerializerModel):
|
||||
"""Mapping of a group and provider to a Microsoft group ID"""
|
||||
|
||||
id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
|
||||
microsoft_id = models.TextField()
|
||||
group = models.ForeignKey(Group, on_delete=models.CASCADE)
|
||||
provider = models.ForeignKey("MicrosoftEntraProvider", on_delete=models.CASCADE)
|
||||
attributes = models.JSONField(default=dict)
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
from authentik.enterprise.providers.microsoft_entra.api.groups import (
|
||||
MicrosoftEntraProviderGroupSerializer,
|
||||
)
|
||||
|
||||
return MicrosoftEntraProviderGroupSerializer
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Microsoft Entra Provider Group")
|
||||
verbose_name_plural = _("Microsoft Entra Provider Groups")
|
||||
unique_together = (("microsoft_id", "group", "provider"),)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Microsoft Entra Provider Group {self.group_id} to {self.provider_id}"
|
||||
|
||||
|
||||
class MicrosoftEntraProvider(OutgoingSyncProvider, BackchannelProvider):
|
||||
"""Sync users from authentik into Microsoft Entra."""
|
||||
|
||||
@ -48,15 +100,16 @@ class MicrosoftEntraProvider(OutgoingSyncProvider, BackchannelProvider):
|
||||
)
|
||||
|
||||
def client_for_model(
|
||||
self, model: type[User | Group]
|
||||
self,
|
||||
model: type[User | Group | MicrosoftEntraProviderUser | MicrosoftEntraProviderGroup],
|
||||
) -> BaseOutgoingSyncClient[User | Group, Any, Any, Self]:
|
||||
if issubclass(model, User):
|
||||
if issubclass(model, User | MicrosoftEntraProviderUser):
|
||||
from authentik.enterprise.providers.microsoft_entra.clients.users import (
|
||||
MicrosoftEntraUserClient,
|
||||
)
|
||||
|
||||
return MicrosoftEntraUserClient(self)
|
||||
if issubclass(model, Group):
|
||||
if issubclass(model, Group | MicrosoftEntraProviderGroup):
|
||||
from authentik.enterprise.providers.microsoft_entra.clients.groups import (
|
||||
MicrosoftEntraGroupClient,
|
||||
)
|
||||
@ -133,55 +186,3 @@ class MicrosoftEntraProviderMapping(PropertyMapping):
|
||||
class Meta:
|
||||
verbose_name = _("Microsoft Entra Provider Mapping")
|
||||
verbose_name_plural = _("Microsoft Entra Provider Mappings")
|
||||
|
||||
|
||||
class MicrosoftEntraProviderUser(SerializerModel):
|
||||
"""Mapping of a user and provider to a Microsoft user ID"""
|
||||
|
||||
id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
|
||||
microsoft_id = models.TextField()
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
provider = models.ForeignKey(MicrosoftEntraProvider, on_delete=models.CASCADE)
|
||||
attributes = models.JSONField(default=dict)
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
from authentik.enterprise.providers.microsoft_entra.api.users import (
|
||||
MicrosoftEntraProviderUserSerializer,
|
||||
)
|
||||
|
||||
return MicrosoftEntraProviderUserSerializer
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Microsoft Entra Provider User")
|
||||
verbose_name_plural = _("Microsoft Entra Provider User")
|
||||
unique_together = (("microsoft_id", "user", "provider"),)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Microsoft Entra Provider User {self.user_id} to {self.provider_id}"
|
||||
|
||||
|
||||
class MicrosoftEntraProviderGroup(SerializerModel):
|
||||
"""Mapping of a group and provider to a Microsoft group ID"""
|
||||
|
||||
id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
|
||||
microsoft_id = models.TextField()
|
||||
group = models.ForeignKey(Group, on_delete=models.CASCADE)
|
||||
provider = models.ForeignKey(MicrosoftEntraProvider, on_delete=models.CASCADE)
|
||||
attributes = models.JSONField(default=dict)
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
from authentik.enterprise.providers.microsoft_entra.api.groups import (
|
||||
MicrosoftEntraProviderGroupSerializer,
|
||||
)
|
||||
|
||||
return MicrosoftEntraProviderGroupSerializer
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Microsoft Entra Provider Group")
|
||||
verbose_name_plural = _("Microsoft Entra Provider Groups")
|
||||
unique_together = (("microsoft_id", "group", "provider"),)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Microsoft Entra Provider Group {self.group_id} to {self.provider_id}"
|
||||
|
@ -3,16 +3,18 @@
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
from azure.identity.aio import ClientSecretCredential
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from msgraph.generated.models.group_collection_response import GroupCollectionResponse
|
||||
from msgraph.generated.models.organization import Organization
|
||||
from msgraph.generated.models.organization_collection_response import OrganizationCollectionResponse
|
||||
from msgraph.generated.models.user import User as MSUser
|
||||
from msgraph.generated.models.user_collection_response import UserCollectionResponse
|
||||
from msgraph.generated.models.verified_domain import VerifiedDomain
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
from authentik.blueprints.tests import apply_blueprint
|
||||
from authentik.core.models import Application, Group, User
|
||||
from authentik.core.tests.utils import create_test_admin_user
|
||||
from authentik.enterprise.providers.microsoft_entra.models import (
|
||||
MicrosoftEntraProvider,
|
||||
MicrosoftEntraProviderMapping,
|
||||
@ -25,11 +27,12 @@ from authentik.lib.sync.outgoing.models import OutgoingSyncDeleteAction
|
||||
from authentik.tenants.models import Tenant
|
||||
|
||||
|
||||
class MicrosoftEntraUserTests(TestCase):
|
||||
class MicrosoftEntraUserTests(APITestCase):
|
||||
"""Microsoft Entra User tests"""
|
||||
|
||||
@apply_blueprint("system/providers-microsoft-entra.yaml")
|
||||
def setUp(self) -> None:
|
||||
|
||||
# Delete all users and groups as the mocked HTTP responses only return one ID
|
||||
# which will cause errors with multiple users
|
||||
Tenant.objects.update(avatars="none")
|
||||
@ -371,3 +374,45 @@ class MicrosoftEntraUserTests(TestCase):
|
||||
)
|
||||
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
|
||||
user_list.assert_called_once()
|
||||
|
||||
def test_connect_manual(self):
|
||||
"""test manual user connection"""
|
||||
uid = generate_id()
|
||||
self.app.backchannel_providers.remove(self.provider)
|
||||
admin = create_test_admin_user()
|
||||
different_user = User.objects.create(
|
||||
username=uid,
|
||||
email=f"{uid}@goauthentik.io",
|
||||
)
|
||||
self.app.backchannel_providers.add(self.provider)
|
||||
with (
|
||||
patch(
|
||||
"authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials",
|
||||
MagicMock(return_value={"credentials": self.creds}),
|
||||
),
|
||||
patch(
|
||||
"msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get",
|
||||
AsyncMock(
|
||||
return_value=OrganizationCollectionResponse(
|
||||
value=[
|
||||
Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")])
|
||||
]
|
||||
)
|
||||
),
|
||||
),
|
||||
patch(
|
||||
"authentik.enterprise.providers.microsoft_entra.clients.users.MicrosoftEntraUserClient.update_single_attribute",
|
||||
MagicMock(),
|
||||
) as user_get,
|
||||
):
|
||||
self.client.force_login(admin)
|
||||
response = self.client.post(
|
||||
reverse("authentik_api:microsoftentraprovideruser-list"),
|
||||
data={
|
||||
"microsoft_id": generate_id(),
|
||||
"user": different_user.pk,
|
||||
"provider": self.provider.pk,
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, 201)
|
||||
user_get.assert_called_once()
|
||||
|
@ -50,7 +50,6 @@ cache:
|
||||
timeout: 300
|
||||
timeout_flows: 300
|
||||
timeout_policies: 300
|
||||
timeout_reputation: 300
|
||||
|
||||
# channel:
|
||||
# url: ""
|
||||
@ -116,6 +115,9 @@ events:
|
||||
context_processors:
|
||||
geoip: "/geoip/GeoLite2-City.mmdb"
|
||||
asn: "/geoip/GeoLite2-ASN.mmdb"
|
||||
compliance:
|
||||
fips:
|
||||
enabled: false
|
||||
|
||||
cert_discovery_dir: /certs
|
||||
|
||||
|
@ -102,6 +102,8 @@ def get_logger_config():
|
||||
"gunicorn": "INFO",
|
||||
"requests_mock": "WARNING",
|
||||
"hpack": "WARNING",
|
||||
"httpx": "WARNING",
|
||||
"azure": "WARNING",
|
||||
}
|
||||
for handler_name, level in handler_level_map.items():
|
||||
base_config["loggers"][handler_name] = {
|
||||
|
@ -57,6 +57,8 @@ class PropertyMappingManager:
|
||||
mapping.set_context(user, request, **kwargs)
|
||||
try:
|
||||
value = mapping.evaluate(mapping.model.expression)
|
||||
except PropertyMappingExpressionException as exc:
|
||||
raise exc from exc
|
||||
except Exception as exc:
|
||||
raise PropertyMappingExpressionException(exc, mapping.model) from exc
|
||||
if value is None:
|
||||
|
@ -7,6 +7,7 @@ from rest_framework.decorators import action
|
||||
from rest_framework.fields import BooleanField
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
|
||||
from authentik.core.api.utils import PassiveSerializer
|
||||
from authentik.events.api.tasks import SystemTaskSerializer
|
||||
@ -54,3 +55,17 @@ class OutgoingSyncProviderStatusMixin:
|
||||
"is_running": not lock_acquired,
|
||||
}
|
||||
return Response(SyncStatusSerializer(status).data)
|
||||
|
||||
|
||||
class OutgoingSyncConnectionCreateMixin:
|
||||
"""Mixin for connection objects that fetches remote data upon creation"""
|
||||
|
||||
def perform_create(self, serializer: ModelSerializer):
|
||||
super().perform_create(serializer)
|
||||
try:
|
||||
instance = serializer.instance
|
||||
client = instance.provider.client_for_model(instance.__class__)
|
||||
client.update_single_attribute(instance)
|
||||
instance.save()
|
||||
except NotImplementedError:
|
||||
pass
|
||||
|
@ -91,10 +91,9 @@ class BaseOutgoingSyncClient[
|
||||
}
|
||||
eval_kwargs.setdefault("user", None)
|
||||
for value in self.mapper.iter_eval(**eval_kwargs):
|
||||
try:
|
||||
always_merger.merge(raw_final_object, value)
|
||||
except SkipObjectException as exc:
|
||||
raise exc from exc
|
||||
always_merger.merge(raw_final_object, value)
|
||||
except SkipObjectException as exc:
|
||||
raise exc from exc
|
||||
except PropertyMappingExpressionException as exc:
|
||||
# Value error can be raised when assigning invalid data to an attribute
|
||||
Event.new(
|
||||
@ -104,7 +103,7 @@ class BaseOutgoingSyncClient[
|
||||
).save()
|
||||
raise StopSync(exc, obj, exc.mapping) from exc
|
||||
if not raw_final_object:
|
||||
raise StopSync(ValueError("No user mappings configured"), obj)
|
||||
raise StopSync(ValueError("No mappings configured"), obj)
|
||||
for key, value in defaults.items():
|
||||
raw_final_object.setdefault(key, value)
|
||||
return raw_final_object
|
||||
@ -115,3 +114,8 @@ class BaseOutgoingSyncClient[
|
||||
pre-link any users/groups in the remote system with the respective
|
||||
object in authentik based on a common identifier"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def update_single_attribute(self, connection: TConnection):
|
||||
"""Update connection attributes on a connection object, when the connection
|
||||
is manually created"""
|
||||
raise NotImplementedError
|
||||
|
@ -14,6 +14,7 @@ from authentik.core.models import Group, User
|
||||
from authentik.events.logs import LogEvent
|
||||
from authentik.events.models import TaskStatus
|
||||
from authentik.events.system_tasks import SystemTask
|
||||
from authentik.events.utils import sanitize_item
|
||||
from authentik.lib.sync.outgoing import PAGE_SIZE, PAGE_TIMEOUT
|
||||
from authentik.lib.sync.outgoing.base import Direction
|
||||
from authentik.lib.sync.outgoing.exceptions import (
|
||||
@ -125,6 +126,7 @@ class SyncTasks:
|
||||
try:
|
||||
client.write(obj)
|
||||
except SkipObjectException:
|
||||
self.logger.debug("skipping object due to SkipObject", obj=obj)
|
||||
continue
|
||||
except BadRequestSyncException as exc:
|
||||
self.logger.warning("failed to sync object", exc=exc, obj=obj)
|
||||
@ -144,8 +146,8 @@ class SyncTasks:
|
||||
)
|
||||
),
|
||||
log_level="warning",
|
||||
logger="",
|
||||
attributes={"arguments": exc.args[1:]},
|
||||
logger=f"{provider._meta.verbose_name}@{object_type}",
|
||||
attributes={"arguments": exc.args[1:], "obj": sanitize_item(obj)},
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -167,7 +169,8 @@ class SyncTasks:
|
||||
)
|
||||
),
|
||||
log_level="warning",
|
||||
logger="",
|
||||
logger=f"{provider._meta.verbose_name}@{object_type}",
|
||||
attributes={"obj": sanitize_item(obj)},
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -184,7 +187,8 @@ class SyncTasks:
|
||||
)
|
||||
),
|
||||
log_level="warning",
|
||||
logger="",
|
||||
logger=f"{provider._meta.verbose_name}@{object_type}",
|
||||
attributes={"obj": sanitize_item(obj)},
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -6,7 +6,7 @@ from django_filters.filters import ModelMultipleChoiceFilter
|
||||
from django_filters.filterset import FilterSet
|
||||
from drf_spectacular.utils import extend_schema
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.fields import BooleanField, CharField, DateTimeField
|
||||
from rest_framework.fields import BooleanField, CharField, DateTimeField, SerializerMethodField
|
||||
from rest_framework.relations import PrimaryKeyRelatedField
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
@ -18,6 +18,7 @@ from authentik.core.api.providers import ProviderSerializer
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.core.api.utils import JSONDictField, PassiveSerializer
|
||||
from authentik.core.models import Provider
|
||||
from authentik.enterprise.license import LicenseKey
|
||||
from authentik.enterprise.providers.rac.models import RACProvider
|
||||
from authentik.outposts.api.service_connections import ServiceConnectionSerializer
|
||||
from authentik.outposts.apps import MANAGED_OUTPOST, MANAGED_OUTPOST_NAME
|
||||
@ -120,7 +121,7 @@ class OutpostHealthSerializer(PassiveSerializer):
|
||||
golang_version = CharField(read_only=True)
|
||||
openssl_enabled = BooleanField(read_only=True)
|
||||
openssl_version = CharField(read_only=True)
|
||||
fips_enabled = BooleanField(read_only=True)
|
||||
fips_enabled = SerializerMethodField()
|
||||
|
||||
version_should = CharField(read_only=True)
|
||||
version_outdated = BooleanField(read_only=True)
|
||||
@ -130,6 +131,12 @@ class OutpostHealthSerializer(PassiveSerializer):
|
||||
|
||||
hostname = CharField(read_only=True, required=False)
|
||||
|
||||
def get_fips_enabled(self, obj: dict) -> bool | None:
|
||||
"""Get FIPS enabled"""
|
||||
if not LicenseKey.get_total().is_valid():
|
||||
return None
|
||||
return obj["fips_enabled"]
|
||||
|
||||
|
||||
class OutpostFilter(FilterSet):
|
||||
"""Filter for Outposts"""
|
||||
|
@ -102,7 +102,7 @@ class EventMatcherPolicy(Policy):
|
||||
result = checker(request, event)
|
||||
if result is None:
|
||||
continue
|
||||
LOGGER.info(
|
||||
LOGGER.debug(
|
||||
"Event matcher check result",
|
||||
checker=checker.__name__,
|
||||
result=result,
|
||||
|
@ -2,8 +2,6 @@
|
||||
|
||||
from authentik.blueprints.apps import ManagedAppConfig
|
||||
|
||||
CACHE_KEY_PREFIX = "goauthentik.io/policies/reputation/scores/"
|
||||
|
||||
|
||||
class AuthentikPolicyReputationConfig(ManagedAppConfig):
|
||||
"""Authentik reputation app config"""
|
||||
|
@ -0,0 +1,25 @@
|
||||
# Generated by Django 5.0.6 on 2024-06-11 08:50
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_policies_reputation", "0006_reputation_ip_asn_data"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddIndex(
|
||||
model_name="reputation",
|
||||
index=models.Index(fields=["identifier"], name="authentik_p_identif_9434d7_idx"),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="reputation",
|
||||
index=models.Index(fields=["ip"], name="authentik_p_ip_7ad0df_idx"),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="reputation",
|
||||
index=models.Index(fields=["ip", "identifier"], name="authentik_p_ip_d779aa_idx"),
|
||||
),
|
||||
]
|
@ -96,3 +96,8 @@ class Reputation(ExpiringModel, SerializerModel):
|
||||
verbose_name = _("Reputation Score")
|
||||
verbose_name_plural = _("Reputation Scores")
|
||||
unique_together = ("identifier", "ip")
|
||||
indexes = [
|
||||
models.Index(fields=["identifier"]),
|
||||
models.Index(fields=["ip"]),
|
||||
models.Index(fields=["ip", "identifier"]),
|
||||
]
|
||||
|
@ -1,11 +0,0 @@
|
||||
"""Reputation Settings"""
|
||||
|
||||
from celery.schedules import crontab
|
||||
|
||||
CELERY_BEAT_SCHEDULE = {
|
||||
"policies_reputation_save": {
|
||||
"task": "authentik.policies.reputation.tasks.save_reputation",
|
||||
"schedule": crontab(minute="1-59/5"),
|
||||
"options": {"queue": "authentik_scheduled"},
|
||||
},
|
||||
}
|
@ -1,40 +1,35 @@
|
||||
"""authentik reputation request signals"""
|
||||
|
||||
from django.contrib.auth.signals import user_logged_in
|
||||
from django.core.cache import cache
|
||||
from django.dispatch import receiver
|
||||
from django.http import HttpRequest
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.core.signals import login_failed
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.policies.reputation.apps import CACHE_KEY_PREFIX
|
||||
from authentik.policies.reputation.tasks import save_reputation
|
||||
from authentik.events.context_processors.asn import ASN_CONTEXT_PROCESSOR
|
||||
from authentik.events.context_processors.geoip import GEOIP_CONTEXT_PROCESSOR
|
||||
from authentik.policies.reputation.models import Reputation, reputation_expiry
|
||||
from authentik.root.middleware import ClientIPMiddleware
|
||||
from authentik.stages.identification.signals import identification_failed
|
||||
|
||||
LOGGER = get_logger()
|
||||
CACHE_TIMEOUT = CONFIG.get_int("cache.timeout_reputation")
|
||||
|
||||
|
||||
def update_score(request: HttpRequest, identifier: str, amount: int):
|
||||
"""Update score for IP and User"""
|
||||
remote_ip = ClientIPMiddleware.get_client_ip(request)
|
||||
|
||||
try:
|
||||
# We only update the cache here, as its faster than writing to the DB
|
||||
score = cache.get_or_set(
|
||||
CACHE_KEY_PREFIX + remote_ip + "/" + identifier,
|
||||
{"ip": remote_ip, "identifier": identifier, "score": 0},
|
||||
CACHE_TIMEOUT,
|
||||
)
|
||||
score["score"] += amount
|
||||
cache.set(CACHE_KEY_PREFIX + remote_ip + "/" + identifier, score)
|
||||
except ValueError as exc:
|
||||
LOGGER.warning("failed to set reputation", exc=exc)
|
||||
|
||||
Reputation.objects.update_or_create(
|
||||
ip=remote_ip,
|
||||
identifier=identifier,
|
||||
defaults={
|
||||
"score": amount,
|
||||
"ip_geo_data": GEOIP_CONTEXT_PROCESSOR.city_dict(remote_ip) or {},
|
||||
"ip_asn_data": ASN_CONTEXT_PROCESSOR.asn_dict(remote_ip) or {},
|
||||
"expires": reputation_expiry(),
|
||||
},
|
||||
)
|
||||
LOGGER.debug("Updated score", amount=amount, for_user=identifier, for_ip=remote_ip)
|
||||
save_reputation.delay()
|
||||
|
||||
|
||||
@receiver(login_failed)
|
||||
|
@ -1,32 +0,0 @@
|
||||
"""Reputation tasks"""
|
||||
|
||||
from django.core.cache import cache
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.events.context_processors.asn import ASN_CONTEXT_PROCESSOR
|
||||
from authentik.events.context_processors.geoip import GEOIP_CONTEXT_PROCESSOR
|
||||
from authentik.events.models import TaskStatus
|
||||
from authentik.events.system_tasks import SystemTask, prefill_task
|
||||
from authentik.policies.reputation.apps import CACHE_KEY_PREFIX
|
||||
from authentik.policies.reputation.models import Reputation
|
||||
from authentik.root.celery import CELERY_APP
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
@CELERY_APP.task(bind=True, base=SystemTask)
|
||||
@prefill_task
|
||||
def save_reputation(self: SystemTask):
|
||||
"""Save currently cached reputation to database"""
|
||||
objects_to_update = []
|
||||
for _, score in cache.get_many(cache.keys(CACHE_KEY_PREFIX + "*")).items():
|
||||
rep, _ = Reputation.objects.get_or_create(
|
||||
ip=score["ip"],
|
||||
identifier=score["identifier"],
|
||||
)
|
||||
rep.ip_geo_data = GEOIP_CONTEXT_PROCESSOR.city_dict(score["ip"]) or {}
|
||||
rep.ip_asn_data = ASN_CONTEXT_PROCESSOR.asn_dict(score["ip"]) or {}
|
||||
rep.score = score["score"]
|
||||
objects_to_update.append(rep)
|
||||
Reputation.objects.bulk_update(objects_to_update, ["score", "ip_geo_data"])
|
||||
self.set_status(TaskStatus.SUCCESSFUL, "Successfully updated Reputation")
|
@ -1,14 +1,11 @@
|
||||
"""test reputation signals and policy"""
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.test import RequestFactory, TestCase
|
||||
|
||||
from authentik.core.models import User
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.policies.reputation.api import ReputationPolicySerializer
|
||||
from authentik.policies.reputation.apps import CACHE_KEY_PREFIX
|
||||
from authentik.policies.reputation.models import Reputation, ReputationPolicy
|
||||
from authentik.policies.reputation.tasks import save_reputation
|
||||
from authentik.policies.types import PolicyRequest
|
||||
from authentik.stages.password import BACKEND_INBUILT
|
||||
from authentik.stages.password.stage import authenticate
|
||||
@ -22,8 +19,6 @@ class TestReputationPolicy(TestCase):
|
||||
self.request = self.request_factory.get("/")
|
||||
self.test_ip = "127.0.0.1"
|
||||
self.test_username = "test"
|
||||
keys = cache.keys(CACHE_KEY_PREFIX + "*")
|
||||
cache.delete_many(keys)
|
||||
# We need a user for the one-to-one in userreputation
|
||||
self.user = User.objects.create(username=self.test_username)
|
||||
self.backends = [BACKEND_INBUILT]
|
||||
@ -34,13 +29,6 @@ class TestReputationPolicy(TestCase):
|
||||
authenticate(
|
||||
self.request, self.backends, username=self.test_username, password=self.test_username
|
||||
)
|
||||
# Test value in cache
|
||||
self.assertEqual(
|
||||
cache.get(CACHE_KEY_PREFIX + self.test_ip + "/" + self.test_username),
|
||||
{"ip": "127.0.0.1", "identifier": "test", "score": -1},
|
||||
)
|
||||
# Save cache and check db values
|
||||
save_reputation.delay().get()
|
||||
self.assertEqual(Reputation.objects.get(ip=self.test_ip).score, -1)
|
||||
|
||||
def test_user_reputation(self):
|
||||
@ -49,13 +37,6 @@ class TestReputationPolicy(TestCase):
|
||||
authenticate(
|
||||
self.request, self.backends, username=self.test_username, password=self.test_username
|
||||
)
|
||||
# Test value in cache
|
||||
self.assertEqual(
|
||||
cache.get(CACHE_KEY_PREFIX + self.test_ip + "/" + self.test_username),
|
||||
{"ip": "127.0.0.1", "identifier": "test", "score": -1},
|
||||
)
|
||||
# Save cache and check db values
|
||||
save_reputation.delay().get()
|
||||
self.assertEqual(Reputation.objects.get(identifier=self.test_username).score, -1)
|
||||
|
||||
def test_policy(self):
|
||||
|
45
authentik/providers/scim/api/groups.py
Normal file
45
authentik/providers/scim/api/groups.py
Normal file
@ -0,0 +1,45 @@
|
||||
"""SCIMProviderGroup API Views"""
|
||||
|
||||
from rest_framework import mixins
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
from rest_framework.viewsets import GenericViewSet
|
||||
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.core.api.users import UserGroupSerializer
|
||||
from authentik.lib.sync.outgoing.api import OutgoingSyncConnectionCreateMixin
|
||||
from authentik.providers.scim.models import SCIMProviderGroup
|
||||
|
||||
|
||||
class SCIMProviderGroupSerializer(ModelSerializer):
|
||||
"""SCIMProviderGroup Serializer"""
|
||||
|
||||
group_obj = UserGroupSerializer(source="group", read_only=True)
|
||||
|
||||
class Meta:
|
||||
|
||||
model = SCIMProviderGroup
|
||||
fields = [
|
||||
"id",
|
||||
"scim_id",
|
||||
"group",
|
||||
"group_obj",
|
||||
"provider",
|
||||
]
|
||||
|
||||
|
||||
class SCIMProviderGroupViewSet(
|
||||
mixins.CreateModelMixin,
|
||||
OutgoingSyncConnectionCreateMixin,
|
||||
mixins.RetrieveModelMixin,
|
||||
mixins.DestroyModelMixin,
|
||||
UsedByMixin,
|
||||
mixins.ListModelMixin,
|
||||
GenericViewSet,
|
||||
):
|
||||
"""SCIMProviderGroup Viewset"""
|
||||
|
||||
queryset = SCIMProviderGroup.objects.all().select_related("group")
|
||||
serializer_class = SCIMProviderGroupSerializer
|
||||
filterset_fields = ["provider__id", "group__name", "group__group_uuid"]
|
||||
search_fields = ["provider__name", "group__name"]
|
||||
ordering = ["group__name"]
|
45
authentik/providers/scim/api/users.py
Normal file
45
authentik/providers/scim/api/users.py
Normal file
@ -0,0 +1,45 @@
|
||||
"""SCIMProviderUser API Views"""
|
||||
|
||||
from rest_framework import mixins
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
from rest_framework.viewsets import GenericViewSet
|
||||
|
||||
from authentik.core.api.groups import GroupMemberSerializer
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.lib.sync.outgoing.api import OutgoingSyncConnectionCreateMixin
|
||||
from authentik.providers.scim.models import SCIMProviderUser
|
||||
|
||||
|
||||
class SCIMProviderUserSerializer(ModelSerializer):
|
||||
"""SCIMProviderUser Serializer"""
|
||||
|
||||
user_obj = GroupMemberSerializer(source="user", read_only=True)
|
||||
|
||||
class Meta:
|
||||
|
||||
model = SCIMProviderUser
|
||||
fields = [
|
||||
"id",
|
||||
"scim_id",
|
||||
"user",
|
||||
"user_obj",
|
||||
"provider",
|
||||
]
|
||||
|
||||
|
||||
class SCIMProviderUserViewSet(
|
||||
mixins.CreateModelMixin,
|
||||
OutgoingSyncConnectionCreateMixin,
|
||||
mixins.RetrieveModelMixin,
|
||||
mixins.DestroyModelMixin,
|
||||
UsedByMixin,
|
||||
mixins.ListModelMixin,
|
||||
GenericViewSet,
|
||||
):
|
||||
"""SCIMProviderUser Viewset"""
|
||||
|
||||
queryset = SCIMProviderUser.objects.all().select_related("user")
|
||||
serializer_class = SCIMProviderUserSerializer
|
||||
filterset_fields = ["provider__id", "user__username", "user__id"]
|
||||
search_fields = ["provider__name", "user__username"]
|
||||
ordering = ["user__username"]
|
@ -19,13 +19,18 @@ from authentik.providers.scim.clients.exceptions import (
|
||||
)
|
||||
from authentik.providers.scim.clients.schema import SCIM_GROUP_SCHEMA, PatchRequest
|
||||
from authentik.providers.scim.clients.schema import Group as SCIMGroupSchema
|
||||
from authentik.providers.scim.models import SCIMGroup, SCIMMapping, SCIMProvider, SCIMUser
|
||||
from authentik.providers.scim.models import (
|
||||
SCIMMapping,
|
||||
SCIMProvider,
|
||||
SCIMProviderGroup,
|
||||
SCIMProviderUser,
|
||||
)
|
||||
|
||||
|
||||
class SCIMGroupClient(SCIMClient[Group, SCIMGroup, SCIMGroupSchema]):
|
||||
class SCIMGroupClient(SCIMClient[Group, SCIMProviderGroup, SCIMGroupSchema]):
|
||||
"""SCIM client for groups"""
|
||||
|
||||
connection_type = SCIMGroup
|
||||
connection_type = SCIMProviderGroup
|
||||
connection_type_query = "group"
|
||||
mapper: PropertyMappingManager
|
||||
|
||||
@ -37,7 +42,7 @@ class SCIMGroupClient(SCIMClient[Group, SCIMGroup, SCIMGroupSchema]):
|
||||
["group", "provider", "connection"],
|
||||
)
|
||||
|
||||
def to_schema(self, obj: Group, connection: SCIMGroup) -> SCIMGroupSchema:
|
||||
def to_schema(self, obj: Group, connection: SCIMProviderGroup) -> SCIMGroupSchema:
|
||||
"""Convert authentik user into SCIM"""
|
||||
raw_scim_group = super().to_schema(
|
||||
obj,
|
||||
@ -52,7 +57,7 @@ class SCIMGroupClient(SCIMClient[Group, SCIMGroup, SCIMGroupSchema]):
|
||||
scim_group.externalId = str(obj.pk)
|
||||
|
||||
users = list(obj.users.order_by("id").values_list("id", flat=True))
|
||||
connections = SCIMUser.objects.filter(provider=self.provider, user__pk__in=users)
|
||||
connections = SCIMProviderUser.objects.filter(provider=self.provider, user__pk__in=users)
|
||||
members = []
|
||||
for user in connections:
|
||||
members.append(
|
||||
@ -66,7 +71,7 @@ class SCIMGroupClient(SCIMClient[Group, SCIMGroup, SCIMGroupSchema]):
|
||||
|
||||
def delete(self, obj: Group):
|
||||
"""Delete group"""
|
||||
scim_group = SCIMGroup.objects.filter(provider=self.provider, group=obj).first()
|
||||
scim_group = SCIMProviderGroup.objects.filter(provider=self.provider, group=obj).first()
|
||||
if not scim_group:
|
||||
self.logger.debug("Group does not exist in SCIM, skipping")
|
||||
return None
|
||||
@ -88,9 +93,11 @@ class SCIMGroupClient(SCIMClient[Group, SCIMGroup, SCIMGroupSchema]):
|
||||
scim_id = response.get("id")
|
||||
if not scim_id or scim_id == "":
|
||||
raise StopSync("SCIM Response with missing or invalid `id`")
|
||||
return SCIMGroup.objects.create(provider=self.provider, group=group, scim_id=scim_id)
|
||||
return SCIMProviderGroup.objects.create(
|
||||
provider=self.provider, group=group, scim_id=scim_id
|
||||
)
|
||||
|
||||
def update(self, group: Group, connection: SCIMGroup):
|
||||
def update(self, group: Group, connection: SCIMProviderGroup):
|
||||
"""Update existing group"""
|
||||
scim_group = self.to_schema(group, connection)
|
||||
scim_group.id = connection.scim_id
|
||||
@ -158,16 +165,16 @@ class SCIMGroupClient(SCIMClient[Group, SCIMGroup, SCIMGroupSchema]):
|
||||
"""Add users in users_set to group"""
|
||||
if len(users_set) < 1:
|
||||
return
|
||||
scim_group = SCIMGroup.objects.filter(provider=self.provider, group=group).first()
|
||||
scim_group = SCIMProviderGroup.objects.filter(provider=self.provider, group=group).first()
|
||||
if not scim_group:
|
||||
self.logger.warning(
|
||||
"could not sync group membership, group does not exist", group=group
|
||||
)
|
||||
return
|
||||
user_ids = list(
|
||||
SCIMUser.objects.filter(user__pk__in=users_set, provider=self.provider).values_list(
|
||||
"scim_id", flat=True
|
||||
)
|
||||
SCIMProviderUser.objects.filter(
|
||||
user__pk__in=users_set, provider=self.provider
|
||||
).values_list("scim_id", flat=True)
|
||||
)
|
||||
if len(user_ids) < 1:
|
||||
return
|
||||
@ -184,16 +191,16 @@ class SCIMGroupClient(SCIMClient[Group, SCIMGroup, SCIMGroupSchema]):
|
||||
"""Remove users in users_set from group"""
|
||||
if len(users_set) < 1:
|
||||
return
|
||||
scim_group = SCIMGroup.objects.filter(provider=self.provider, group=group).first()
|
||||
scim_group = SCIMProviderGroup.objects.filter(provider=self.provider, group=group).first()
|
||||
if not scim_group:
|
||||
self.logger.warning(
|
||||
"could not sync group membership, group does not exist", group=group
|
||||
)
|
||||
return
|
||||
user_ids = list(
|
||||
SCIMUser.objects.filter(user__pk__in=users_set, provider=self.provider).values_list(
|
||||
"scim_id", flat=True
|
||||
)
|
||||
SCIMProviderUser.objects.filter(
|
||||
user__pk__in=users_set, provider=self.provider
|
||||
).values_list("scim_id", flat=True)
|
||||
)
|
||||
if len(user_ids) < 1:
|
||||
return
|
||||
|
@ -9,13 +9,13 @@ from authentik.policies.utils import delete_none_values
|
||||
from authentik.providers.scim.clients.base import SCIMClient
|
||||
from authentik.providers.scim.clients.schema import SCIM_USER_SCHEMA
|
||||
from authentik.providers.scim.clients.schema import User as SCIMUserSchema
|
||||
from authentik.providers.scim.models import SCIMMapping, SCIMProvider, SCIMUser
|
||||
from authentik.providers.scim.models import SCIMMapping, SCIMProvider, SCIMProviderUser
|
||||
|
||||
|
||||
class SCIMUserClient(SCIMClient[User, SCIMUser, SCIMUserSchema]):
|
||||
class SCIMUserClient(SCIMClient[User, SCIMProviderUser, SCIMUserSchema]):
|
||||
"""SCIM client for users"""
|
||||
|
||||
connection_type = SCIMUser
|
||||
connection_type = SCIMProviderUser
|
||||
connection_type_query = "user"
|
||||
mapper: PropertyMappingManager
|
||||
|
||||
@ -27,7 +27,7 @@ class SCIMUserClient(SCIMClient[User, SCIMUser, SCIMUserSchema]):
|
||||
["provider", "connection"],
|
||||
)
|
||||
|
||||
def to_schema(self, obj: User, connection: SCIMUser) -> SCIMUserSchema:
|
||||
def to_schema(self, obj: User, connection: SCIMProviderUser) -> SCIMUserSchema:
|
||||
"""Convert authentik user into SCIM"""
|
||||
raw_scim_user = super().to_schema(
|
||||
obj,
|
||||
@ -44,7 +44,7 @@ class SCIMUserClient(SCIMClient[User, SCIMUser, SCIMUserSchema]):
|
||||
|
||||
def delete(self, obj: User):
|
||||
"""Delete user"""
|
||||
scim_user = SCIMUser.objects.filter(provider=self.provider, user=obj).first()
|
||||
scim_user = SCIMProviderUser.objects.filter(provider=self.provider, user=obj).first()
|
||||
if not scim_user:
|
||||
self.logger.debug("User does not exist in SCIM, skipping")
|
||||
return None
|
||||
@ -66,9 +66,9 @@ class SCIMUserClient(SCIMClient[User, SCIMUser, SCIMUserSchema]):
|
||||
scim_id = response.get("id")
|
||||
if not scim_id or scim_id == "":
|
||||
raise StopSync("SCIM Response with missing or invalid `id`")
|
||||
return SCIMUser.objects.create(provider=self.provider, user=user, scim_id=scim_id)
|
||||
return SCIMProviderUser.objects.create(provider=self.provider, user=user, scim_id=scim_id)
|
||||
|
||||
def update(self, user: User, connection: SCIMUser):
|
||||
def update(self, user: User, connection: SCIMProviderUser):
|
||||
"""Update existing user"""
|
||||
scim_user = self.to_schema(user, connection)
|
||||
scim_user.id = connection.scim_id
|
||||
|
@ -0,0 +1,24 @@
|
||||
# Generated by Django 5.0.6 on 2024-06-04 07:45
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_core", "0035_alter_group_options_and_more"),
|
||||
("authentik_providers_scim", "0007_scimgroup_scim_id_scimuser_scim_id_and_more"),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameModel(
|
||||
old_name="SCIMGroup",
|
||||
new_name="SCIMProviderGroup",
|
||||
),
|
||||
migrations.RenameModel(
|
||||
old_name="SCIMUser",
|
||||
new_name="SCIMProviderUser",
|
||||
),
|
||||
]
|
@ -10,10 +10,53 @@ from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework.serializers import Serializer
|
||||
|
||||
from authentik.core.models import BackchannelProvider, Group, PropertyMapping, User, UserTypes
|
||||
from authentik.lib.models import SerializerModel
|
||||
from authentik.lib.sync.outgoing.base import BaseOutgoingSyncClient
|
||||
from authentik.lib.sync.outgoing.models import OutgoingSyncProvider
|
||||
|
||||
|
||||
class SCIMProviderUser(SerializerModel):
|
||||
"""Mapping of a user and provider to a SCIM user ID"""
|
||||
|
||||
id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
|
||||
scim_id = models.TextField()
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
provider = models.ForeignKey("SCIMProvider", on_delete=models.CASCADE)
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
from authentik.providers.scim.api.users import SCIMProviderUserSerializer
|
||||
|
||||
return SCIMProviderUserSerializer
|
||||
|
||||
class Meta:
|
||||
unique_together = (("scim_id", "user", "provider"),)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"SCIM Provider User {self.user_id} to {self.provider_id}"
|
||||
|
||||
|
||||
class SCIMProviderGroup(SerializerModel):
|
||||
"""Mapping of a group and provider to a SCIM user ID"""
|
||||
|
||||
id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
|
||||
scim_id = models.TextField()
|
||||
group = models.ForeignKey(Group, on_delete=models.CASCADE)
|
||||
provider = models.ForeignKey("SCIMProvider", on_delete=models.CASCADE)
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
from authentik.providers.scim.api.groups import SCIMProviderGroupSerializer
|
||||
|
||||
return SCIMProviderGroupSerializer
|
||||
|
||||
class Meta:
|
||||
unique_together = (("scim_id", "group", "provider"),)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"SCIM Provider Group {self.group_id} to {self.provider_id}"
|
||||
|
||||
|
||||
class SCIMProvider(OutgoingSyncProvider, BackchannelProvider):
|
||||
"""SCIM 2.0 provider to create users and groups in external applications"""
|
||||
|
||||
@ -38,13 +81,13 @@ class SCIMProvider(OutgoingSyncProvider, BackchannelProvider):
|
||||
return static("authentik/sources/scim.png")
|
||||
|
||||
def client_for_model(
|
||||
self, model: type[User | Group]
|
||||
self, model: type[User | Group | SCIMProviderUser | SCIMProviderGroup]
|
||||
) -> BaseOutgoingSyncClient[User | Group, Any, Any, Self]:
|
||||
if issubclass(model, User):
|
||||
if issubclass(model, User | SCIMProviderUser):
|
||||
from authentik.providers.scim.clients.users import SCIMUserClient
|
||||
|
||||
return SCIMUserClient(self)
|
||||
if issubclass(model, Group):
|
||||
if issubclass(model, Group | SCIMProviderGroup):
|
||||
from authentik.providers.scim.clients.groups import SCIMGroupClient
|
||||
|
||||
return SCIMGroupClient(self)
|
||||
@ -104,33 +147,3 @@ class SCIMMapping(PropertyMapping):
|
||||
class Meta:
|
||||
verbose_name = _("SCIM Mapping")
|
||||
verbose_name_plural = _("SCIM Mappings")
|
||||
|
||||
|
||||
class SCIMUser(models.Model):
|
||||
"""Mapping of a user and provider to a SCIM user ID"""
|
||||
|
||||
id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
|
||||
scim_id = models.TextField()
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
provider = models.ForeignKey(SCIMProvider, on_delete=models.CASCADE)
|
||||
|
||||
class Meta:
|
||||
unique_together = (("scim_id", "user", "provider"),)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"SCIM User {self.user_id} to {self.provider_id}"
|
||||
|
||||
|
||||
class SCIMGroup(models.Model):
|
||||
"""Mapping of a group and provider to a SCIM user ID"""
|
||||
|
||||
id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
|
||||
scim_id = models.TextField()
|
||||
group = models.ForeignKey(Group, on_delete=models.CASCADE)
|
||||
provider = models.ForeignKey(SCIMProvider, on_delete=models.CASCADE)
|
||||
|
||||
class Meta:
|
||||
unique_together = (("scim_id", "group", "provider"),)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"SCIM Group {self.group_id} to {self.provider_id}"
|
||||
|
@ -1,9 +1,17 @@
|
||||
"""API URLs"""
|
||||
|
||||
from authentik.providers.scim.api.groups import (
|
||||
SCIMProviderGroupViewSet,
|
||||
)
|
||||
from authentik.providers.scim.api.property_mappings import SCIMMappingViewSet
|
||||
from authentik.providers.scim.api.providers import SCIMProviderViewSet
|
||||
from authentik.providers.scim.api.users import (
|
||||
SCIMProviderUserViewSet,
|
||||
)
|
||||
|
||||
api_urlpatterns = [
|
||||
("providers/scim", SCIMProviderViewSet),
|
||||
("providers/scim_users", SCIMProviderUserViewSet),
|
||||
("providers/scim_groups", SCIMProviderGroupViewSet),
|
||||
("propertymappings/scim", SCIMMappingViewSet),
|
||||
]
|
||||
|
@ -25,7 +25,7 @@ class ObjectFilter(ObjectPermissionsFilter):
|
||||
# Outposts (which are the only objects using internal service accounts)
|
||||
# except requests to return an empty list when they have no objects
|
||||
# assigned
|
||||
if request.user.type == UserTypes.INTERNAL_SERVICE_ACCOUNT:
|
||||
if getattr(request.user, "type", None) == UserTypes.INTERNAL_SERVICE_ACCOUNT:
|
||||
return queryset
|
||||
if not queryset.exists():
|
||||
# User doesn't have direct permission to all objects
|
||||
|
@ -147,3 +147,11 @@ class TestAPIPerms(APITestCase):
|
||||
},
|
||||
)
|
||||
self.assertEqual(res.status_code, 403)
|
||||
|
||||
def test_anonymous_user_denied(self):
|
||||
"""Test anonymous user denied"""
|
||||
res = self.client.get(reverse("authentik_api:invitation-list"))
|
||||
self.assertEqual(res.status_code, 403)
|
||||
|
||||
res = self.client.get(reverse("authentik_api:user-detail", kwargs={"pk": self.user.pk}))
|
||||
self.assertEqual(res.status_code, 403)
|
||||
|
@ -5,6 +5,7 @@ from hashlib import sha512
|
||||
from time import perf_counter, time
|
||||
from typing import Any
|
||||
|
||||
from channels.exceptions import DenyConnection
|
||||
from django.conf import settings
|
||||
from django.contrib.sessions.backends.base import UpdateError
|
||||
from django.contrib.sessions.exceptions import SessionInterrupted
|
||||
@ -271,7 +272,11 @@ class ChannelsLoggingMiddleware:
|
||||
|
||||
async def __call__(self, scope, receive, send):
|
||||
self.log(scope)
|
||||
return await self.inner(scope, receive, send)
|
||||
try:
|
||||
return await self.inner(scope, receive, send)
|
||||
except Exception as exc:
|
||||
LOGGER.warning("Exception in ASGI application", exc=exc)
|
||||
raise DenyConnection() from None
|
||||
|
||||
def log(self, scope: dict, **kwargs):
|
||||
"""Log request"""
|
||||
|
@ -161,19 +161,18 @@ class BaseLDAPSynchronizer:
|
||||
dn=object_dn,
|
||||
source=self._source,
|
||||
):
|
||||
try:
|
||||
if isinstance(value, (bytes)):
|
||||
self._logger.warning("property mapping returned bytes", mapping=mapping)
|
||||
continue
|
||||
object_field = mapping.object_field
|
||||
if object_field.startswith("attributes."):
|
||||
# Because returning a list might desired, we can't
|
||||
# rely on flatten here. Instead, just save the result as-is
|
||||
set_path_in_dict(properties, object_field, value)
|
||||
else:
|
||||
properties[object_field] = flatten(value)
|
||||
except SkipObjectException as exc:
|
||||
raise exc from exc
|
||||
if isinstance(value, (bytes)):
|
||||
self._logger.warning("property mapping returned bytes", mapping=mapping)
|
||||
continue
|
||||
object_field = mapping.object_field
|
||||
if object_field.startswith("attributes."):
|
||||
# Because returning a list might desired, we can't
|
||||
# rely on flatten here. Instead, just save the result as-is
|
||||
set_path_in_dict(properties, object_field, value)
|
||||
else:
|
||||
properties[object_field] = flatten(value)
|
||||
except SkipObjectException as exc:
|
||||
raise exc from exc
|
||||
except PropertyMappingExpressionException as exc:
|
||||
# Value error can be raised when assigning invalid data to an attribute
|
||||
Event.new(
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -12,7 +12,15 @@ class CaptchaStageSerializer(StageSerializer):
|
||||
|
||||
class Meta:
|
||||
model = CaptchaStage
|
||||
fields = StageSerializer.Meta.fields + ["public_key", "private_key", "js_url", "api_url"]
|
||||
fields = StageSerializer.Meta.fields + [
|
||||
"public_key",
|
||||
"private_key",
|
||||
"js_url",
|
||||
"api_url",
|
||||
"score_min_threshold",
|
||||
"score_max_threshold",
|
||||
"error_on_invalid_score",
|
||||
]
|
||||
extra_kwargs = {"private_key": {"write_only": True}}
|
||||
|
||||
|
||||
|
@ -0,0 +1,31 @@
|
||||
# Generated by Django 5.0.6 on 2024-06-03 15:18
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_stages_captcha", "0002_captchastage_api_url_captchastage_js_url_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="captchastage",
|
||||
name="error_on_invalid_score",
|
||||
field=models.BooleanField(
|
||||
default=True,
|
||||
help_text="When enabled and the received captcha score is outside of the given threshold, the stage will show an error message. When not enabled, the flow will continue, but the data from the captcha will be available in the context for policy decisions",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="captchastage",
|
||||
name="score_max_threshold",
|
||||
field=models.FloatField(default=1.0),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="captchastage",
|
||||
name="score_min_threshold",
|
||||
field=models.FloatField(default=0.5),
|
||||
),
|
||||
]
|
@ -14,6 +14,18 @@ class CaptchaStage(Stage):
|
||||
public_key = models.TextField(help_text=_("Public key, acquired your captcha Provider."))
|
||||
private_key = models.TextField(help_text=_("Private key, acquired your captcha Provider."))
|
||||
|
||||
score_min_threshold = models.FloatField(default=0.5) # Default values for reCaptcha
|
||||
score_max_threshold = models.FloatField(default=1.0) # Default values for reCaptcha
|
||||
|
||||
error_on_invalid_score = models.BooleanField(
|
||||
default=True,
|
||||
help_text=_(
|
||||
"When enabled and the received captcha score is outside of the given threshold, "
|
||||
"the stage will show an error message. When not enabled, the flow will continue, "
|
||||
"but the data from the captcha will be available in the context for policy decisions"
|
||||
),
|
||||
)
|
||||
|
||||
js_url = models.TextField(default="https://www.recaptcha.net/recaptcha/api.js")
|
||||
api_url = models.TextField(default="https://www.recaptcha.net/recaptcha/api/siteverify")
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
"""authentik captcha stage"""
|
||||
|
||||
from django.http.response import HttpResponse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from requests import RequestException
|
||||
from rest_framework.fields import CharField
|
||||
from rest_framework.serializers import ValidationError
|
||||
@ -16,6 +17,8 @@ from authentik.lib.utils.http import get_http_session
|
||||
from authentik.root.middleware import ClientIPMiddleware
|
||||
from authentik.stages.captcha.models import CaptchaStage
|
||||
|
||||
PLAN_CONTEXT_CAPTCHA = "captcha"
|
||||
|
||||
|
||||
class CaptchaChallenge(WithUserInfoChallenge):
|
||||
"""Site public key"""
|
||||
@ -48,11 +51,24 @@ class CaptchaChallengeResponse(ChallengeResponse):
|
||||
)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
if not data.get("success", False):
|
||||
raise ValidationError(f"Failed to validate token: {data.get('error-codes', '')}")
|
||||
except RequestException as exc:
|
||||
raise ValidationError("Failed to validate token") from exc
|
||||
return token
|
||||
if stage.error_on_invalid_score:
|
||||
if not data.get("success", False):
|
||||
raise ValidationError(
|
||||
_(
|
||||
"Failed to validate token: {error}".format(
|
||||
error=data.get("error-codes", _("Unknown error"))
|
||||
)
|
||||
)
|
||||
)
|
||||
if "score" in data:
|
||||
score = float(data.get("score"))
|
||||
if stage.score_max_threshold > -1 and score > stage.score_max_threshold:
|
||||
raise ValidationError(_("Invalid captcha response"))
|
||||
if stage.score_min_threshold > -1 and score < stage.score_min_threshold:
|
||||
raise ValidationError(_("Invalid captcha response"))
|
||||
except (RequestException, TypeError) as exc:
|
||||
raise ValidationError(_("Failed to validate token")) from exc
|
||||
return data
|
||||
|
||||
|
||||
class CaptchaStageView(ChallengeStageView):
|
||||
@ -69,5 +85,10 @@ class CaptchaStageView(ChallengeStageView):
|
||||
}
|
||||
)
|
||||
|
||||
def challenge_valid(self, response: ChallengeResponse) -> HttpResponse:
|
||||
def challenge_valid(self, response: CaptchaChallengeResponse) -> HttpResponse:
|
||||
response = response.validated_data["token"]
|
||||
self.executor.plan.context[PLAN_CONTEXT_CAPTCHA] = {
|
||||
"response": response,
|
||||
"stage": self.executor.current_stage,
|
||||
}
|
||||
return self.executor.stage_ok()
|
||||
|
@ -1,6 +1,7 @@
|
||||
"""captcha tests"""
|
||||
|
||||
from django.urls import reverse
|
||||
from requests_mock import Mocker
|
||||
|
||||
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
|
||||
from authentik.flows.markers import StageMarker
|
||||
@ -30,8 +31,89 @@ class TestCaptchaStage(FlowTestCase):
|
||||
)
|
||||
self.binding = FlowStageBinding.objects.create(target=self.flow, stage=self.stage, order=2)
|
||||
|
||||
def test_valid(self):
|
||||
@Mocker()
|
||||
def test_valid(self, mock: Mocker):
|
||||
"""Test valid captcha"""
|
||||
mock.post(
|
||||
"https://www.recaptcha.net/recaptcha/api/siteverify",
|
||||
json={
|
||||
"success": True,
|
||||
"score": 0.5,
|
||||
},
|
||||
)
|
||||
plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()])
|
||||
session = self.client.session
|
||||
session[SESSION_KEY_PLAN] = plan
|
||||
session.save()
|
||||
response = self.client.post(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
{"token": "PASSED"},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||
|
||||
@Mocker()
|
||||
def test_invalid_score_high(self, mock: Mocker):
|
||||
"""Test invalid captcha (score too high)"""
|
||||
mock.post(
|
||||
"https://www.recaptcha.net/recaptcha/api/siteverify",
|
||||
json={
|
||||
"success": True,
|
||||
"score": 99,
|
||||
},
|
||||
)
|
||||
plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()])
|
||||
session = self.client.session
|
||||
session[SESSION_KEY_PLAN] = plan
|
||||
session.save()
|
||||
response = self.client.post(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
{"token": "PASSED"},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageResponse(
|
||||
response,
|
||||
component="ak-stage-captcha",
|
||||
response_errors={"token": [{"string": "Invalid captcha response", "code": "invalid"}]},
|
||||
)
|
||||
|
||||
@Mocker()
|
||||
def test_invalid_score_low(self, mock: Mocker):
|
||||
"""Test invalid captcha (score too low)"""
|
||||
mock.post(
|
||||
"https://www.recaptcha.net/recaptcha/api/siteverify",
|
||||
json={
|
||||
"success": True,
|
||||
"score": -3,
|
||||
},
|
||||
)
|
||||
plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()])
|
||||
session = self.client.session
|
||||
session[SESSION_KEY_PLAN] = plan
|
||||
session.save()
|
||||
response = self.client.post(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
{"token": "PASSED"},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageResponse(
|
||||
response,
|
||||
component="ak-stage-captcha",
|
||||
response_errors={"token": [{"string": "Invalid captcha response", "code": "invalid"}]},
|
||||
)
|
||||
|
||||
@Mocker()
|
||||
def test_invalid_score_low_continue(self, mock: Mocker):
|
||||
"""Test invalid captcha (score too low, but continue)"""
|
||||
self.stage.error_on_invalid_score = False
|
||||
self.stage.save()
|
||||
mock.post(
|
||||
"https://www.recaptcha.net/recaptcha/api/siteverify",
|
||||
json={
|
||||
"success": True,
|
||||
"score": -3,
|
||||
},
|
||||
)
|
||||
plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()])
|
||||
session = self.client.session
|
||||
session[SESSION_KEY_PLAN] = plan
|
||||
|
@ -96,7 +96,7 @@ class TestEmailStageTemplates(FlowTestCase):
|
||||
"""Test addresses are correctly parsed"""
|
||||
message = TemplateEmailMessage(to=[("foo@bar.baz", "foo@bar.baz")])
|
||||
[sanitize_address(addr, "utf-8") for addr in message.recipients()]
|
||||
self.assertEqual(message.recipients(), ["foo@bar.baz"])
|
||||
self.assertEqual(message.recipients(), ['"foo@bar.baz" <foo@bar.baz>'])
|
||||
message = TemplateEmailMessage(to=[("some-name", "foo@bar.baz")])
|
||||
[sanitize_address(addr, "utf-8") for addr in message.recipients()]
|
||||
self.assertEqual(message.recipients(), ["some-name <foo@bar.baz>"])
|
||||
|
@ -5,6 +5,7 @@ from functools import lru_cache
|
||||
from pathlib import Path
|
||||
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
from django.core.mail.message import sanitize_address
|
||||
from django.template.exceptions import TemplateDoesNotExist
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils import translation
|
||||
@ -31,10 +32,7 @@ class TemplateEmailMessage(EmailMultiAlternatives):
|
||||
sanitized_to = []
|
||||
# Ensure that all recipients are valid
|
||||
for recipient_name, recipient_email in to:
|
||||
if recipient_name == recipient_email:
|
||||
sanitized_to.append(recipient_email)
|
||||
else:
|
||||
sanitized_to.append(f"{recipient_name} <{recipient_email}>")
|
||||
sanitized_to.append(sanitize_address((recipient_name, recipient_email), "utf-8"))
|
||||
super().__init__(to=sanitized_to, **kwargs)
|
||||
if not template_name:
|
||||
return
|
||||
|
@ -2,7 +2,7 @@
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://goauthentik.io/blueprints/schema.json",
|
||||
"type": "object",
|
||||
"title": "authentik 2024.4.2 Blueprint schema",
|
||||
"title": "authentik 2024.6.0 Blueprint schema",
|
||||
"required": [
|
||||
"version",
|
||||
"entries"
|
||||
@ -6164,6 +6164,19 @@
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"title": "Api url"
|
||||
},
|
||||
"score_min_threshold": {
|
||||
"type": "number",
|
||||
"title": "Score min threshold"
|
||||
},
|
||||
"score_max_threshold": {
|
||||
"type": "number",
|
||||
"title": "Score max threshold"
|
||||
},
|
||||
"error_on_invalid_score": {
|
||||
"type": "boolean",
|
||||
"title": "Error on invalid score",
|
||||
"description": "When enabled and the received captcha score is outside of the given threshold, the stage will show an error message. When not enabled, the flow will continue, but the data from the captcha will be available in the context for policy decisions"
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
|
@ -9,8 +9,9 @@ entries:
|
||||
model: authentik_providers_google_workspace.googleworkspaceprovidermapping
|
||||
attrs:
|
||||
name: "authentik default Google Workspace Mapping: User"
|
||||
# https://developers.google.com/admin-sdk/directory/reference/rest/v1/users#User
|
||||
expression: |
|
||||
# Field reference:
|
||||
# https://developers.google.com/admin-sdk/directory/reference/rest/v1/users#User
|
||||
# Google require givenName and familyName to be set
|
||||
givenName, familyName = request.user.name, " "
|
||||
formatted = request.user.name + " "
|
||||
@ -20,23 +21,26 @@ entries:
|
||||
if " " in request.user.name:
|
||||
givenName, _, familyName = request.user.name.partition(" ")
|
||||
formatted = request.user.name
|
||||
return {
|
||||
user = {
|
||||
"name": {
|
||||
"fullName": formatted,
|
||||
"familyName": familyName.strip(),
|
||||
"givenName": givenName.strip(),
|
||||
"displayName": formatted,
|
||||
},
|
||||
"password": request.user.password,
|
||||
"suspended": not request.user.is_active,
|
||||
}
|
||||
if not connection:
|
||||
user["password"] = request.user.password
|
||||
return user
|
||||
- identifiers:
|
||||
managed: goauthentik.io/providers/google_workspace/group
|
||||
model: authentik_providers_google_workspace.googleworkspaceprovidermapping
|
||||
attrs:
|
||||
name: "authentik default Google Workspace Mapping: Group"
|
||||
# https://developers.google.com/admin-sdk/directory/reference/rest/v1/groups#Group
|
||||
expression: |
|
||||
# Field reference:
|
||||
# https://developers.google.com/admin-sdk/directory/reference/rest/v1/groups#Group
|
||||
return {
|
||||
"name": group.name,
|
||||
}
|
||||
|
@ -9,8 +9,9 @@ entries:
|
||||
model: authentik_providers_microsoft_entra.microsoftentraprovidermapping
|
||||
attrs:
|
||||
name: "authentik default Microsoft Entra Mapping: User"
|
||||
# https://learn.microsoft.com/en-us/graph/api/resources/user?view=graph-rest-1.0
|
||||
expression: |
|
||||
# Field reference: (note that keys have to converted to snake_case)
|
||||
# https://learn.microsoft.com/en-us/graph/api/resources/user?view=graph-rest-1.0
|
||||
from msgraph.generated.models.password_profile import PasswordProfile
|
||||
|
||||
user = {
|
||||
@ -35,8 +36,9 @@ entries:
|
||||
model: authentik_providers_microsoft_entra.microsoftentraprovidermapping
|
||||
attrs:
|
||||
name: "authentik default Microsoft Entra Mapping: Group"
|
||||
# https://learn.microsoft.com/en-us/graph/api/group-post-groups?view=graph-rest-1.0&tabs=http#request-body
|
||||
expression: |
|
||||
# Field reference: (note that keys have to converted to snake_case)
|
||||
# https://learn.microsoft.com/en-us/graph/api/group-post-groups?view=graph-rest-1.0&tabs=http#request-body
|
||||
return {
|
||||
"display_name": group.name,
|
||||
"mail_enabled": False,
|
||||
|
@ -31,7 +31,7 @@ services:
|
||||
volumes:
|
||||
- redis:/data
|
||||
server:
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.4.2}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.6.0}
|
||||
restart: unless-stopped
|
||||
command: server
|
||||
environment:
|
||||
@ -52,7 +52,7 @@ services:
|
||||
- postgresql
|
||||
- redis
|
||||
worker:
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.4.2}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.6.0}
|
||||
restart: unless-stopped
|
||||
command: worker
|
||||
environment:
|
||||
|
10
go.mod
10
go.mod
@ -5,7 +5,7 @@ go 1.22.2
|
||||
require (
|
||||
beryju.io/ldap v0.1.0
|
||||
github.com/coreos/go-oidc v2.2.1+incompatible
|
||||
github.com/getsentry/sentry-go v0.28.0
|
||||
github.com/getsentry/sentry-go v0.28.1
|
||||
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
|
||||
github.com/go-ldap/ldap/v3 v3.4.8
|
||||
github.com/go-openapi/runtime v0.28.0
|
||||
@ -16,21 +16,21 @@ require (
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/gorilla/securecookie v1.1.2
|
||||
github.com/gorilla/sessions v1.2.2
|
||||
github.com/gorilla/websocket v1.5.1
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/jellydator/ttlcache/v3 v3.2.0
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
|
||||
github.com/pires/go-proxyproto v0.7.0
|
||||
github.com/prometheus/client_golang v1.19.1
|
||||
github.com/redis/go-redis/v9 v9.5.2
|
||||
github.com/redis/go-redis/v9 v9.5.3
|
||||
github.com/sethvargo/go-envconfig v1.0.3
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/wwt/guac v1.3.2
|
||||
goauthentik.io/api/v3 v3.2024042.9
|
||||
goauthentik.io/api/v3 v3.2024042.11
|
||||
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
||||
golang.org/x/oauth2 v0.20.0
|
||||
golang.org/x/oauth2 v0.21.0
|
||||
golang.org/x/sync v0.7.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
layeh.com/radius v0.0.0-20210819152912-ad72663a72ab
|
||||
|
20
go.sum
20
go.sum
@ -69,8 +69,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
|
||||
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/getsentry/sentry-go v0.28.0 h1:7Rqx9M3ythTKy2J6uZLHmc8Sz9OGgIlseuO1iBX/s0M=
|
||||
github.com/getsentry/sentry-go v0.28.0/go.mod h1:1fQZ+7l7eeJ3wYi82q5Hg8GqAPgefRq+FP/QhafYVgg=
|
||||
github.com/getsentry/sentry-go v0.28.1 h1:zzaSm/vHmGllRM6Tpx1492r0YDzauArdBfkJRtY6P5k=
|
||||
github.com/getsentry/sentry-go v0.28.1/go.mod h1:1fQZ+7l7eeJ3wYi82q5Hg8GqAPgefRq+FP/QhafYVgg=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||
@ -176,8 +176,8 @@ github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/z
|
||||
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
|
||||
github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
@ -242,8 +242,8 @@ github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSz
|
||||
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
|
||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||
github.com/redis/go-redis/v9 v9.5.2 h1:L0L3fcSNReTRGyZ6AqAEN0K56wYeYAwapBIhkvh0f3E=
|
||||
github.com/redis/go-redis/v9 v9.5.2/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/redis/go-redis/v9 v9.5.3 h1:fOAp1/uJG+ZtcITgZOfYFmTKPE7n4Vclj1wZFgRciUU=
|
||||
github.com/redis/go-redis/v9 v9.5.3/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
@ -294,8 +294,8 @@ go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
||||
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
|
||||
goauthentik.io/api/v3 v3.2024042.9 h1:zj3nqmOEHNBsfANTv7Ec4yLZA755GKHU+WLz2t+nfaI=
|
||||
goauthentik.io/api/v3 v3.2024042.9/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||
goauthentik.io/api/v3 v3.2024042.11 h1:cGgUz1E8rlMphGvv04VI7i+MgT8eidZbxTpza5zd96I=
|
||||
goauthentik.io/api/v3 v3.2024042.11/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
@ -382,8 +382,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-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.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=
|
||||
golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -29,4 +29,4 @@ func UserAgent() string {
|
||||
return fmt.Sprintf("authentik@%s", FullVersion())
|
||||
}
|
||||
|
||||
const VERSION = "2024.4.2"
|
||||
const VERSION = "2024.6.0"
|
||||
|
@ -38,7 +38,6 @@ func (cs *CryptoStore) AddKeypair(uuid string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cs.fingerprints[uuid] = cs.getFingerprint(uuid)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -73,6 +72,7 @@ func (cs *CryptoStore) Fetch(uuid string) error {
|
||||
return err
|
||||
}
|
||||
cs.certificates[uuid] = &x509cert
|
||||
cs.fingerprints[uuid] = cfp
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ from pathlib import Path
|
||||
from tempfile import gettempdir
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from cryptography.exceptions import InternalError
|
||||
from cryptography.hazmat.backends.openssl.backend import backend
|
||||
from defusedxml import defuse_stdlib
|
||||
from prometheus_client.values import MultiProcessValue
|
||||
@ -30,10 +29,8 @@ if TYPE_CHECKING:
|
||||
|
||||
defuse_stdlib()
|
||||
|
||||
try:
|
||||
if CONFIG.get_bool("compliance.fips.enabled", False):
|
||||
backend._enable_fips()
|
||||
except InternalError:
|
||||
pass
|
||||
|
||||
wait_for_db()
|
||||
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-06-03 00:08+0000\n"
|
||||
"POT-Creation-Date: 2024-06-05 00:07+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -2363,6 +2363,14 @@ msgstr ""
|
||||
msgid "Private key, acquired your captcha Provider."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/captcha/models.py
|
||||
msgid ""
|
||||
"When enabled and the received captcha score is outside of the given "
|
||||
"threshold, the stage will show an error message. When not enabled, the flow "
|
||||
"will continue, but the data from the captcha will be available in the "
|
||||
"context for policy decisions"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/captcha/models.py
|
||||
msgid "Captcha Stage"
|
||||
msgstr ""
|
||||
@ -2371,6 +2379,23 @@ msgstr ""
|
||||
msgid "Captcha Stages"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
msgid "Unknown error"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
#, python-brace-format
|
||||
msgid "Failed to validate token: {error}"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
msgid "Invalid captcha response"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
msgid "Failed to validate token"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/consent/models.py
|
||||
msgid ""
|
||||
"Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
|
||||
|
Binary file not shown.
@ -19,7 +19,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-05-03 00:08+0000\n"
|
||||
"POT-Creation-Date: 2024-06-05 00:07+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: Marc Schmitt, 2024\n"
|
||||
"Language-Team: French (https://app.transifex.com/authentik/teams/119923/fr/)\n"
|
||||
@ -106,12 +106,14 @@ msgid "Brands"
|
||||
msgstr "Marques"
|
||||
|
||||
#: authentik/core/api/providers.py
|
||||
msgid "SAML Provider from Metadata"
|
||||
msgstr "Fournisseur SAML depuis métadonnées"
|
||||
|
||||
#: authentik/core/api/providers.py
|
||||
msgid "Create a SAML Provider by importing its Metadata."
|
||||
msgstr "Créer un fournisseur SAML en important ses métadonnées."
|
||||
msgid ""
|
||||
"When not set all providers are returned. When set to true, only backchannel "
|
||||
"providers are returned. When set to false, backchannel providers are "
|
||||
"excluded"
|
||||
msgstr ""
|
||||
"Si non défini, tous les fournisseurs sont retournés. Si vrai, seul les "
|
||||
"fournisseurs backchannels sont retournés. Si faux, les fournisseurs "
|
||||
"backchannels sont exclus"
|
||||
|
||||
#: authentik/core/api/users.py
|
||||
msgid "No leading or trailing slashes allowed."
|
||||
@ -466,6 +468,74 @@ msgstr "Entreprise est requis pour accéder à cette fonctionnalité."
|
||||
msgid "Feature only accessible for internal users."
|
||||
msgstr "Fonctionnalité accessible aux utilisateurs internes uniquement."
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
#: authentik/providers/scim/models.py authentik/sources/ldap/models.py
|
||||
msgid "Property mappings used for group creation/updating."
|
||||
msgstr ""
|
||||
"Mappages de propriétés utilisés lors de la création et de la mise à jour des"
|
||||
" groupes."
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider"
|
||||
msgstr "Fournisseur Google Workspace"
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Providers"
|
||||
msgstr "Fournisseurs Google Workspace"
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider Mapping"
|
||||
msgstr "Mappage de propriété Google Workspace"
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider Mappings"
|
||||
msgstr "Mappages de propriété Google Workspace"
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider User"
|
||||
msgstr "Utilisateur du fournisseur Google Workspace"
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider Users"
|
||||
msgstr "Utilisateurs du fournisseur Google Workspace"
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider Group"
|
||||
msgstr "Groupe du fournisseur Google Workspace"
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider Groups"
|
||||
msgstr "Groupes du fournisseur Google Workspace"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider"
|
||||
msgstr "Fournisseur Microsoft Entra"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Providers"
|
||||
msgstr "Fournisseurs Microsoft Entra"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider Mapping"
|
||||
msgstr "Mappage de propriété Microsoft Entra"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider Mappings"
|
||||
msgstr "Mappages de propriété Microsoft Entra"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider User"
|
||||
msgstr "Utilisateur du fournisseur Microsoft Entra"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider Group"
|
||||
msgstr "Groupe du fournisseur Microsoft Entra"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider Groups"
|
||||
msgstr "Groupes du fournisseur Microsoft Entra"
|
||||
|
||||
#: authentik/enterprise/providers/rac/models.py
|
||||
#: authentik/stages/user_login/models.py
|
||||
msgid ""
|
||||
@ -838,6 +908,25 @@ msgstr "Jetons du flux"
|
||||
msgid "Invalid next URL"
|
||||
msgstr "URL suivante invalide"
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
msgid "Starting full provider sync"
|
||||
msgstr "Démarrage d'une synchronisation complète du fournisseur"
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-format
|
||||
msgid "Syncing page %(page)d of users"
|
||||
msgstr "Synchronisation de la page %(page)d d'utilisateurs"
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-format
|
||||
msgid "Syncing page %(page)d of groups"
|
||||
msgstr "Synchronisation de la page %(page)d de groupes"
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
msgid "Stopping sync due to error: {error}"
|
||||
msgstr "Arrêt de la synchronisation due à l'erreur : {error}"
|
||||
|
||||
#: authentik/lib/utils/time.py
|
||||
#, python-format
|
||||
msgid "%(value)s is not in the correct format of 'hours=3;minutes=1'."
|
||||
@ -1526,10 +1615,6 @@ msgstr "Compatibilité GitHub : accès aux adresses email"
|
||||
msgid "GitHub Compatibility: Access your Groups"
|
||||
msgstr "Compatibilité GitHub : accès aux groupes"
|
||||
|
||||
#: authentik/providers/oauth2/views/userinfo.py
|
||||
msgid "authentik API Access on behalf of your user"
|
||||
msgstr "Accès à l'API authentik au nom des utilisateurs"
|
||||
|
||||
#: authentik/providers/proxy/api.py
|
||||
msgid "User and password attributes must be set when basic auth is enabled."
|
||||
msgstr ""
|
||||
@ -1811,6 +1896,14 @@ msgstr "Mappages de propriétés SAML"
|
||||
msgid "SAML Property Mappings"
|
||||
msgstr "Mappages de propriétés SAML"
|
||||
|
||||
#: authentik/providers/saml/models.py
|
||||
msgid "SAML Provider from Metadata"
|
||||
msgstr "Fournisseur SAML depuis métadonnées"
|
||||
|
||||
#: authentik/providers/saml/models.py
|
||||
msgid "SAML Providers from Metadata"
|
||||
msgstr "Fournisseurs SAML depuis métadonnées"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "Base URL to SCIM requests, usually ends in /v2"
|
||||
msgstr "URL de base pour les requêtes SCIM, se terminant généralement par /v2"
|
||||
@ -1819,12 +1912,6 @@ msgstr "URL de base pour les requêtes SCIM, se terminant généralement par /v2
|
||||
msgid "Authentication token"
|
||||
msgstr "Jeton d'authentification"
|
||||
|
||||
#: authentik/providers/scim/models.py authentik/sources/ldap/models.py
|
||||
msgid "Property mappings used for group creation/updating."
|
||||
msgstr ""
|
||||
"Mappages de propriétés utilisés lors de la création et de la mise à jour des"
|
||||
" groupes."
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "SCIM Provider"
|
||||
msgstr "Fournisseur SCIM"
|
||||
@ -1841,39 +1928,6 @@ msgstr "Mappage SCIM"
|
||||
msgid "SCIM Mappings"
|
||||
msgstr "Mappages SCIM"
|
||||
|
||||
#: authentik/providers/scim/tasks.py
|
||||
msgid "Starting full SCIM sync"
|
||||
msgstr "Démarrage d'une synchronisation SCIM complète"
|
||||
|
||||
#: authentik/providers/scim/tasks.py
|
||||
#, python-format
|
||||
msgid "Syncing page %(page)d of users"
|
||||
msgstr "Synchronisation de la page %(page)d d'utilisateurs"
|
||||
|
||||
#: authentik/providers/scim/tasks.py
|
||||
#, python-format
|
||||
msgid "Syncing page %(page)d of groups"
|
||||
msgstr "Synchronisation de la page %(page)d de groupes"
|
||||
|
||||
#: authentik/providers/scim/tasks.py
|
||||
#, python-brace-format
|
||||
msgid "Failed to sync user {user_name} due to remote error: {error}"
|
||||
msgstr ""
|
||||
"Échec de synchronisation de l'utilisateur {user_name} dû à une erreur "
|
||||
"distante : {error}"
|
||||
|
||||
#: authentik/providers/scim/tasks.py
|
||||
#, python-brace-format
|
||||
msgid "Stopping sync due to error: {error}"
|
||||
msgstr "Arrêt de la synchronisation due à l'erreur : {error}"
|
||||
|
||||
#: authentik/providers/scim/tasks.py
|
||||
#, python-brace-format
|
||||
msgid "Failed to sync group {group_name} due to remote error: {error}"
|
||||
msgstr ""
|
||||
"Échec de synchronisation du groupe {group_name} dû à une erreur distante : "
|
||||
"{error}"
|
||||
|
||||
#: authentik/rbac/models.py
|
||||
msgid "Role"
|
||||
msgstr "Rôle"
|
||||
@ -1894,14 +1948,6 @@ msgstr "Permissions système"
|
||||
msgid "Can view system info"
|
||||
msgstr "Peut voir les informations du système"
|
||||
|
||||
#: authentik/rbac/models.py
|
||||
msgid "Can view system tasks"
|
||||
msgstr "Peut voir les tâches du système"
|
||||
|
||||
#: authentik/rbac/models.py
|
||||
msgid "Can run system tasks"
|
||||
msgstr "Peut lancer des tâches système"
|
||||
|
||||
#: authentik/rbac/models.py
|
||||
msgid "Can access admin interface"
|
||||
msgstr "Peut accéder à l'interface d'administration"
|
||||
@ -2562,6 +2608,18 @@ msgstr "Clé publique, acquise auprès de votre fournisseur captcha."
|
||||
msgid "Private key, acquired your captcha Provider."
|
||||
msgstr "Clé privée, acquise auprès de votre fournisseur captcha."
|
||||
|
||||
#: authentik/stages/captcha/models.py
|
||||
msgid ""
|
||||
"When enabled and the received captcha score is outside of the given "
|
||||
"threshold, the stage will show an error message. When not enabled, the flow "
|
||||
"will continue, but the data from the captcha will be available in the "
|
||||
"context for policy decisions"
|
||||
msgstr ""
|
||||
"Si activé et le score captcha reçu est hors de la limite donnée, l'étape "
|
||||
"affichera un message d'erreur. Si désactivé, le flux continuera, mais la "
|
||||
"donnée du captcha sera disponible dans le contexte pour décision dans une "
|
||||
"politique."
|
||||
|
||||
#: authentik/stages/captcha/models.py
|
||||
msgid "Captcha Stage"
|
||||
msgstr "Étape de Captcha"
|
||||
@ -2570,6 +2628,23 @@ msgstr "Étape de Captcha"
|
||||
msgid "Captcha Stages"
|
||||
msgstr "Étapes de Captcha"
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
msgid "Unknown error"
|
||||
msgstr "Erreur inconnue"
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
#, python-brace-format
|
||||
msgid "Failed to validate token: {error}"
|
||||
msgstr "Échec de validation du jeton : {error}"
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
msgid "Invalid captcha response"
|
||||
msgstr "Réponse captcha invalide"
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
msgid "Failed to validate token"
|
||||
msgstr "Échec de validation du jeton"
|
||||
|
||||
#: authentik/stages/consent/models.py
|
||||
msgid ""
|
||||
"Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -9,16 +9,16 @@
|
||||
# Nicholas Winterhalter, 2023
|
||||
# Ренат Шарафутдинов, 2023
|
||||
# Stepan Karavaev, 2024
|
||||
# Anton Babenko, 2024
|
||||
# Anton, 2024
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-06-03 00:08+0000\n"
|
||||
"POT-Creation-Date: 2024-06-05 00:07+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: Anton Babenko, 2024\n"
|
||||
"Last-Translator: Anton, 2024\n"
|
||||
"Language-Team: Russian (https://app.transifex.com/authentik/teams/119923/ru/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -162,7 +162,7 @@ msgstr "Сбросить пароль"
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "Can impersonate other users"
|
||||
msgstr "Может выдавать себя за других пользователей"
|
||||
msgstr "Может имитировать других пользователей"
|
||||
|
||||
#: authentik/core/models.py authentik/rbac/models.py
|
||||
msgid "Can assign permissions to users"
|
||||
@ -2589,6 +2589,18 @@ msgstr "Открытый ключ, полученный от вашего про
|
||||
msgid "Private key, acquired your captcha Provider."
|
||||
msgstr "Закрытый ключ, полученный от вашего провайдера капчи."
|
||||
|
||||
#: authentik/stages/captcha/models.py
|
||||
msgid ""
|
||||
"When enabled and the received captcha score is outside of the given "
|
||||
"threshold, the stage will show an error message. When not enabled, the flow "
|
||||
"will continue, but the data from the captcha will be available in the "
|
||||
"context for policy decisions"
|
||||
msgstr ""
|
||||
"Когда эта опция включена, и полученная оценка капчи находится за пределами "
|
||||
"заданного порогового значения, на этом этапе будет показано сообщение об "
|
||||
"ошибке. Когда эта опция не включена, поток будет продолжаться, но данные "
|
||||
"капчи будут доступны в контексте для принятия решений политики"
|
||||
|
||||
#: authentik/stages/captcha/models.py
|
||||
msgid "Captcha Stage"
|
||||
msgstr "Этап с каптчей"
|
||||
@ -2597,6 +2609,23 @@ msgstr "Этап с каптчей"
|
||||
msgid "Captcha Stages"
|
||||
msgstr "Этапы с каптчей"
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
msgid "Unknown error"
|
||||
msgstr "Неизвестная ошибка"
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
#, python-brace-format
|
||||
msgid "Failed to validate token: {error}"
|
||||
msgstr "Ошибка валидации токена: {error}"
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
msgid "Invalid captcha response"
|
||||
msgstr "Неверный ответ на капчу"
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
msgid "Failed to validate token"
|
||||
msgstr "Ошибка валидации токена"
|
||||
|
||||
#: authentik/stages/consent/models.py
|
||||
msgid ""
|
||||
"Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
|
||||
@ -3054,7 +3083,7 @@ msgstr "Разделитель: Статическая разделительн
|
||||
#: authentik/stages/prompt/models.py
|
||||
msgid "Hidden: Hidden field, can be used to insert data into form."
|
||||
msgstr ""
|
||||
"Скрытый: Скрытое поле, может быть использовано для вставки данных в форму."
|
||||
"Скрыто: Скрытое поле, может быть использовано для вставки данных в форму."
|
||||
|
||||
#: authentik/stages/prompt/models.py
|
||||
msgid "Static: Static value, displayed as-is."
|
||||
@ -3086,7 +3115,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"По желанию предварительно заполните поле ввода начальным значением. При "
|
||||
"создании поля с фиксированным выбором включите интерпретацию как выражение и"
|
||||
" возврат списка, чтобы вернуть несколько вариантов по умолчанию."
|
||||
" возвращайте список, чтобы вернуть несколько вариантов по умолчанию."
|
||||
|
||||
#: authentik/stages/prompt/models.py
|
||||
msgid "Prompt"
|
||||
|
@ -7,17 +7,17 @@
|
||||
# Chen Zhikai, 2022
|
||||
# 刘松, 2022
|
||||
# Jens L. <jens@goauthentik.io>, 2024
|
||||
# deluxghost, 2024
|
||||
# Tianhao Chai <cth451@gmail.com>, 2024
|
||||
# deluxghost, 2024
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-06-03 00:08+0000\n"
|
||||
"POT-Creation-Date: 2024-06-05 00:07+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: Tianhao Chai <cth451@gmail.com>, 2024\n"
|
||||
"Last-Translator: deluxghost, 2024\n"
|
||||
"Language-Team: Chinese Simplified (https://app.transifex.com/authentik/teams/119923/zh-Hans/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -2401,6 +2401,14 @@ msgstr "公钥,从您的验证码提供商处取得。"
|
||||
msgid "Private key, acquired your captcha Provider."
|
||||
msgstr "私钥,从您的验证码提供商处取得。"
|
||||
|
||||
#: authentik/stages/captcha/models.py
|
||||
msgid ""
|
||||
"When enabled and the received captcha score is outside of the given "
|
||||
"threshold, the stage will show an error message. When not enabled, the flow "
|
||||
"will continue, but the data from the captcha will be available in the "
|
||||
"context for policy decisions"
|
||||
msgstr "启用时,如果接收到的验证码分数超出给定的阈值,此阶段将显示错误信息。未启用时,流程会继续,但来自验证码的数据可在上下文中用于策略决定。"
|
||||
|
||||
#: authentik/stages/captcha/models.py
|
||||
msgid "Captcha Stage"
|
||||
msgstr "验证码阶段"
|
||||
@ -2409,6 +2417,23 @@ msgstr "验证码阶段"
|
||||
msgid "Captcha Stages"
|
||||
msgstr "验证码阶段"
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
msgid "Unknown error"
|
||||
msgstr "未知错误"
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
#, python-brace-format
|
||||
msgid "Failed to validate token: {error}"
|
||||
msgstr "验证令牌失败:{error}"
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
msgid "Invalid captcha response"
|
||||
msgstr "验证码响应无效"
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
msgid "Failed to validate token"
|
||||
msgstr "验证令牌失败"
|
||||
|
||||
#: authentik/stages/consent/models.py
|
||||
msgid ""
|
||||
"Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
|
||||
|
@ -14,7 +14,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-06-03 00:08+0000\n"
|
||||
"POT-Creation-Date: 2024-06-05 00:07+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: deluxghost, 2024\n"
|
||||
"Language-Team: Chinese (China) (https://app.transifex.com/authentik/teams/119923/zh_CN/)\n"
|
||||
@ -2400,6 +2400,14 @@ msgstr "公钥,从您的验证码提供商处取得。"
|
||||
msgid "Private key, acquired your captcha Provider."
|
||||
msgstr "私钥,从您的验证码提供商处取得。"
|
||||
|
||||
#: authentik/stages/captcha/models.py
|
||||
msgid ""
|
||||
"When enabled and the received captcha score is outside of the given "
|
||||
"threshold, the stage will show an error message. When not enabled, the flow "
|
||||
"will continue, but the data from the captcha will be available in the "
|
||||
"context for policy decisions"
|
||||
msgstr "启用时,如果接收到的验证码分数超出给定的阈值,此阶段将显示错误信息。未启用时,流程会继续,但来自验证码的数据可在上下文中用于策略决定。"
|
||||
|
||||
#: authentik/stages/captcha/models.py
|
||||
msgid "Captcha Stage"
|
||||
msgstr "验证码阶段"
|
||||
@ -2408,6 +2416,23 @@ msgstr "验证码阶段"
|
||||
msgid "Captcha Stages"
|
||||
msgstr "验证码阶段"
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
msgid "Unknown error"
|
||||
msgstr "未知错误"
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
#, python-brace-format
|
||||
msgid "Failed to validate token: {error}"
|
||||
msgstr "验证令牌失败:{error}"
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
msgid "Invalid captcha response"
|
||||
msgstr "验证码响应无效"
|
||||
|
||||
#: authentik/stages/captcha/stage.py
|
||||
msgid "Failed to validate token"
|
||||
msgstr "验证令牌失败"
|
||||
|
||||
#: authentik/stages/consent/models.py
|
||||
msgid ""
|
||||
"Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
|
||||
|
@ -4,7 +4,7 @@ import os
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
from cryptography.exceptions import InternalError
|
||||
from authentik.lib.config import CONFIG
|
||||
from cryptography.hazmat.backends.openssl.backend import backend
|
||||
from defusedxml import defuse_stdlib
|
||||
from django.utils.autoreload import DJANGO_AUTORELOAD_ENV
|
||||
@ -24,10 +24,8 @@ warnings.filterwarnings(
|
||||
|
||||
defuse_stdlib()
|
||||
|
||||
try:
|
||||
if CONFIG.get_bool("compliance.fips.enabled", False):
|
||||
backend._enable_fips()
|
||||
except InternalError:
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
354
poetry.lock
generated
354
poetry.lock
generated
@ -1,4 +1,4 @@
|
||||
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "aiohttp"
|
||||
@ -336,13 +336,13 @@ aio = ["aiohttp (>=3.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "azure-identity"
|
||||
version = "1.16.0"
|
||||
version = "1.16.1"
|
||||
description = "Microsoft Azure Identity Library for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "azure-identity-1.16.0.tar.gz", hash = "sha256:6ff1d667cdcd81da1ceab42f80a0be63ca846629f518a922f7317a7e3c844e1b"},
|
||||
{file = "azure_identity-1.16.0-py3-none-any.whl", hash = "sha256:722fdb60b8fdd55fa44dc378b8072f4b419b56a5e54c0de391f644949f3a826f"},
|
||||
{file = "azure-identity-1.16.1.tar.gz", hash = "sha256:6d93f04468f240d59246d8afde3091494a5040d4f141cad0f49fc0c399d0d91e"},
|
||||
{file = "azure_identity-1.16.1-py3-none-any.whl", hash = "sha256:8fb07c25642cd4ac422559a8b50d3e77f73dcc2bbfaba419d06d6c9d7cff6726"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -353,13 +353,13 @@ msal-extensions = ">=0.3.0"
|
||||
|
||||
[[package]]
|
||||
name = "bandit"
|
||||
version = "1.7.8"
|
||||
version = "1.7.9"
|
||||
description = "Security oriented static analyser for python code."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "bandit-1.7.8-py3-none-any.whl", hash = "sha256:509f7af645bc0cd8fd4587abc1a038fc795636671ee8204d502b933aee44f381"},
|
||||
{file = "bandit-1.7.8.tar.gz", hash = "sha256:36de50f720856ab24a24dbaa5fee2c66050ed97c1477e0a1159deab1775eab6b"},
|
||||
{file = "bandit-1.7.9-py3-none-any.whl", hash = "sha256:52077cb339000f337fb25f7e045995c4ad01511e716e5daac37014b9752de8ec"},
|
||||
{file = "bandit-1.7.9.tar.gz", hash = "sha256:7c395a436743018f7be0a4cbb0a4ea9b902b6d87264ddecf8cfdc73b4f78ff61"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -1020,43 +1020,43 @@ toml = ["tomli"]
|
||||
|
||||
[[package]]
|
||||
name = "cryptography"
|
||||
version = "42.0.7"
|
||||
version = "42.0.8"
|
||||
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "cryptography-42.0.7-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a987f840718078212fdf4504d0fd4c6effe34a7e4740378e59d47696e8dfb477"},
|
||||
{file = "cryptography-42.0.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd13b5e9b543532453de08bcdc3cc7cebec6f9883e886fd20a92f26940fd3e7a"},
|
||||
{file = "cryptography-42.0.7-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a79165431551042cc9d1d90e6145d5d0d3ab0f2d66326c201d9b0e7f5bf43604"},
|
||||
{file = "cryptography-42.0.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a47787a5e3649008a1102d3df55424e86606c9bae6fb77ac59afe06d234605f8"},
|
||||
{file = "cryptography-42.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:02c0eee2d7133bdbbc5e24441258d5d2244beb31da5ed19fbb80315f4bbbff55"},
|
||||
{file = "cryptography-42.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5e44507bf8d14b36b8389b226665d597bc0f18ea035d75b4e53c7b1ea84583cc"},
|
||||
{file = "cryptography-42.0.7-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7f8b25fa616d8b846aef64b15c606bb0828dbc35faf90566eb139aa9cff67af2"},
|
||||
{file = "cryptography-42.0.7-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:93a3209f6bb2b33e725ed08ee0991b92976dfdcf4e8b38646540674fc7508e13"},
|
||||
{file = "cryptography-42.0.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e6b8f1881dac458c34778d0a424ae5769de30544fc678eac51c1c8bb2183e9da"},
|
||||
{file = "cryptography-42.0.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3de9a45d3b2b7d8088c3fbf1ed4395dfeff79d07842217b38df14ef09ce1d8d7"},
|
||||
{file = "cryptography-42.0.7-cp37-abi3-win32.whl", hash = "sha256:789caea816c6704f63f6241a519bfa347f72fbd67ba28d04636b7c6b7da94b0b"},
|
||||
{file = "cryptography-42.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:8cb8ce7c3347fcf9446f201dc30e2d5a3c898d009126010cbd1f443f28b52678"},
|
||||
{file = "cryptography-42.0.7-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:a3a5ac8b56fe37f3125e5b72b61dcde43283e5370827f5233893d461b7360cd4"},
|
||||
{file = "cryptography-42.0.7-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:779245e13b9a6638df14641d029add5dc17edbef6ec915688f3acb9e720a5858"},
|
||||
{file = "cryptography-42.0.7-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d563795db98b4cd57742a78a288cdbdc9daedac29f2239793071fe114f13785"},
|
||||
{file = "cryptography-42.0.7-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:31adb7d06fe4383226c3e963471f6837742889b3c4caa55aac20ad951bc8ffda"},
|
||||
{file = "cryptography-42.0.7-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:efd0bf5205240182e0f13bcaea41be4fdf5c22c5129fc7ced4a0282ac86998c9"},
|
||||
{file = "cryptography-42.0.7-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a9bc127cdc4ecf87a5ea22a2556cab6c7eda2923f84e4f3cc588e8470ce4e42e"},
|
||||
{file = "cryptography-42.0.7-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:3577d029bc3f4827dd5bf8bf7710cac13527b470bbf1820a3f394adb38ed7d5f"},
|
||||
{file = "cryptography-42.0.7-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2e47577f9b18723fa294b0ea9a17d5e53a227867a0a4904a1a076d1646d45ca1"},
|
||||
{file = "cryptography-42.0.7-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1a58839984d9cb34c855197043eaae2c187d930ca6d644612843b4fe8513c886"},
|
||||
{file = "cryptography-42.0.7-cp39-abi3-win32.whl", hash = "sha256:e6b79d0adb01aae87e8a44c2b64bc3f3fe59515280e00fb6d57a7267a2583cda"},
|
||||
{file = "cryptography-42.0.7-cp39-abi3-win_amd64.whl", hash = "sha256:16268d46086bb8ad5bf0a2b5544d8a9ed87a0e33f5e77dd3c3301e63d941a83b"},
|
||||
{file = "cryptography-42.0.7-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2954fccea107026512b15afb4aa664a5640cd0af630e2ee3962f2602693f0c82"},
|
||||
{file = "cryptography-42.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:362e7197754c231797ec45ee081f3088a27a47c6c01eff2ac83f60f85a50fe60"},
|
||||
{file = "cryptography-42.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4f698edacf9c9e0371112792558d2f705b5645076cc0aaae02f816a0171770fd"},
|
||||
{file = "cryptography-42.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5482e789294854c28237bba77c4c83be698be740e31a3ae5e879ee5444166582"},
|
||||
{file = "cryptography-42.0.7-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e9b2a6309f14c0497f348d08a065d52f3020656f675819fc405fb63bbcd26562"},
|
||||
{file = "cryptography-42.0.7-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d8e3098721b84392ee45af2dd554c947c32cc52f862b6a3ae982dbb90f577f14"},
|
||||
{file = "cryptography-42.0.7-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c65f96dad14f8528a447414125e1fc8feb2ad5a272b8f68477abbcc1ea7d94b9"},
|
||||
{file = "cryptography-42.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:36017400817987670037fbb0324d71489b6ead6231c9604f8fc1f7d008087c68"},
|
||||
{file = "cryptography-42.0.7.tar.gz", hash = "sha256:ecbfbc00bf55888edda9868a4cf927205de8499e7fabe6c050322298382953f2"},
|
||||
{file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:81d8a521705787afe7a18d5bfb47ea9d9cc068206270aad0b96a725022e18d2e"},
|
||||
{file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:961e61cefdcb06e0c6d7e3a1b22ebe8b996eb2bf50614e89384be54c48c6b63d"},
|
||||
{file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3ec3672626e1b9e55afd0df6d774ff0e953452886e06e0f1eb7eb0c832e8902"},
|
||||
{file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e599b53fd95357d92304510fb7bda8523ed1f79ca98dce2f43c115950aa78801"},
|
||||
{file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5226d5d21ab681f432a9c1cf8b658c0cb02533eece706b155e5fbd8a0cdd3949"},
|
||||
{file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6b7c4f03ce01afd3b76cf69a5455caa9cfa3de8c8f493e0d3ab7d20611c8dae9"},
|
||||
{file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:2346b911eb349ab547076f47f2e035fc8ff2c02380a7cbbf8d87114fa0f1c583"},
|
||||
{file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad803773e9df0b92e0a817d22fd8a3675493f690b96130a5e24f1b8fabbea9c7"},
|
||||
{file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2f66d9cd9147ee495a8374a45ca445819f8929a3efcd2e3df6428e46c3cbb10b"},
|
||||
{file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d45b940883a03e19e944456a558b67a41160e367a719833c53de6911cabba2b7"},
|
||||
{file = "cryptography-42.0.8-cp37-abi3-win32.whl", hash = "sha256:a0c5b2b0585b6af82d7e385f55a8bc568abff8923af147ee3c07bd8b42cda8b2"},
|
||||
{file = "cryptography-42.0.8-cp37-abi3-win_amd64.whl", hash = "sha256:57080dee41209e556a9a4ce60d229244f7a66ef52750f813bfbe18959770cfba"},
|
||||
{file = "cryptography-42.0.8-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:dea567d1b0e8bc5764b9443858b673b734100c2871dc93163f58c46a97a83d28"},
|
||||
{file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4783183f7cb757b73b2ae9aed6599b96338eb957233c58ca8f49a49cc32fd5e"},
|
||||
{file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0608251135d0e03111152e41f0cc2392d1e74e35703960d4190b2e0f4ca9c70"},
|
||||
{file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dc0fdf6787f37b1c6b08e6dfc892d9d068b5bdb671198c72072828b80bd5fe4c"},
|
||||
{file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9c0c1716c8447ee7dbf08d6db2e5c41c688544c61074b54fc4564196f55c25a7"},
|
||||
{file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fff12c88a672ab9c9c1cf7b0c80e3ad9e2ebd9d828d955c126be4fd3e5578c9e"},
|
||||
{file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:cafb92b2bc622cd1aa6a1dce4b93307792633f4c5fe1f46c6b97cf67073ec961"},
|
||||
{file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:31f721658a29331f895a5a54e7e82075554ccfb8b163a18719d342f5ffe5ecb1"},
|
||||
{file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b297f90c5723d04bcc8265fc2a0f86d4ea2e0f7ab4b6994459548d3a6b992a14"},
|
||||
{file = "cryptography-42.0.8-cp39-abi3-win32.whl", hash = "sha256:2f88d197e66c65be5e42cd72e5c18afbfae3f741742070e3019ac8f4ac57262c"},
|
||||
{file = "cryptography-42.0.8-cp39-abi3-win_amd64.whl", hash = "sha256:fa76fbb7596cc5839320000cdd5d0955313696d9511debab7ee7278fc8b5c84a"},
|
||||
{file = "cryptography-42.0.8-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ba4f0a211697362e89ad822e667d8d340b4d8d55fae72cdd619389fb5912eefe"},
|
||||
{file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:81884c4d096c272f00aeb1f11cf62ccd39763581645b0812e99a91505fa48e0c"},
|
||||
{file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c9bb2ae11bfbab395bdd072985abde58ea9860ed84e59dbc0463a5d0159f5b71"},
|
||||
{file = "cryptography-42.0.8-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7016f837e15b0a1c119d27ecd89b3515f01f90a8615ed5e9427e30d9cdbfed3d"},
|
||||
{file = "cryptography-42.0.8-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5a94eccb2a81a309806027e1670a358b99b8fe8bfe9f8d329f27d72c094dde8c"},
|
||||
{file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dec9b018df185f08483f294cae6ccac29e7a6e0678996587363dc352dc65c842"},
|
||||
{file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:343728aac38decfdeecf55ecab3264b015be68fc2816ca800db649607aeee648"},
|
||||
{file = "cryptography-42.0.8-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:013629ae70b40af70c9a7a5db40abe5d9054e6f4380e50ce769947b73bf3caad"},
|
||||
{file = "cryptography-42.0.8.tar.gz", hash = "sha256:8d09d05439ce7baa8e9e95b07ec5b6c886f548deb7e0f69ef25f64b3bce842f2"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -1196,13 +1196,13 @@ bcrypt = ["bcrypt"]
|
||||
|
||||
[[package]]
|
||||
name = "django-cte"
|
||||
version = "1.3.2"
|
||||
version = "1.3.3"
|
||||
description = "Common Table Expressions (CTE) for Django"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "django-cte-1.3.2.tar.gz", hash = "sha256:841b264f83417d421393c8c4644f76300962c6c3ddbc37339cf99672eb3696a2"},
|
||||
{file = "django_cte-1.3.2-py2.py3-none-any.whl", hash = "sha256:9ca0a900df4819bbe86447a594f27272d67069f3ef13da61e905b0ace67704e9"},
|
||||
{file = "django-cte-1.3.3.tar.gz", hash = "sha256:0c1aeef067278a22886151c1d27f6f665a303952d058900e5ca82a24cde40697"},
|
||||
{file = "django_cte-1.3.3-py2.py3-none-any.whl", hash = "sha256:85bbc3efb30c2f8c9ae3080ca6f0b9570e43d2cb4b6be10846c8ef9f046873fa"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1707,13 +1707,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"]
|
||||
|
||||
[[package]]
|
||||
name = "google-api-python-client"
|
||||
version = "2.131.0"
|
||||
version = "2.133.0"
|
||||
description = "Google API Client Library for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "google-api-python-client-2.131.0.tar.gz", hash = "sha256:1c03e24af62238a8817ecc24e9d4c32ddd4cb1f323b08413652d9a9a592fc00d"},
|
||||
{file = "google_api_python_client-2.131.0-py2.py3-none-any.whl", hash = "sha256:e325409bdcef4604d505d9246ce7199960a010a0569ac503b9f319db8dbdc217"},
|
||||
{file = "google-api-python-client-2.133.0.tar.gz", hash = "sha256:293092905b66a046d3187a99ac454e12b00cc2c70444f26eb2f1f9c1a82720b4"},
|
||||
{file = "google_api_python_client-2.133.0-py2.py3-none-any.whl", hash = "sha256:396fe676ea0dfed066654dcf9f8dea77a1342f9d9bb23bb88e45b7b81e773926"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -2172,13 +2172,13 @@ zookeeper = ["kazoo (>=2.8.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "kubernetes"
|
||||
version = "29.0.0"
|
||||
version = "30.1.0"
|
||||
description = "Kubernetes python client"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "kubernetes-29.0.0-py2.py3-none-any.whl", hash = "sha256:ab8cb0e0576ccdfb71886366efb102c6a20f268d817be065ce7f9909c631e43e"},
|
||||
{file = "kubernetes-29.0.0.tar.gz", hash = "sha256:c4812e227ae74d07d53c88293e564e54b850452715a59a927e7e1bc6b9a60459"},
|
||||
{file = "kubernetes-30.1.0-py2.py3-none-any.whl", hash = "sha256:e212e8b7579031dd2e512168b617373bc1e03888d41ac4e04039240a292d478d"},
|
||||
{file = "kubernetes-30.1.0.tar.gz", hash = "sha256:41e4c77af9f28e7a6c314e3bd06a8c6229ddd787cad684e0ab9f69b498e98ebc"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -3019,13 +3019,13 @@ attrs = ">=19.2.0"
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "24.0"
|
||||
version = "24.1"
|
||||
description = "Core utilities for Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
|
||||
{file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
|
||||
{file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
|
||||
{file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3376,19 +3376,19 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.7.2"
|
||||
version = "2.7.4"
|
||||
description = "Data validation using Python type hints"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pydantic-2.7.2-py3-none-any.whl", hash = "sha256:834ab954175f94e6e68258537dc49402c4a5e9d0409b9f1b86b7e934a8372de7"},
|
||||
{file = "pydantic-2.7.2.tar.gz", hash = "sha256:71b2945998f9c9b7919a45bde9a50397b289937d215ae141c1d0903ba7149fd7"},
|
||||
{file = "pydantic-2.7.4-py3-none-any.whl", hash = "sha256:ee8538d41ccb9c0a9ad3e0e5f07bf15ed8015b481ced539a1759d8cc89ae90d0"},
|
||||
{file = "pydantic-2.7.4.tar.gz", hash = "sha256:0c84efd9548d545f63ac0060c1e4d39bb9b14db8b3c0652338aecc07b5adec52"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
annotated-types = ">=0.4.0"
|
||||
email-validator = {version = ">=2.0.0", optional = true, markers = "extra == \"email\""}
|
||||
pydantic-core = "2.18.3"
|
||||
pydantic-core = "2.18.4"
|
||||
typing-extensions = ">=4.6.1"
|
||||
|
||||
[package.extras]
|
||||
@ -3396,90 +3396,90 @@ email = ["email-validator (>=2.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic-core"
|
||||
version = "2.18.3"
|
||||
version = "2.18.4"
|
||||
description = "Core functionality for Pydantic validation and serialization"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pydantic_core-2.18.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:744697428fcdec6be5670460b578161d1ffe34743a5c15656be7ea82b008197c"},
|
||||
{file = "pydantic_core-2.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37b40c05ced1ba4218b14986fe6f283d22e1ae2ff4c8e28881a70fb81fbfcda7"},
|
||||
{file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:544a9a75622357076efb6b311983ff190fbfb3c12fc3a853122b34d3d358126c"},
|
||||
{file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e2e253af04ceaebde8eb201eb3f3e3e7e390f2d275a88300d6a1959d710539e2"},
|
||||
{file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:855ec66589c68aa367d989da5c4755bb74ee92ccad4fdb6af942c3612c067e34"},
|
||||
{file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d3e42bb54e7e9d72c13ce112e02eb1b3b55681ee948d748842171201a03a98a"},
|
||||
{file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6ac9ffccc9d2e69d9fba841441d4259cb668ac180e51b30d3632cd7abca2b9b"},
|
||||
{file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c56eca1686539fa0c9bda992e7bd6a37583f20083c37590413381acfc5f192d6"},
|
||||
{file = "pydantic_core-2.18.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:17954d784bf8abfc0ec2a633108207ebc4fa2df1a0e4c0c3ccbaa9bb01d2c426"},
|
||||
{file = "pydantic_core-2.18.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:98ed737567d8f2ecd54f7c8d4f8572ca7c7921ede93a2e52939416170d357812"},
|
||||
{file = "pydantic_core-2.18.3-cp310-none-win32.whl", hash = "sha256:9f9e04afebd3ed8c15d67a564ed0a34b54e52136c6d40d14c5547b238390e779"},
|
||||
{file = "pydantic_core-2.18.3-cp310-none-win_amd64.whl", hash = "sha256:45e4ffbae34f7ae30d0047697e724e534a7ec0a82ef9994b7913a412c21462a0"},
|
||||
{file = "pydantic_core-2.18.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b9ebe8231726c49518b16b237b9fe0d7d361dd221302af511a83d4ada01183ab"},
|
||||
{file = "pydantic_core-2.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b8e20e15d18bf7dbb453be78a2d858f946f5cdf06c5072453dace00ab652e2b2"},
|
||||
{file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0d9ff283cd3459fa0bf9b0256a2b6f01ac1ff9ffb034e24457b9035f75587cb"},
|
||||
{file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f7ef5f0ebb77ba24c9970da18b771711edc5feaf00c10b18461e0f5f5949231"},
|
||||
{file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73038d66614d2e5cde30435b5afdced2b473b4c77d4ca3a8624dd3e41a9c19be"},
|
||||
{file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6afd5c867a74c4d314c557b5ea9520183fadfbd1df4c2d6e09fd0d990ce412cd"},
|
||||
{file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd7df92f28d351bb9f12470f4c533cf03d1b52ec5a6e5c58c65b183055a60106"},
|
||||
{file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:80aea0ffeb1049336043d07799eace1c9602519fb3192916ff525b0287b2b1e4"},
|
||||
{file = "pydantic_core-2.18.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaee40f25bba38132e655ffa3d1998a6d576ba7cf81deff8bfa189fb43fd2bbe"},
|
||||
{file = "pydantic_core-2.18.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9128089da8f4fe73f7a91973895ebf2502539d627891a14034e45fb9e707e26d"},
|
||||
{file = "pydantic_core-2.18.3-cp311-none-win32.whl", hash = "sha256:fec02527e1e03257aa25b1a4dcbe697b40a22f1229f5d026503e8b7ff6d2eda7"},
|
||||
{file = "pydantic_core-2.18.3-cp311-none-win_amd64.whl", hash = "sha256:58ff8631dbab6c7c982e6425da8347108449321f61fe427c52ddfadd66642af7"},
|
||||
{file = "pydantic_core-2.18.3-cp311-none-win_arm64.whl", hash = "sha256:3fc1c7f67f34c6c2ef9c213e0f2a351797cda98249d9ca56a70ce4ebcaba45f4"},
|
||||
{file = "pydantic_core-2.18.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f0928cde2ae416a2d1ebe6dee324709c6f73e93494d8c7aea92df99aab1fc40f"},
|
||||
{file = "pydantic_core-2.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bee9bb305a562f8b9271855afb6ce00223f545de3d68560b3c1649c7c5295e9"},
|
||||
{file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e862823be114387257dacbfa7d78547165a85d7add33b446ca4f4fae92c7ff5c"},
|
||||
{file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6a36f78674cbddc165abab0df961b5f96b14461d05feec5e1f78da58808b97e7"},
|
||||
{file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba905d184f62e7ddbb7a5a751d8a5c805463511c7b08d1aca4a3e8c11f2e5048"},
|
||||
{file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fdd362f6a586e681ff86550b2379e532fee63c52def1c666887956748eaa326"},
|
||||
{file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24b214b7ee3bd3b865e963dbed0f8bc5375f49449d70e8d407b567af3222aae4"},
|
||||
{file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:691018785779766127f531674fa82bb368df5b36b461622b12e176c18e119022"},
|
||||
{file = "pydantic_core-2.18.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:60e4c625e6f7155d7d0dcac151edf5858102bc61bf959d04469ca6ee4e8381bd"},
|
||||
{file = "pydantic_core-2.18.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4e651e47d981c1b701dcc74ab8fec5a60a5b004650416b4abbef13db23bc7be"},
|
||||
{file = "pydantic_core-2.18.3-cp312-none-win32.whl", hash = "sha256:ffecbb5edb7f5ffae13599aec33b735e9e4c7676ca1633c60f2c606beb17efc5"},
|
||||
{file = "pydantic_core-2.18.3-cp312-none-win_amd64.whl", hash = "sha256:2c8333f6e934733483c7eddffdb094c143b9463d2af7e6bd85ebcb2d4a1b82c6"},
|
||||
{file = "pydantic_core-2.18.3-cp312-none-win_arm64.whl", hash = "sha256:7a20dded653e516a4655f4c98e97ccafb13753987434fe7cf044aa25f5b7d417"},
|
||||
{file = "pydantic_core-2.18.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:eecf63195be644b0396f972c82598cd15693550f0ff236dcf7ab92e2eb6d3522"},
|
||||
{file = "pydantic_core-2.18.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2c44efdd3b6125419c28821590d7ec891c9cb0dff33a7a78d9d5c8b6f66b9702"},
|
||||
{file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e59fca51ffbdd1638b3856779342ed69bcecb8484c1d4b8bdb237d0eb5a45e2"},
|
||||
{file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:70cf099197d6b98953468461d753563b28e73cf1eade2ffe069675d2657ed1d5"},
|
||||
{file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:63081a49dddc6124754b32a3774331467bfc3d2bd5ff8f10df36a95602560361"},
|
||||
{file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:370059b7883485c9edb9655355ff46d912f4b03b009d929220d9294c7fd9fd60"},
|
||||
{file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a64faeedfd8254f05f5cf6fc755023a7e1606af3959cfc1a9285744cc711044"},
|
||||
{file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19d2e725de0f90d8671f89e420d36c3dd97639b98145e42fcc0e1f6d492a46dc"},
|
||||
{file = "pydantic_core-2.18.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:67bc078025d70ec5aefe6200ef094576c9d86bd36982df1301c758a9fff7d7f4"},
|
||||
{file = "pydantic_core-2.18.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:adf952c3f4100e203cbaf8e0c907c835d3e28f9041474e52b651761dc248a3c0"},
|
||||
{file = "pydantic_core-2.18.3-cp38-none-win32.whl", hash = "sha256:9a46795b1f3beb167eaee91736d5d17ac3a994bf2215a996aed825a45f897558"},
|
||||
{file = "pydantic_core-2.18.3-cp38-none-win_amd64.whl", hash = "sha256:200ad4e3133cb99ed82342a101a5abf3d924722e71cd581cc113fe828f727fbc"},
|
||||
{file = "pydantic_core-2.18.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:304378b7bf92206036c8ddd83a2ba7b7d1a5b425acafff637172a3aa72ad7083"},
|
||||
{file = "pydantic_core-2.18.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c826870b277143e701c9ccf34ebc33ddb4d072612683a044e7cce2d52f6c3fef"},
|
||||
{file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e201935d282707394f3668380e41ccf25b5794d1b131cdd96b07f615a33ca4b1"},
|
||||
{file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5560dda746c44b48bf82b3d191d74fe8efc5686a9ef18e69bdabccbbb9ad9442"},
|
||||
{file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b32c2a1f8032570842257e4c19288eba9a2bba4712af542327de9a1204faff8"},
|
||||
{file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:929c24e9dea3990bc8bcd27c5f2d3916c0c86f5511d2caa69e0d5290115344a9"},
|
||||
{file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1a8376fef60790152564b0eab376b3e23dd6e54f29d84aad46f7b264ecca943"},
|
||||
{file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dccf3ef1400390ddd1fb55bf0632209d39140552d068ee5ac45553b556780e06"},
|
||||
{file = "pydantic_core-2.18.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:41dbdcb0c7252b58fa931fec47937edb422c9cb22528f41cb8963665c372caf6"},
|
||||
{file = "pydantic_core-2.18.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:666e45cf071669fde468886654742fa10b0e74cd0fa0430a46ba6056b24fb0af"},
|
||||
{file = "pydantic_core-2.18.3-cp39-none-win32.whl", hash = "sha256:f9c08cabff68704a1b4667d33f534d544b8a07b8e5d039c37067fceb18789e78"},
|
||||
{file = "pydantic_core-2.18.3-cp39-none-win_amd64.whl", hash = "sha256:4afa5f5973e8572b5c0dcb4e2d4fda7890e7cd63329bd5cc3263a25c92ef0026"},
|
||||
{file = "pydantic_core-2.18.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:77319771a026f7c7d29c6ebc623de889e9563b7087911b46fd06c044a12aa5e9"},
|
||||
{file = "pydantic_core-2.18.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:df11fa992e9f576473038510d66dd305bcd51d7dd508c163a8c8fe148454e059"},
|
||||
{file = "pydantic_core-2.18.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d531076bdfb65af593326ffd567e6ab3da145020dafb9187a1d131064a55f97c"},
|
||||
{file = "pydantic_core-2.18.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d33ce258e4e6e6038f2b9e8b8a631d17d017567db43483314993b3ca345dcbbb"},
|
||||
{file = "pydantic_core-2.18.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1f9cd7f5635b719939019be9bda47ecb56e165e51dd26c9a217a433e3d0d59a9"},
|
||||
{file = "pydantic_core-2.18.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cd4a032bb65cc132cae1fe3e52877daecc2097965cd3914e44fbd12b00dae7c5"},
|
||||
{file = "pydantic_core-2.18.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f2718430098bcdf60402136c845e4126a189959d103900ebabb6774a5d9fdb"},
|
||||
{file = "pydantic_core-2.18.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c0037a92cf0c580ed14e10953cdd26528e8796307bb8bb312dc65f71547df04d"},
|
||||
{file = "pydantic_core-2.18.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b95a0972fac2b1ff3c94629fc9081b16371dad870959f1408cc33b2f78ad347a"},
|
||||
{file = "pydantic_core-2.18.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a62e437d687cc148381bdd5f51e3e81f5b20a735c55f690c5be94e05da2b0d5c"},
|
||||
{file = "pydantic_core-2.18.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b367a73a414bbb08507da102dc2cde0fa7afe57d09b3240ce82a16d608a7679c"},
|
||||
{file = "pydantic_core-2.18.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ecce4b2360aa3f008da3327d652e74a0e743908eac306198b47e1c58b03dd2b"},
|
||||
{file = "pydantic_core-2.18.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd4435b8d83f0c9561a2a9585b1de78f1abb17cb0cef5f39bf6a4b47d19bafe3"},
|
||||
{file = "pydantic_core-2.18.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:616221a6d473c5b9aa83fa8982745441f6a4a62a66436be9445c65f241b86c94"},
|
||||
{file = "pydantic_core-2.18.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:7e6382ce89a92bc1d0c0c5edd51e931432202b9080dc921d8d003e616402efd1"},
|
||||
{file = "pydantic_core-2.18.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ff58f379345603d940e461eae474b6bbb6dab66ed9a851ecd3cb3709bf4dcf6a"},
|
||||
{file = "pydantic_core-2.18.3.tar.gz", hash = "sha256:432e999088d85c8f36b9a3f769a8e2b57aabd817bbb729a90d1fe7f18f6f1f39"},
|
||||
{file = "pydantic_core-2.18.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f76d0ad001edd426b92233d45c746fd08f467d56100fd8f30e9ace4b005266e4"},
|
||||
{file = "pydantic_core-2.18.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:59ff3e89f4eaf14050c8022011862df275b552caef8082e37b542b066ce1ff26"},
|
||||
{file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a55b5b16c839df1070bc113c1f7f94a0af4433fcfa1b41799ce7606e5c79ce0a"},
|
||||
{file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4d0dcc59664fcb8974b356fe0a18a672d6d7cf9f54746c05f43275fc48636851"},
|
||||
{file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8951eee36c57cd128f779e641e21eb40bc5073eb28b2d23f33eb0ef14ffb3f5d"},
|
||||
{file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4701b19f7e3a06ea655513f7938de6f108123bf7c86bbebb1196eb9bd35cf724"},
|
||||
{file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00a3f196329e08e43d99b79b286d60ce46bed10f2280d25a1718399457e06be"},
|
||||
{file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:97736815b9cc893b2b7f663628e63f436018b75f44854c8027040e05230eeddb"},
|
||||
{file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6891a2ae0e8692679c07728819b6e2b822fb30ca7445f67bbf6509b25a96332c"},
|
||||
{file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bc4ff9805858bd54d1a20efff925ccd89c9d2e7cf4986144b30802bf78091c3e"},
|
||||
{file = "pydantic_core-2.18.4-cp310-none-win32.whl", hash = "sha256:1b4de2e51bbcb61fdebd0ab86ef28062704f62c82bbf4addc4e37fa4b00b7cbc"},
|
||||
{file = "pydantic_core-2.18.4-cp310-none-win_amd64.whl", hash = "sha256:6a750aec7bf431517a9fd78cb93c97b9b0c496090fee84a47a0d23668976b4b0"},
|
||||
{file = "pydantic_core-2.18.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:942ba11e7dfb66dc70f9ae66b33452f51ac7bb90676da39a7345e99ffb55402d"},
|
||||
{file = "pydantic_core-2.18.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b2ebef0e0b4454320274f5e83a41844c63438fdc874ea40a8b5b4ecb7693f1c4"},
|
||||
{file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a642295cd0c8df1b86fc3dced1d067874c353a188dc8e0f744626d49e9aa51c4"},
|
||||
{file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f09baa656c904807e832cf9cce799c6460c450c4ad80803517032da0cd062e2"},
|
||||
{file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98906207f29bc2c459ff64fa007afd10a8c8ac080f7e4d5beff4c97086a3dabd"},
|
||||
{file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19894b95aacfa98e7cb093cd7881a0c76f55731efad31073db4521e2b6ff5b7d"},
|
||||
{file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fbbdc827fe5e42e4d196c746b890b3d72876bdbf160b0eafe9f0334525119c8"},
|
||||
{file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f85d05aa0918283cf29a30b547b4df2fbb56b45b135f9e35b6807cb28bc47951"},
|
||||
{file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e85637bc8fe81ddb73fda9e56bab24560bdddfa98aa64f87aaa4e4b6730c23d2"},
|
||||
{file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2f5966897e5461f818e136b8451d0551a2e77259eb0f73a837027b47dc95dab9"},
|
||||
{file = "pydantic_core-2.18.4-cp311-none-win32.whl", hash = "sha256:44c7486a4228413c317952e9d89598bcdfb06399735e49e0f8df643e1ccd0558"},
|
||||
{file = "pydantic_core-2.18.4-cp311-none-win_amd64.whl", hash = "sha256:8a7164fe2005d03c64fd3b85649891cd4953a8de53107940bf272500ba8a788b"},
|
||||
{file = "pydantic_core-2.18.4-cp311-none-win_arm64.whl", hash = "sha256:4e99bc050fe65c450344421017f98298a97cefc18c53bb2f7b3531eb39bc7805"},
|
||||
{file = "pydantic_core-2.18.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6f5c4d41b2771c730ea1c34e458e781b18cc668d194958e0112455fff4e402b2"},
|
||||
{file = "pydantic_core-2.18.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2fdf2156aa3d017fddf8aea5adfba9f777db1d6022d392b682d2a8329e087cef"},
|
||||
{file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4748321b5078216070b151d5271ef3e7cc905ab170bbfd27d5c83ee3ec436695"},
|
||||
{file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847a35c4d58721c5dc3dba599878ebbdfd96784f3fb8bb2c356e123bdcd73f34"},
|
||||
{file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c40d4eaad41f78e3bbda31b89edc46a3f3dc6e171bf0ecf097ff7a0ffff7cb1"},
|
||||
{file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:21a5e440dbe315ab9825fcd459b8814bb92b27c974cbc23c3e8baa2b76890077"},
|
||||
{file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01dd777215e2aa86dfd664daed5957704b769e726626393438f9c87690ce78c3"},
|
||||
{file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4b06beb3b3f1479d32befd1f3079cc47b34fa2da62457cdf6c963393340b56e9"},
|
||||
{file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:564d7922e4b13a16b98772441879fcdcbe82ff50daa622d681dd682175ea918c"},
|
||||
{file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0eb2a4f660fcd8e2b1c90ad566db2b98d7f3f4717c64fe0a83e0adb39766d5b8"},
|
||||
{file = "pydantic_core-2.18.4-cp312-none-win32.whl", hash = "sha256:8b8bab4c97248095ae0c4455b5a1cd1cdd96e4e4769306ab19dda135ea4cdb07"},
|
||||
{file = "pydantic_core-2.18.4-cp312-none-win_amd64.whl", hash = "sha256:14601cdb733d741b8958224030e2bfe21a4a881fb3dd6fbb21f071cabd48fa0a"},
|
||||
{file = "pydantic_core-2.18.4-cp312-none-win_arm64.whl", hash = "sha256:c1322d7dd74713dcc157a2b7898a564ab091ca6c58302d5c7b4c07296e3fd00f"},
|
||||
{file = "pydantic_core-2.18.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:823be1deb01793da05ecb0484d6c9e20baebb39bd42b5d72636ae9cf8350dbd2"},
|
||||
{file = "pydantic_core-2.18.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ebef0dd9bf9b812bf75bda96743f2a6c5734a02092ae7f721c048d156d5fabae"},
|
||||
{file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae1d6df168efb88d7d522664693607b80b4080be6750c913eefb77e34c12c71a"},
|
||||
{file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f9899c94762343f2cc2fc64c13e7cae4c3cc65cdfc87dd810a31654c9b7358cc"},
|
||||
{file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99457f184ad90235cfe8461c4d70ab7dd2680e28821c29eca00252ba90308c78"},
|
||||
{file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18f469a3d2a2fdafe99296a87e8a4c37748b5080a26b806a707f25a902c040a8"},
|
||||
{file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7cdf28938ac6b8b49ae5e92f2735056a7ba99c9b110a474473fd71185c1af5d"},
|
||||
{file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:938cb21650855054dc54dfd9120a851c974f95450f00683399006aa6e8abb057"},
|
||||
{file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:44cd83ab6a51da80fb5adbd9560e26018e2ac7826f9626bc06ca3dc074cd198b"},
|
||||
{file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:972658f4a72d02b8abfa2581d92d59f59897d2e9f7e708fdabe922f9087773af"},
|
||||
{file = "pydantic_core-2.18.4-cp38-none-win32.whl", hash = "sha256:1d886dc848e60cb7666f771e406acae54ab279b9f1e4143babc9c2258213daa2"},
|
||||
{file = "pydantic_core-2.18.4-cp38-none-win_amd64.whl", hash = "sha256:bb4462bd43c2460774914b8525f79b00f8f407c945d50881568f294c1d9b4443"},
|
||||
{file = "pydantic_core-2.18.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:44a688331d4a4e2129140a8118479443bd6f1905231138971372fcde37e43528"},
|
||||
{file = "pydantic_core-2.18.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a2fdd81edd64342c85ac7cf2753ccae0b79bf2dfa063785503cb85a7d3593223"},
|
||||
{file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86110d7e1907ab36691f80b33eb2da87d780f4739ae773e5fc83fb272f88825f"},
|
||||
{file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46387e38bd641b3ee5ce247563b60c5ca098da9c56c75c157a05eaa0933ed154"},
|
||||
{file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:123c3cec203e3f5ac7b000bd82235f1a3eced8665b63d18be751f115588fea30"},
|
||||
{file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc1803ac5c32ec324c5261c7209e8f8ce88e83254c4e1aebdc8b0a39f9ddb443"},
|
||||
{file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53db086f9f6ab2b4061958d9c276d1dbe3690e8dd727d6abf2321d6cce37fa94"},
|
||||
{file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abc267fa9837245cc28ea6929f19fa335f3dc330a35d2e45509b6566dc18be23"},
|
||||
{file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a0d829524aaefdebccb869eed855e2d04c21d2d7479b6cada7ace5448416597b"},
|
||||
{file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:509daade3b8649f80d4e5ff21aa5673e4ebe58590b25fe42fac5f0f52c6f034a"},
|
||||
{file = "pydantic_core-2.18.4-cp39-none-win32.whl", hash = "sha256:ca26a1e73c48cfc54c4a76ff78df3727b9d9f4ccc8dbee4ae3f73306a591676d"},
|
||||
{file = "pydantic_core-2.18.4-cp39-none-win_amd64.whl", hash = "sha256:c67598100338d5d985db1b3d21f3619ef392e185e71b8d52bceacc4a7771ea7e"},
|
||||
{file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:574d92eac874f7f4db0ca653514d823a0d22e2354359d0759e3f6a406db5d55d"},
|
||||
{file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1f4d26ceb5eb9eed4af91bebeae4b06c3fb28966ca3a8fb765208cf6b51102ab"},
|
||||
{file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77450e6d20016ec41f43ca4a6c63e9fdde03f0ae3fe90e7c27bdbeaece8b1ed4"},
|
||||
{file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d323a01da91851a4f17bf592faf46149c9169d68430b3146dcba2bb5e5719abc"},
|
||||
{file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43d447dd2ae072a0065389092a231283f62d960030ecd27565672bd40746c507"},
|
||||
{file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:578e24f761f3b425834f297b9935e1ce2e30f51400964ce4801002435a1b41ef"},
|
||||
{file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:81b5efb2f126454586d0f40c4d834010979cb80785173d1586df845a632e4e6d"},
|
||||
{file = "pydantic_core-2.18.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ab86ce7c8f9bea87b9d12c7f0af71102acbf5ecbc66c17796cff45dae54ef9a5"},
|
||||
{file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:90afc12421df2b1b4dcc975f814e21bc1754640d502a2fbcc6d41e77af5ec312"},
|
||||
{file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:51991a89639a912c17bef4b45c87bd83593aee0437d8102556af4885811d59f5"},
|
||||
{file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:293afe532740370aba8c060882f7d26cfd00c94cae32fd2e212a3a6e3b7bc15e"},
|
||||
{file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48ece5bde2e768197a2d0f6e925f9d7e3e826f0ad2271120f8144a9db18d5c8"},
|
||||
{file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eae237477a873ab46e8dd748e515c72c0c804fb380fbe6c85533c7de51f23a8f"},
|
||||
{file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:834b5230b5dfc0c1ec37b2fda433b271cbbc0e507560b5d1588e2cc1148cf1ce"},
|
||||
{file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e858ac0a25074ba4bce653f9b5d0a85b7456eaddadc0ce82d3878c22489fa4ee"},
|
||||
{file = "pydantic_core-2.18.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2fd41f6eff4c20778d717af1cc50eca52f5afe7805ee530a4fbd0bae284f16e9"},
|
||||
{file = "pydantic_core-2.18.4.tar.gz", hash = "sha256:ec3beeada09ff865c344ff3bc2f427f5e6c26401cc6113d77e372c3fdac73864"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -3624,13 +3624,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "8.2.1"
|
||||
version = "8.2.2"
|
||||
description = "pytest: simple powerful testing with Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pytest-8.2.1-py3-none-any.whl", hash = "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1"},
|
||||
{file = "pytest-8.2.1.tar.gz", hash = "sha256:5046e5b46d8e4cac199c373041f26be56fdb81eb4e67dc11d4e10811fc3408fd"},
|
||||
{file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"},
|
||||
{file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -4052,28 +4052,28 @@ pyasn1 = ">=0.1.3"
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.4.7"
|
||||
version = "0.4.8"
|
||||
description = "An extremely fast Python linter and code formatter, written in Rust."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "ruff-0.4.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e089371c67892a73b6bb1525608e89a2aca1b77b5440acf7a71dda5dac958f9e"},
|
||||
{file = "ruff-0.4.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:10f973d521d910e5f9c72ab27e409e839089f955be8a4c8826601a6323a89753"},
|
||||
{file = "ruff-0.4.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59c3d110970001dfa494bcd95478e62286c751126dfb15c3c46e7915fc49694f"},
|
||||
{file = "ruff-0.4.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa9773c6c00f4958f73b317bc0fd125295110c3776089f6ef318f4b775f0abe4"},
|
||||
{file = "ruff-0.4.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07fc80bbb61e42b3b23b10fda6a2a0f5a067f810180a3760c5ef1b456c21b9db"},
|
||||
{file = "ruff-0.4.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:fa4dafe3fe66d90e2e2b63fa1591dd6e3f090ca2128daa0be33db894e6c18648"},
|
||||
{file = "ruff-0.4.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7c0083febdec17571455903b184a10026603a1de078428ba155e7ce9358c5f6"},
|
||||
{file = "ruff-0.4.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad1b20e66a44057c326168437d680a2166c177c939346b19c0d6b08a62a37589"},
|
||||
{file = "ruff-0.4.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbf5d818553add7511c38b05532d94a407f499d1a76ebb0cad0374e32bc67202"},
|
||||
{file = "ruff-0.4.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:50e9651578b629baec3d1513b2534de0ac7ed7753e1382272b8d609997e27e83"},
|
||||
{file = "ruff-0.4.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8874a9df7766cb956b218a0a239e0a5d23d9e843e4da1e113ae1d27ee420877a"},
|
||||
{file = "ruff-0.4.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b9de9a6e49f7d529decd09381c0860c3f82fa0b0ea00ea78409b785d2308a567"},
|
||||
{file = "ruff-0.4.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:13a1768b0691619822ae6d446132dbdfd568b700ecd3652b20d4e8bc1e498f78"},
|
||||
{file = "ruff-0.4.7-py3-none-win32.whl", hash = "sha256:769e5a51df61e07e887b81e6f039e7ed3573316ab7dd9f635c5afaa310e4030e"},
|
||||
{file = "ruff-0.4.7-py3-none-win_amd64.whl", hash = "sha256:9e3ab684ad403a9ed1226894c32c3ab9c2e0718440f6f50c7c5829932bc9e054"},
|
||||
{file = "ruff-0.4.7-py3-none-win_arm64.whl", hash = "sha256:10f2204b9a613988e3484194c2c9e96a22079206b22b787605c255f130db5ed7"},
|
||||
{file = "ruff-0.4.7.tar.gz", hash = "sha256:2331d2b051dc77a289a653fcc6a42cce357087c5975738157cd966590b18b5e1"},
|
||||
{file = "ruff-0.4.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7663a6d78f6adb0eab270fa9cf1ff2d28618ca3a652b60f2a234d92b9ec89066"},
|
||||
{file = "ruff-0.4.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eeceb78da8afb6de0ddada93112869852d04f1cd0f6b80fe464fd4e35c330913"},
|
||||
{file = "ruff-0.4.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aad360893e92486662ef3be0a339c5ca3c1b109e0134fcd37d534d4be9fb8de3"},
|
||||
{file = "ruff-0.4.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:284c2e3f3396fb05f5f803c9fffb53ebbe09a3ebe7dda2929ed8d73ded736deb"},
|
||||
{file = "ruff-0.4.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7354f921e3fbe04d2a62d46707e569f9315e1a613307f7311a935743c51a764"},
|
||||
{file = "ruff-0.4.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:72584676164e15a68a15778fd1b17c28a519e7a0622161eb2debdcdabdc71883"},
|
||||
{file = "ruff-0.4.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9678d5c9b43315f323af2233a04d747409d1e3aa6789620083a82d1066a35199"},
|
||||
{file = "ruff-0.4.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704977a658131651a22b5ebeb28b717ef42ac6ee3b11e91dc87b633b5d83142b"},
|
||||
{file = "ruff-0.4.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d05f8d6f0c3cce5026cecd83b7a143dcad503045857bc49662f736437380ad45"},
|
||||
{file = "ruff-0.4.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6ea874950daca5697309d976c9afba830d3bf0ed66887481d6bca1673fc5b66a"},
|
||||
{file = "ruff-0.4.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:fc95aac2943ddf360376be9aa3107c8cf9640083940a8c5bd824be692d2216dc"},
|
||||
{file = "ruff-0.4.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:384154a1c3f4bf537bac69f33720957ee49ac8d484bfc91720cc94172026ceed"},
|
||||
{file = "ruff-0.4.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e9d5ce97cacc99878aa0d084c626a15cd21e6b3d53fd6f9112b7fc485918e1fa"},
|
||||
{file = "ruff-0.4.8-py3-none-win32.whl", hash = "sha256:6d795d7639212c2dfd01991259460101c22aabf420d9b943f153ab9d9706e6a9"},
|
||||
{file = "ruff-0.4.8-py3-none-win_amd64.whl", hash = "sha256:e14a3a095d07560a9d6769a72f781d73259655919d9b396c650fc98a8157555d"},
|
||||
{file = "ruff-0.4.8-py3-none-win_arm64.whl", hash = "sha256:14019a06dbe29b608f6b7cbcec300e3170a8d86efaddb7b23405cb7f7dcaf780"},
|
||||
{file = "ruff-0.4.8.tar.gz", hash = "sha256:16d717b1d57b2e2fd68bd0bf80fb43931b79d05a7131aa477d66fc40fbd86268"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4130,13 +4130,13 @@ urllib3 = {version = ">=1.26,<3", extras = ["socks"]}
|
||||
|
||||
[[package]]
|
||||
name = "sentry-sdk"
|
||||
version = "2.3.1"
|
||||
version = "2.5.1"
|
||||
description = "Python client for Sentry (https://sentry.io)"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "sentry_sdk-2.3.1-py2.py3-none-any.whl", hash = "sha256:c5aeb095ba226391d337dd42a6f9470d86c9fc236ecc71cfc7cd1942b45010c6"},
|
||||
{file = "sentry_sdk-2.3.1.tar.gz", hash = "sha256:139a71a19f5e9eb5d3623942491ce03cf8ebc14ea2e39ba3e6fe79560d8a5b1f"},
|
||||
{file = "sentry_sdk-2.5.1-py2.py3-none-any.whl", hash = "sha256:1f87acdce4a43a523ae5aa21a3fc37522d73ebd9ec04b1dbf01aa3d173852def"},
|
||||
{file = "sentry_sdk-2.5.1.tar.gz", hash = "sha256:fbc40a78a8a9c6675133031116144f0d0940376fa6e4e1acd5624c90b0aaf58b"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -4451,22 +4451,22 @@ celery = "*"
|
||||
|
||||
[[package]]
|
||||
name = "tornado"
|
||||
version = "6.4"
|
||||
version = "6.4.1"
|
||||
description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed."
|
||||
optional = false
|
||||
python-versions = ">= 3.8"
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "tornado-6.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:02ccefc7d8211e5a7f9e8bc3f9e5b0ad6262ba2fbb683a6443ecc804e5224ce0"},
|
||||
{file = "tornado-6.4-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:27787de946a9cffd63ce5814c33f734c627a87072ec7eed71f7fc4417bb16263"},
|
||||
{file = "tornado-6.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e"},
|
||||
{file = "tornado-6.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43bc2e5370a6a8e413e1e1cd0c91bedc5bd62a74a532371042a18ef19e10579"},
|
||||
{file = "tornado-6.4-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0251554cdd50b4b44362f73ad5ba7126fc5b2c2895cc62b14a1c2d7ea32f212"},
|
||||
{file = "tornado-6.4-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2"},
|
||||
{file = "tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:88b84956273fbd73420e6d4b8d5ccbe913c65d31351b4c004ae362eba06e1f78"},
|
||||
{file = "tornado-6.4-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:71ddfc23a0e03ef2df1c1397d859868d158c8276a0603b96cf86892bff58149f"},
|
||||
{file = "tornado-6.4-cp38-abi3-win32.whl", hash = "sha256:6f8a6c77900f5ae93d8b4ae1196472d0ccc2775cc1dfdc9e7727889145c45052"},
|
||||
{file = "tornado-6.4-cp38-abi3-win_amd64.whl", hash = "sha256:10aeaa8006333433da48dec9fe417877f8bcc21f48dda8d661ae79da357b2a63"},
|
||||
{file = "tornado-6.4.tar.gz", hash = "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee"},
|
||||
{file = "tornado-6.4.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:163b0aafc8e23d8cdc3c9dfb24c5368af84a81e3364745ccb4427669bf84aec8"},
|
||||
{file = "tornado-6.4.1-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6d5ce3437e18a2b66fbadb183c1d3364fb03f2be71299e7d10dbeeb69f4b2a14"},
|
||||
{file = "tornado-6.4.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e20b9113cd7293f164dc46fffb13535266e713cdb87bd2d15ddb336e96cfc4"},
|
||||
{file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ae50a504a740365267b2a8d1a90c9fbc86b780a39170feca9bcc1787ff80842"},
|
||||
{file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:613bf4ddf5c7a95509218b149b555621497a6cc0d46ac341b30bd9ec19eac7f3"},
|
||||
{file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25486eb223babe3eed4b8aecbac33b37e3dd6d776bc730ca14e1bf93888b979f"},
|
||||
{file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:454db8a7ecfcf2ff6042dde58404164d969b6f5d58b926da15e6b23817950fc4"},
|
||||
{file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a02a08cc7a9314b006f653ce40483b9b3c12cda222d6a46d4ac63bb6c9057698"},
|
||||
{file = "tornado-6.4.1-cp38-abi3-win32.whl", hash = "sha256:d9a566c40b89757c9aa8e6f032bcdb8ca8795d7c1a9762910c722b1635c9de4d"},
|
||||
{file = "tornado-6.4.1-cp38-abi3-win_amd64.whl", hash = "sha256:b24b8982ed444378d7f21d563f4180a2de31ced9d8d84443907a0a64da2072e7"},
|
||||
{file = "tornado-6.4.1.tar.gz", hash = "sha256:92d3ab53183d8c50f8204a51e6f91d18a15d5ef261e84d452800d4ff6fc504e9"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4505,13 +4505,13 @@ wsproto = ">=0.14"
|
||||
|
||||
[[package]]
|
||||
name = "twilio"
|
||||
version = "9.1.0"
|
||||
version = "9.1.1"
|
||||
description = "Twilio API client and TwiML generator"
|
||||
optional = false
|
||||
python-versions = ">=3.7.0"
|
||||
files = [
|
||||
{file = "twilio-9.1.0-py2.py3-none-any.whl", hash = "sha256:eb4687a9f81dc3118e8981c5a46d9f8184baee135c79afed47c714c759c31bbc"},
|
||||
{file = "twilio-9.1.0.tar.gz", hash = "sha256:ab2eb19c779855bf02cdca8a7e02ebaa64feee47da7b591ac9088ec07a6962e2"},
|
||||
{file = "twilio-9.1.1-py2.py3-none-any.whl", hash = "sha256:cc3e090c3884db7d70e7c647358b9cf1f4d30fd3fbe0412adcae0df8459d29b0"},
|
||||
{file = "twilio-9.1.1.tar.gz", hash = "sha256:cfe72b12cabac2f0997f1060d53cea14bd1196e2cbda14789e53c7dd762c4349"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "authentik"
|
||||
version = "2024.4.2"
|
||||
version = "2024.6.0"
|
||||
description = ""
|
||||
authors = ["authentik Team <hello@goauthentik.io>"]
|
||||
|
||||
|
576
schema.yml
576
schema.yml
@ -1,7 +1,7 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: authentik
|
||||
version: 2024.4.2
|
||||
version: 2024.6.0
|
||||
description: Making authentication simple.
|
||||
contact:
|
||||
email: hello@goauthentik.io
|
||||
@ -19726,6 +19726,403 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/providers/scim_groups/:
|
||||
get:
|
||||
operationId: providers_scim_groups_list
|
||||
description: SCIMProviderGroup Viewset
|
||||
parameters:
|
||||
- in: query
|
||||
name: group__group_uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- in: query
|
||||
name: group__name
|
||||
schema:
|
||||
type: string
|
||||
- name: ordering
|
||||
required: false
|
||||
in: query
|
||||
description: Which field to use when ordering the results.
|
||||
schema:
|
||||
type: string
|
||||
- name: page
|
||||
required: false
|
||||
in: query
|
||||
description: A page number within the paginated result set.
|
||||
schema:
|
||||
type: integer
|
||||
- name: page_size
|
||||
required: false
|
||||
in: query
|
||||
description: Number of results to return per page.
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: provider__id
|
||||
schema:
|
||||
type: integer
|
||||
- name: search
|
||||
required: false
|
||||
in: query
|
||||
description: A search term.
|
||||
schema:
|
||||
type: string
|
||||
tags:
|
||||
- providers
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PaginatedSCIMProviderGroupList'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
post:
|
||||
operationId: providers_scim_groups_create
|
||||
description: SCIMProviderGroup Viewset
|
||||
tags:
|
||||
- providers
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SCIMProviderGroupRequest'
|
||||
required: true
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'201':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SCIMProviderGroup'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/providers/scim_groups/{id}/:
|
||||
get:
|
||||
operationId: providers_scim_groups_retrieve
|
||||
description: SCIMProviderGroup Viewset
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this scim provider group.
|
||||
required: true
|
||||
tags:
|
||||
- providers
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SCIMProviderGroup'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
delete:
|
||||
operationId: providers_scim_groups_destroy
|
||||
description: SCIMProviderGroup Viewset
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this scim provider group.
|
||||
required: true
|
||||
tags:
|
||||
- providers
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'204':
|
||||
description: No response body
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/providers/scim_groups/{id}/used_by/:
|
||||
get:
|
||||
operationId: providers_scim_groups_used_by_list
|
||||
description: Get a list of all objects that use this object
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this scim provider group.
|
||||
required: true
|
||||
tags:
|
||||
- providers
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/UsedBy'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/providers/scim_users/:
|
||||
get:
|
||||
operationId: providers_scim_users_list
|
||||
description: SCIMProviderUser Viewset
|
||||
parameters:
|
||||
- name: ordering
|
||||
required: false
|
||||
in: query
|
||||
description: Which field to use when ordering the results.
|
||||
schema:
|
||||
type: string
|
||||
- name: page
|
||||
required: false
|
||||
in: query
|
||||
description: A page number within the paginated result set.
|
||||
schema:
|
||||
type: integer
|
||||
- name: page_size
|
||||
required: false
|
||||
in: query
|
||||
description: Number of results to return per page.
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: provider__id
|
||||
schema:
|
||||
type: integer
|
||||
- name: search
|
||||
required: false
|
||||
in: query
|
||||
description: A search term.
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: user__id
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: user__username
|
||||
schema:
|
||||
type: string
|
||||
tags:
|
||||
- providers
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PaginatedSCIMProviderUserList'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
post:
|
||||
operationId: providers_scim_users_create
|
||||
description: SCIMProviderUser Viewset
|
||||
tags:
|
||||
- providers
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SCIMProviderUserRequest'
|
||||
required: true
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'201':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SCIMProviderUser'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/providers/scim_users/{id}/:
|
||||
get:
|
||||
operationId: providers_scim_users_retrieve
|
||||
description: SCIMProviderUser Viewset
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this scim provider user.
|
||||
required: true
|
||||
tags:
|
||||
- providers
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SCIMProviderUser'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
delete:
|
||||
operationId: providers_scim_users_destroy
|
||||
description: SCIMProviderUser Viewset
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this scim provider user.
|
||||
required: true
|
||||
tags:
|
||||
- providers
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'204':
|
||||
description: No response body
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/providers/scim_users/{id}/used_by/:
|
||||
get:
|
||||
operationId: providers_scim_users_used_by_list
|
||||
description: Get a list of all objects that use this object
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this scim provider user.
|
||||
required: true
|
||||
tags:
|
||||
- providers
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/UsedBy'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/rac/connection_tokens/:
|
||||
get:
|
||||
operationId: rac_connection_tokens_list
|
||||
@ -34067,6 +34464,18 @@ components:
|
||||
type: string
|
||||
api_url:
|
||||
type: string
|
||||
score_min_threshold:
|
||||
type: number
|
||||
format: double
|
||||
score_max_threshold:
|
||||
type: number
|
||||
format: double
|
||||
error_on_invalid_score:
|
||||
type: boolean
|
||||
description: When enabled and the received captcha score is outside of the
|
||||
given threshold, the stage will show an error message. When not enabled,
|
||||
the flow will continue, but the data from the captcha will be available
|
||||
in the context for policy decisions
|
||||
required:
|
||||
- component
|
||||
- meta_model_name
|
||||
@ -34101,6 +34510,18 @@ components:
|
||||
api_url:
|
||||
type: string
|
||||
minLength: 1
|
||||
score_min_threshold:
|
||||
type: number
|
||||
format: double
|
||||
score_max_threshold:
|
||||
type: number
|
||||
format: double
|
||||
error_on_invalid_score:
|
||||
type: boolean
|
||||
description: When enabled and the received captcha score is outside of the
|
||||
given threshold, the stage will show an error message. When not enabled,
|
||||
the flow will continue, but the data from the captcha will be available
|
||||
in the context for policy decisions
|
||||
required:
|
||||
- name
|
||||
- private_key
|
||||
@ -36311,6 +36732,8 @@ components:
|
||||
type: string
|
||||
format: uuid
|
||||
readOnly: true
|
||||
google_id:
|
||||
type: string
|
||||
group:
|
||||
type: string
|
||||
format: uuid
|
||||
@ -36324,6 +36747,7 @@ components:
|
||||
readOnly: true
|
||||
required:
|
||||
- attributes
|
||||
- google_id
|
||||
- group
|
||||
- group_obj
|
||||
- id
|
||||
@ -36332,12 +36756,16 @@ components:
|
||||
type: object
|
||||
description: GoogleWorkspaceProviderGroup Serializer
|
||||
properties:
|
||||
google_id:
|
||||
type: string
|
||||
minLength: 1
|
||||
group:
|
||||
type: string
|
||||
format: uuid
|
||||
provider:
|
||||
type: integer
|
||||
required:
|
||||
- google_id
|
||||
- group
|
||||
- provider
|
||||
GoogleWorkspaceProviderMapping:
|
||||
@ -36460,6 +36888,8 @@ components:
|
||||
type: string
|
||||
format: uuid
|
||||
readOnly: true
|
||||
google_id:
|
||||
type: string
|
||||
user:
|
||||
type: integer
|
||||
user_obj:
|
||||
@ -36472,6 +36902,7 @@ components:
|
||||
readOnly: true
|
||||
required:
|
||||
- attributes
|
||||
- google_id
|
||||
- id
|
||||
- provider
|
||||
- user
|
||||
@ -36480,11 +36911,15 @@ components:
|
||||
type: object
|
||||
description: GoogleWorkspaceProviderUser Serializer
|
||||
properties:
|
||||
google_id:
|
||||
type: string
|
||||
minLength: 1
|
||||
user:
|
||||
type: integer
|
||||
provider:
|
||||
type: integer
|
||||
required:
|
||||
- google_id
|
||||
- provider
|
||||
- user
|
||||
Group:
|
||||
@ -37979,6 +38414,8 @@ components:
|
||||
type: string
|
||||
format: uuid
|
||||
readOnly: true
|
||||
microsoft_id:
|
||||
type: string
|
||||
group:
|
||||
type: string
|
||||
format: uuid
|
||||
@ -37995,11 +38432,15 @@ components:
|
||||
- group
|
||||
- group_obj
|
||||
- id
|
||||
- microsoft_id
|
||||
- provider
|
||||
MicrosoftEntraProviderGroupRequest:
|
||||
type: object
|
||||
description: MicrosoftEntraProviderGroup Serializer
|
||||
properties:
|
||||
microsoft_id:
|
||||
type: string
|
||||
minLength: 1
|
||||
group:
|
||||
type: string
|
||||
format: uuid
|
||||
@ -38007,6 +38448,7 @@ components:
|
||||
type: integer
|
||||
required:
|
||||
- group
|
||||
- microsoft_id
|
||||
- provider
|
||||
MicrosoftEntraProviderMapping:
|
||||
type: object
|
||||
@ -38125,6 +38567,8 @@ components:
|
||||
type: string
|
||||
format: uuid
|
||||
readOnly: true
|
||||
microsoft_id:
|
||||
type: string
|
||||
user:
|
||||
type: integer
|
||||
user_obj:
|
||||
@ -38138,6 +38582,7 @@ components:
|
||||
required:
|
||||
- attributes
|
||||
- id
|
||||
- microsoft_id
|
||||
- provider
|
||||
- user
|
||||
- user_obj
|
||||
@ -38145,11 +38590,15 @@ components:
|
||||
type: object
|
||||
description: MicrosoftEntraProviderUser Serializer
|
||||
properties:
|
||||
microsoft_id:
|
||||
type: string
|
||||
minLength: 1
|
||||
user:
|
||||
type: integer
|
||||
provider:
|
||||
type: integer
|
||||
required:
|
||||
- microsoft_id
|
||||
- provider
|
||||
- user
|
||||
ModelEnum:
|
||||
@ -39098,6 +39547,8 @@ components:
|
||||
readOnly: true
|
||||
fips_enabled:
|
||||
type: boolean
|
||||
nullable: true
|
||||
description: Get FIPS enabled
|
||||
readOnly: true
|
||||
version_should:
|
||||
type: string
|
||||
@ -40142,6 +40593,18 @@ components:
|
||||
required:
|
||||
- pagination
|
||||
- results
|
||||
PaginatedSCIMProviderGroupList:
|
||||
type: object
|
||||
properties:
|
||||
pagination:
|
||||
$ref: '#/components/schemas/Pagination'
|
||||
results:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/SCIMProviderGroup'
|
||||
required:
|
||||
- pagination
|
||||
- results
|
||||
PaginatedSCIMProviderList:
|
||||
type: object
|
||||
properties:
|
||||
@ -40154,6 +40617,18 @@ components:
|
||||
required:
|
||||
- pagination
|
||||
- results
|
||||
PaginatedSCIMProviderUserList:
|
||||
type: object
|
||||
properties:
|
||||
pagination:
|
||||
$ref: '#/components/schemas/Pagination'
|
||||
results:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/SCIMProviderUser'
|
||||
required:
|
||||
- pagination
|
||||
- results
|
||||
PaginatedSCIMSourceGroupList:
|
||||
type: object
|
||||
properties:
|
||||
@ -41182,6 +41657,18 @@ components:
|
||||
api_url:
|
||||
type: string
|
||||
minLength: 1
|
||||
score_min_threshold:
|
||||
type: number
|
||||
format: double
|
||||
score_max_threshold:
|
||||
type: number
|
||||
format: double
|
||||
error_on_invalid_score:
|
||||
type: boolean
|
||||
description: When enabled and the received captcha score is outside of the
|
||||
given threshold, the stage will show an error message. When not enabled,
|
||||
the flow will continue, but the data from the captcha will be available
|
||||
in the context for policy decisions
|
||||
PatchedCertificateKeyPairRequest:
|
||||
type: object
|
||||
description: CertificateKeyPair Serializer
|
||||
@ -44111,12 +44598,14 @@ components:
|
||||
properties:
|
||||
user:
|
||||
type: integer
|
||||
nullable: true
|
||||
context:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
group:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
PropertyMappingTestResult:
|
||||
type: object
|
||||
description: Result of a Property-mapping test
|
||||
@ -45870,6 +46359,47 @@ components:
|
||||
- url
|
||||
- verbose_name
|
||||
- verbose_name_plural
|
||||
SCIMProviderGroup:
|
||||
type: object
|
||||
description: SCIMProviderGroup Serializer
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
readOnly: true
|
||||
scim_id:
|
||||
type: string
|
||||
group:
|
||||
type: string
|
||||
format: uuid
|
||||
group_obj:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/UserGroup'
|
||||
readOnly: true
|
||||
provider:
|
||||
type: integer
|
||||
required:
|
||||
- group
|
||||
- group_obj
|
||||
- id
|
||||
- provider
|
||||
- scim_id
|
||||
SCIMProviderGroupRequest:
|
||||
type: object
|
||||
description: SCIMProviderGroup Serializer
|
||||
properties:
|
||||
scim_id:
|
||||
type: string
|
||||
minLength: 1
|
||||
group:
|
||||
type: string
|
||||
format: uuid
|
||||
provider:
|
||||
type: integer
|
||||
required:
|
||||
- group
|
||||
- provider
|
||||
- scim_id
|
||||
SCIMProviderRequest:
|
||||
type: object
|
||||
description: SCIMProvider Serializer
|
||||
@ -45906,6 +46436,45 @@ components:
|
||||
- name
|
||||
- token
|
||||
- url
|
||||
SCIMProviderUser:
|
||||
type: object
|
||||
description: SCIMProviderUser Serializer
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
readOnly: true
|
||||
scim_id:
|
||||
type: string
|
||||
user:
|
||||
type: integer
|
||||
user_obj:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/GroupMember'
|
||||
readOnly: true
|
||||
provider:
|
||||
type: integer
|
||||
required:
|
||||
- id
|
||||
- provider
|
||||
- scim_id
|
||||
- user
|
||||
- user_obj
|
||||
SCIMProviderUserRequest:
|
||||
type: object
|
||||
description: SCIMProviderUser Serializer
|
||||
properties:
|
||||
scim_id:
|
||||
type: string
|
||||
minLength: 1
|
||||
user:
|
||||
type: integer
|
||||
provider:
|
||||
type: integer
|
||||
required:
|
||||
- provider
|
||||
- scim_id
|
||||
- user
|
||||
SCIMSource:
|
||||
type: object
|
||||
description: SCIMSource Serializer
|
||||
@ -46837,15 +47406,16 @@ components:
|
||||
type: string
|
||||
openssl_version:
|
||||
type: string
|
||||
openssl_fips_mode:
|
||||
openssl_fips_enabled:
|
||||
type: boolean
|
||||
nullable: true
|
||||
authentik_version:
|
||||
type: string
|
||||
required:
|
||||
- architecture
|
||||
- authentik_version
|
||||
- environment
|
||||
- openssl_fips_mode
|
||||
- openssl_fips_enabled
|
||||
- openssl_version
|
||||
- platform
|
||||
- python_version
|
||||
|
@ -1,5 +1,3 @@
|
||||
version: '3.7'
|
||||
|
||||
services:
|
||||
chrome:
|
||||
image: docker.io/selenium/standalone-chrome:122.0
|
||||
|
168
tests/wdio/package-lock.json
generated
168
tests/wdio/package-lock.json
generated
@ -6,21 +6,21 @@
|
||||
"": {
|
||||
"name": "@goauthentik/web-tests",
|
||||
"dependencies": {
|
||||
"chromedriver": "^125.0.3"
|
||||
"chromedriver": "^126.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.5.0",
|
||||
"@typescript-eslint/parser": "^7.5.0",
|
||||
"@wdio/cli": "^8.38.1",
|
||||
"@wdio/local-runner": "^8.38.0",
|
||||
"@wdio/mocha-framework": "^8.38.0",
|
||||
"@wdio/spec-reporter": "^8.38.0",
|
||||
"@wdio/cli": "^8.38.2",
|
||||
"@wdio/local-runner": "^8.38.2",
|
||||
"@wdio/mocha-framework": "^8.38.2",
|
||||
"@wdio/spec-reporter": "^8.38.2",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"eslint-plugin-sonarjs": "^0.25.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.3.0",
|
||||
"prettier": "^3.3.2",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.4.5",
|
||||
"wdio-wait-for": "^3.0.11"
|
||||
@ -1189,19 +1189,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@wdio/cli": {
|
||||
"version": "8.38.1",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.38.1.tgz",
|
||||
"integrity": "sha512-xif0RvJX+saXVdzrhlEafbEdptNyeyqm0pyilT4H7OI7OMa+23rWB67r2SPoIZbBrVeFv1iqtUx9hR73RRio2A==",
|
||||
"version": "8.38.2",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.38.2.tgz",
|
||||
"integrity": "sha512-p9y6jxmpmw53OoB9v/uTLwMetmz7Q0K7NewdVONgmeTY/ERpkU15qL3fMw1rXb+E+vrV8dlce4srnXroec6SFA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "^20.1.1",
|
||||
"@vitest/snapshot": "^1.2.1",
|
||||
"@wdio/config": "8.38.0",
|
||||
"@wdio/globals": "8.38.0",
|
||||
"@wdio/config": "8.38.2",
|
||||
"@wdio/globals": "8.38.2",
|
||||
"@wdio/logger": "8.38.0",
|
||||
"@wdio/protocols": "8.38.0",
|
||||
"@wdio/types": "8.37.0",
|
||||
"@wdio/utils": "8.38.0",
|
||||
"@wdio/types": "8.38.2",
|
||||
"@wdio/utils": "8.38.2",
|
||||
"async-exit-hook": "^2.0.1",
|
||||
"chalk": "^5.2.0",
|
||||
"chokidar": "^3.5.3",
|
||||
@ -1216,7 +1216,7 @@
|
||||
"lodash.union": "^4.6.0",
|
||||
"read-pkg-up": "10.0.0",
|
||||
"recursive-readdir": "^2.2.3",
|
||||
"webdriverio": "8.38.0",
|
||||
"webdriverio": "8.38.2",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"bin": {
|
||||
@ -1239,14 +1239,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@wdio/config": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.38.0.tgz",
|
||||
"integrity": "sha512-9eMmHYkXw/0htj8Nok2vBa8Q+IS/wj7HXbLczKb5rEmDb57SW5iPMpZutFywGki1D/GIDLvejWaZvlxmS/yfBA==",
|
||||
"version": "8.38.2",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.38.2.tgz",
|
||||
"integrity": "sha512-xlnapTr1vOA0h5HsHTIqj47729FbG3WjxmgHweDEQvcT4C1g9l+WKf+N3FM7DNNoIsAqxKi6rOHG02rJADQJtw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@wdio/logger": "8.38.0",
|
||||
"@wdio/types": "8.37.0",
|
||||
"@wdio/utils": "8.38.0",
|
||||
"@wdio/types": "8.38.2",
|
||||
"@wdio/utils": "8.38.2",
|
||||
"decamelize": "^6.0.0",
|
||||
"deepmerge-ts": "^5.0.0",
|
||||
"glob": "^10.2.2",
|
||||
@ -1257,29 +1257,29 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@wdio/globals": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.38.0.tgz",
|
||||
"integrity": "sha512-3Lo7R305gwJrloUolrgSRaMbIFKwnj/SPoYUt94dsTF1oM3TpyWpomcVokElNQiOFx9WYjMmBla2M+YcnoGmgw==",
|
||||
"version": "8.38.2",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.38.2.tgz",
|
||||
"integrity": "sha512-iIrUF1EODfHLh3V/CSNCqbNNxUTe3ND+c86zDjzJcPFjawLX1plvAApsU/eDmtsFShcOS2KHbfSjiydFoqQG1Q==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^16.13 || >=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"expect-webdriverio": "^4.11.2",
|
||||
"webdriverio": "8.38.0"
|
||||
"webdriverio": "8.38.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@wdio/local-runner": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.38.0.tgz",
|
||||
"integrity": "sha512-++eVI+EQapBMRxtLSbeooK02uELAhIXNLzgBlC03s23OTZUUOxzl4WxFuGqaG8gpNHer5Bjg+uAy0rIbSMnklA==",
|
||||
"version": "8.38.2",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.38.2.tgz",
|
||||
"integrity": "sha512-syW+R5VUHJ3GBkQGFcNYe6MYwWRgklc9W7A83xQDTvKWFNHCetLvc8AtKZ54vs8MItBejjU+Oh94ZNbNX1pBcg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "^20.1.0",
|
||||
"@wdio/logger": "8.38.0",
|
||||
"@wdio/repl": "8.24.12",
|
||||
"@wdio/runner": "8.38.0",
|
||||
"@wdio/types": "8.37.0",
|
||||
"@wdio/runner": "8.38.2",
|
||||
"@wdio/types": "8.38.2",
|
||||
"async-exit-hook": "^2.0.1",
|
||||
"split2": "^4.1.0",
|
||||
"stream-buffers": "^3.0.2"
|
||||
@ -1316,16 +1316,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@wdio/mocha-framework": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.38.0.tgz",
|
||||
"integrity": "sha512-LWwKslKIxqhQNskdffcS6QbxN45PzJN21b5zByELbEYd9PIGNbuXXqueVE09RwEAzBHtL86ZhSpFclAB+ulsBw==",
|
||||
"version": "8.38.2",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.38.2.tgz",
|
||||
"integrity": "sha512-qJmRL5E6/ypjCUACH4hvCAAmTdU4YUrUlp9o/IKvTIAHMnZPE0/HgUFixCeu8Mop+rdzTPVBrbqxpRDdSnraYA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/mocha": "^10.0.0",
|
||||
"@types/node": "^20.1.0",
|
||||
"@wdio/logger": "8.38.0",
|
||||
"@wdio/types": "8.37.0",
|
||||
"@wdio/utils": "8.38.0",
|
||||
"@wdio/types": "8.38.2",
|
||||
"@wdio/utils": "8.38.2",
|
||||
"mocha": "^10.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@ -1351,14 +1351,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@wdio/reporter": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-8.38.0.tgz",
|
||||
"integrity": "sha512-7eyBM06j0PAu2g14wOwJbSmUOaT8q1pmMukzeRl1YDoW2sLpQG0zMZNzQNsQyi7+1KgltTJJnvHCec5MBZrjpw==",
|
||||
"version": "8.38.2",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-8.38.2.tgz",
|
||||
"integrity": "sha512-R78UdAtAnkaV22NYlCCcbPPhmYweiDURiw64LYhlVIQrKNuXUQcafR2kRlWKy31rZc9thSLs5LzrZDReENUlFQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "^20.1.0",
|
||||
"@wdio/logger": "8.38.0",
|
||||
"@wdio/types": "8.37.0",
|
||||
"@wdio/types": "8.38.2",
|
||||
"diff": "^5.0.0",
|
||||
"object-inspect": "^1.12.0"
|
||||
},
|
||||
@ -1367,35 +1367,35 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@wdio/runner": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.38.0.tgz",
|
||||
"integrity": "sha512-pmoXwRMUxWXhqce64Y3gqoMqJn0YknJ1U9aHOMxp6uB3iHfzoPA+7Mf5ziSc0uDBcEyjXtau9qMfu7iBHUz62Q==",
|
||||
"version": "8.38.2",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.38.2.tgz",
|
||||
"integrity": "sha512-5lPnKSX2BBLI2AbYW+hoGPiEUAJXj8F8I6NC2LaBVzf1CLN+w2HWZ7lUiqS14XT0b5/hlSUX6+JYwUXlDbpuuw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "^20.11.28",
|
||||
"@wdio/config": "8.38.0",
|
||||
"@wdio/globals": "8.38.0",
|
||||
"@wdio/config": "8.38.2",
|
||||
"@wdio/globals": "8.38.2",
|
||||
"@wdio/logger": "8.38.0",
|
||||
"@wdio/types": "8.37.0",
|
||||
"@wdio/utils": "8.38.0",
|
||||
"@wdio/types": "8.38.2",
|
||||
"@wdio/utils": "8.38.2",
|
||||
"deepmerge-ts": "^5.1.0",
|
||||
"expect-webdriverio": "^4.12.0",
|
||||
"gaze": "^1.1.3",
|
||||
"webdriver": "8.38.0",
|
||||
"webdriverio": "8.38.0"
|
||||
"webdriver": "8.38.2",
|
||||
"webdriverio": "8.38.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.13 || >=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@wdio/spec-reporter": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-8.38.0.tgz",
|
||||
"integrity": "sha512-Cuk/mqsBa+YgAL3OCtsYLUxRkGSYeaXM3LSE5muzY1mSqQlK3a/5GdAXlaOosuhYpUgI0A9xTsMiq7PQd75qHA==",
|
||||
"version": "8.38.2",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-8.38.2.tgz",
|
||||
"integrity": "sha512-Dntk+lmrp+0I3HRRWkkXED+smshvgsW5cdLKwJhEJ1liI48MdBpdNGf9IHTVckE6nfxcWDyFI4icD9qYv/5bFA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@wdio/reporter": "8.38.0",
|
||||
"@wdio/types": "8.37.0",
|
||||
"@wdio/reporter": "8.38.2",
|
||||
"@wdio/types": "8.38.2",
|
||||
"chalk": "^5.1.2",
|
||||
"easy-table": "^1.2.0",
|
||||
"pretty-ms": "^7.0.0"
|
||||
@ -1417,9 +1417,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@wdio/types": {
|
||||
"version": "8.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.37.0.tgz",
|
||||
"integrity": "sha512-36kmSlZcVhsMlbhaSCQUfL51iG81FlbzW4Dfkz4903cDkxmh64bgxydZbRB5aPLnJzzR7tI3chIME8zSVZFR8w==",
|
||||
"version": "8.38.2",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.38.2.tgz",
|
||||
"integrity": "sha512-+wj1c1OSLdnN4WO5b44Ih4263dTl/eSwMGSI4/pCgIyXIuYQH38JQ+6WRa+c8vJEskUzboq2cSgEQumVZ39ozQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "^20.1.0"
|
||||
@ -1429,14 +1429,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@wdio/utils": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.38.0.tgz",
|
||||
"integrity": "sha512-ios7MpyJk4kGW9ZOYxbPpdwVZBI7SzccIgiirqSf8rvJi62VpDA2nfa7i7BY1rs9p7lnenF8phwnuVFGMCoL0w==",
|
||||
"version": "8.38.2",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.38.2.tgz",
|
||||
"integrity": "sha512-y5AnBwsGcu/XuCBGCgKmlvKdwEIFyzLA+Cr+denySxY3jbWDONtPUcGaVdFALwsIa5jcIjcATqGmZcCPGnkd7g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@puppeteer/browsers": "^1.6.0",
|
||||
"@wdio/logger": "8.38.0",
|
||||
"@wdio/types": "8.37.0",
|
||||
"@wdio/types": "8.38.2",
|
||||
"decamelize": "^6.0.0",
|
||||
"deepmerge-ts": "^5.1.0",
|
||||
"edgedriver": "^5.5.0",
|
||||
@ -1879,12 +1879,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fill-range": "^7.0.1"
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@ -2084,9 +2084,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/chromedriver": {
|
||||
"version": "125.0.3",
|
||||
"resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-125.0.3.tgz",
|
||||
"integrity": "sha512-Qzuk5Wian2o3EVGjtbz6V/jv+pT/AV9246HbG6kUljZXXjsKZLZxqJC+kHR3qEh/wdv4EJD0YwAOWV72v9hogw==",
|
||||
"version": "126.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-126.0.0.tgz",
|
||||
"integrity": "sha512-rzwKp1okI9RmFtSyIzkk9+GTlTK62ai5P3/AS2qMwl86+gw84d2S/IyLkQMm5cqieFs4dgDAuqqPu0AqQACScg==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@testim/chrome-version": "^1.1.4",
|
||||
@ -3655,9 +3655,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
@ -6908,9 +6908,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.0.tgz",
|
||||
"integrity": "sha512-J9odKxERhCQ10OC2yb93583f6UnYutOeiV5i0zEDS7UGTdUt0u+y8erxl3lBKvwo/JHyyoEdXjwp4dke9oyZ/g==",
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz",
|
||||
"integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
@ -8969,18 +8969,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webdriver": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.38.0.tgz",
|
||||
"integrity": "sha512-BT3sd667AqcZ1lzaOd7lphjnc7MNG3WgduAq4vUUDYlJAbs6SyjYd0EZPIPv9KUUYr0BoSJTDa0Xuyl/6PPW0Q==",
|
||||
"version": "8.38.2",
|
||||
"resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.38.2.tgz",
|
||||
"integrity": "sha512-NGfjW0BDYwFgOIzeojOcWGn3tYloQdvHr+Y2xKKYVqa9Rs0x1mzlTjU1kWtC4DaV8DltskwaPa7o+s8hTNpuyA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "^20.1.0",
|
||||
"@types/ws": "^8.5.3",
|
||||
"@wdio/config": "8.38.0",
|
||||
"@wdio/config": "8.38.2",
|
||||
"@wdio/logger": "8.38.0",
|
||||
"@wdio/protocols": "8.38.0",
|
||||
"@wdio/types": "8.37.0",
|
||||
"@wdio/utils": "8.38.0",
|
||||
"@wdio/types": "8.38.2",
|
||||
"@wdio/utils": "8.38.2",
|
||||
"deepmerge-ts": "^5.1.0",
|
||||
"got": "^12.6.1",
|
||||
"ky": "^0.33.0",
|
||||
@ -8991,18 +8991,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webdriverio": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.38.0.tgz",
|
||||
"integrity": "sha512-PxLtJPK8Aa+f/G/P8YGUwGH80uHowMA8cDHshhog6sKbp1BXEVB8x6PyC1AIswRlTWVijoes7cD0cUoVs6C87A==",
|
||||
"version": "8.38.2",
|
||||
"resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.38.2.tgz",
|
||||
"integrity": "sha512-r09y5UfivyYh5JOzT2SpJJ1zDmQl/R4OTH12opUqkjvp21BibCQm/uu1mrxGy4lzSHljrvqSVrrcGI+6UA1O8w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "^20.1.0",
|
||||
"@wdio/config": "8.38.0",
|
||||
"@wdio/config": "8.38.2",
|
||||
"@wdio/logger": "8.38.0",
|
||||
"@wdio/protocols": "8.38.0",
|
||||
"@wdio/repl": "8.24.12",
|
||||
"@wdio/types": "8.37.0",
|
||||
"@wdio/utils": "8.38.0",
|
||||
"@wdio/types": "8.38.2",
|
||||
"@wdio/utils": "8.38.2",
|
||||
"archiver": "^7.0.0",
|
||||
"aria-query": "^5.0.0",
|
||||
"css-shorthand-properties": "^1.1.1",
|
||||
@ -9020,7 +9020,7 @@
|
||||
"resq": "^1.9.1",
|
||||
"rgb2hex": "0.2.5",
|
||||
"serialize-error": "^11.0.1",
|
||||
"webdriver": "8.38.0"
|
||||
"webdriver": "8.38.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.13 || >=18"
|
||||
|
@ -6,15 +6,15 @@
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.5.0",
|
||||
"@typescript-eslint/parser": "^7.5.0",
|
||||
"@wdio/cli": "^8.38.1",
|
||||
"@wdio/local-runner": "^8.38.0",
|
||||
"@wdio/mocha-framework": "^8.38.0",
|
||||
"@wdio/spec-reporter": "^8.38.0",
|
||||
"@wdio/cli": "^8.38.2",
|
||||
"@wdio/local-runner": "^8.38.2",
|
||||
"@wdio/mocha-framework": "^8.38.2",
|
||||
"@wdio/spec-reporter": "^8.38.2",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"eslint-plugin-sonarjs": "^0.25.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.3.0",
|
||||
"prettier": "^3.3.2",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.4.5",
|
||||
"wdio-wait-for": "^3.0.11"
|
||||
@ -32,6 +32,6 @@
|
||||
"node": ">=20"
|
||||
},
|
||||
"dependencies": {
|
||||
"chromedriver": "^125.0.3"
|
||||
"chromedriver": "^126.0.0"
|
||||
}
|
||||
}
|
||||
|
2450
web/package-lock.json
generated
2450
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -15,7 +15,7 @@
|
||||
"build-proxy": "run-s build-locales esbuild:build-proxy",
|
||||
"watch": "run-s build-locales esbuild:watch",
|
||||
"lint": "cross-env NODE_OPTIONS='--max_old_space_size=65536' eslint . --max-warnings 0 --fix",
|
||||
"lint:precommit": "cross-env NODE_OPTIONS='--max_old_space_size=65536' node scripts/eslint-precommit.mjs",
|
||||
"lint:precommit": "bun scripts/eslint-precommit.mjs",
|
||||
"lint:spelling": "node scripts/check-spelling.mjs",
|
||||
"lit-analyse": "lit-analyzer src",
|
||||
"precommit": "npm-run-all --parallel tsc lit-analyse lint:spelling --sequential lint:precommit prettier",
|
||||
@ -38,15 +38,15 @@
|
||||
"@codemirror/theme-one-dark": "^6.1.2",
|
||||
"@formatjs/intl-listformat": "^7.5.7",
|
||||
"@fortawesome/fontawesome-free": "^6.5.2",
|
||||
"@goauthentik/api": "^2024.4.2-1717033226",
|
||||
"@goauthentik/api": "^2024.4.2-1718378698",
|
||||
"@lit-labs/task": "^3.1.0",
|
||||
"@lit/context": "^1.1.1",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@lit/localize": "^0.12.1",
|
||||
"@lit/reactive-element": "^2.0.4",
|
||||
"@open-wc/lit-helpers": "^0.7.0",
|
||||
"@patternfly/elements": "^3.0.1",
|
||||
"@patternfly/patternfly": "^4.224.2",
|
||||
"@sentry/browser": "^8.7.0",
|
||||
"@sentry/browser": "^8.9.2",
|
||||
"@webcomponents/webcomponentsjs": "^2.8.0",
|
||||
"base64-js": "^1.5.1",
|
||||
"chart.js": "^4.4.3",
|
||||
@ -54,40 +54,40 @@
|
||||
"codemirror": "^6.0.1",
|
||||
"construct-style-sheets-polyfill": "^3.1.0",
|
||||
"core-js": "^3.37.1",
|
||||
"country-flag-icons": "^1.5.11",
|
||||
"country-flag-icons": "^1.5.12",
|
||||
"fuse.js": "^7.0.0",
|
||||
"guacamole-common-js": "^1.5.0",
|
||||
"lit": "^3.1.3",
|
||||
"lit": "^3.1.4",
|
||||
"md-front-matter": "^1.0.4",
|
||||
"mermaid": "^10.9.1",
|
||||
"rapidoc": "^9.3.4",
|
||||
"showdown": "^2.1.0",
|
||||
"style-mod": "^4.1.2",
|
||||
"ts-pattern": "^5.1.2",
|
||||
"ts-pattern": "^5.2.0",
|
||||
"webcomponent-qr-code": "^1.2.0",
|
||||
"yaml": "^2.4.3"
|
||||
"yaml": "^2.4.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.24.6",
|
||||
"@babel/core": "^7.24.7",
|
||||
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||
"@babel/plugin-proposal-decorators": "^7.24.6",
|
||||
"@babel/plugin-transform-private-methods": "^7.24.6",
|
||||
"@babel/plugin-transform-private-property-in-object": "^7.24.6",
|
||||
"@babel/plugin-transform-runtime": "^7.24.6",
|
||||
"@babel/preset-env": "^7.24.6",
|
||||
"@babel/preset-typescript": "^7.24.6",
|
||||
"@babel/plugin-proposal-decorators": "^7.24.7",
|
||||
"@babel/plugin-transform-private-methods": "^7.24.7",
|
||||
"@babel/plugin-transform-private-property-in-object": "^7.24.7",
|
||||
"@babel/plugin-transform-runtime": "^7.24.7",
|
||||
"@babel/preset-env": "^7.24.7",
|
||||
"@babel/preset-typescript": "^7.24.7",
|
||||
"@hcaptcha/types": "^1.0.3",
|
||||
"@jeysal/storybook-addon-css-user-preferences": "^0.2.0",
|
||||
"@lit/localize-tools": "^0.7.2",
|
||||
"@rollup/plugin-replace": "^5.0.5",
|
||||
"@spotlightjs/spotlight": "^1.2.17",
|
||||
"@storybook/addon-essentials": "^8.1.5",
|
||||
"@storybook/addon-links": "^8.1.5",
|
||||
"@rollup/plugin-replace": "^5.0.7",
|
||||
"@spotlightjs/spotlight": "^2.0.0",
|
||||
"@storybook/addon-essentials": "^8.1.9",
|
||||
"@storybook/addon-links": "^8.1.9",
|
||||
"@storybook/api": "^7.6.17",
|
||||
"@storybook/blocks": "^8.0.8",
|
||||
"@storybook/manager-api": "^8.1.5",
|
||||
"@storybook/web-components": "^8.1.5",
|
||||
"@storybook/web-components-vite": "^8.1.5",
|
||||
"@storybook/manager-api": "^8.1.9",
|
||||
"@storybook/web-components": "^8.1.9",
|
||||
"@storybook/web-components-vite": "^8.1.9",
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||
"@types/chart.js": "^2.9.41",
|
||||
"@types/codemirror": "5.60.15",
|
||||
@ -100,7 +100,7 @@
|
||||
"babel-plugin-tsconfig-paths": "^1.0.3",
|
||||
"chokidar": "^3.6.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"esbuild": "^0.21.4",
|
||||
"esbuild": "^0.21.5",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"eslint-plugin-custom-elements": "0.0.8",
|
||||
@ -111,16 +111,16 @@
|
||||
"glob": "^10.4.1",
|
||||
"lit-analyzer": "^2.0.3",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.3.0",
|
||||
"prettier": "^3.3.2",
|
||||
"pseudolocale": "^2.0.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.3.1",
|
||||
"rollup-plugin-modify": "^3.0.0",
|
||||
"rollup-plugin-postcss-lit": "^2.1.0",
|
||||
"storybook": "^8.1.5",
|
||||
"storybook": "^8.1.9",
|
||||
"storybook-addon-mock": "^5.0.0",
|
||||
"ts-lit-plugin": "^2.0.2",
|
||||
"tslib": "^2.6.2",
|
||||
"tslib": "^2.6.3",
|
||||
"turnstile-types": "^1.2.1",
|
||||
"typescript": "^5.4.5",
|
||||
"vite-tsconfig-paths": "^4.3.2"
|
||||
|
@ -1,5 +1,6 @@
|
||||
import "@goauthentik/admin/admin-overview/TopApplicationsTable";
|
||||
import "@goauthentik/admin/admin-overview/cards/AdminStatusCard";
|
||||
import "@goauthentik/admin/admin-overview/cards/FipsStatusCard";
|
||||
import "@goauthentik/admin/admin-overview/cards/RecentEventsCard";
|
||||
import "@goauthentik/admin/admin-overview/cards/SystemStatusCard";
|
||||
import "@goauthentik/admin/admin-overview/cards/VersionStatusCard";
|
||||
@ -10,13 +11,17 @@ import "@goauthentik/admin/admin-overview/charts/SyncStatusChart";
|
||||
import { VERSION } from "@goauthentik/common/constants";
|
||||
import { me } from "@goauthentik/common/users";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider.js";
|
||||
import "@goauthentik/elements/PageHeader";
|
||||
import "@goauthentik/elements/cards/AggregatePromiseCard";
|
||||
import { paramURL } from "@goauthentik/elements/router/RouterOutlet";
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
import { CSSResult, TemplateResult, css, html, nothing } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { classMap } from "lit/directives/class-map.js";
|
||||
import { map } from "lit/directives/map.js";
|
||||
import { when } from "lit/directives/when.js";
|
||||
|
||||
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||
import PFDivider from "@patternfly/patternfly/components/Divider/divider.css";
|
||||
@ -33,8 +38,12 @@ export function versionFamily(): string {
|
||||
return parts.join(".");
|
||||
}
|
||||
|
||||
const AdminOverviewBase = WithLicenseSummary(AKElement);
|
||||
|
||||
type Renderer = () => TemplateResult | typeof nothing;
|
||||
|
||||
@customElement("ak-admin-overview")
|
||||
export class AdminOverviewPage extends AKElement {
|
||||
export class AdminOverviewPage extends AdminOverviewBase {
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
PFBase,
|
||||
@ -73,6 +82,7 @@ export class AdminOverviewPage extends AKElement {
|
||||
|
||||
render(): TemplateResult {
|
||||
const name = this.user?.user.name ?? this.user?.user.username;
|
||||
|
||||
return html`<ak-page-header icon="" header="" description=${msg("General system status")}>
|
||||
<span slot="header"> ${msg(str`Welcome, ${name}.`)} </span>
|
||||
</ak-page-header>
|
||||
@ -89,48 +99,7 @@ export class AdminOverviewPage extends AKElement {
|
||||
.isCenter=${false}
|
||||
>
|
||||
<ul class="pf-c-list">
|
||||
<li>
|
||||
<a
|
||||
class="pf-u-mb-xl"
|
||||
href=${paramURL("/core/applications", {
|
||||
createForm: true,
|
||||
})}
|
||||
>${msg("Create a new application")}</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a class="pf-u-mb-xl" href=${paramURL("/events/log")}
|
||||
>${msg("Check the logs")}</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
class="pf-u-mb-xl"
|
||||
target="_blank"
|
||||
href="https://goauthentik.io/integrations/"
|
||||
>${msg("Explore integrations")}<i
|
||||
class="fas fa-external-link-alt ak-external-link"
|
||||
></i
|
||||
></a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="pf-u-mb-xl" href=${paramURL("/identity/users")}
|
||||
>${msg("Manage users")}</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
class="pf-u-mb-xl"
|
||||
target="_blank"
|
||||
href="https://goauthentik.io/docs/releases/${versionFamily()}#fixed-in-${VERSION.replaceAll(
|
||||
".",
|
||||
"",
|
||||
)}"
|
||||
>${msg("Check the release notes")}<i
|
||||
class="fas fa-external-link-alt ak-external-link"
|
||||
></i
|
||||
></a>
|
||||
</li>
|
||||
${this.renderActions()}
|
||||
</ul>
|
||||
</ak-aggregate-card>
|
||||
</div>
|
||||
@ -153,21 +122,7 @@ export class AdminOverviewPage extends AKElement {
|
||||
<div class="pf-l-grid__item pf-m-12-col">
|
||||
<hr class="pf-c-divider" />
|
||||
</div>
|
||||
<div
|
||||
class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-md pf-m-4-col-on-xl card-container"
|
||||
>
|
||||
<ak-admin-status-system> </ak-admin-status-system>
|
||||
</div>
|
||||
<div
|
||||
class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-md pf-m-4-col-on-xl card-container"
|
||||
>
|
||||
<ak-admin-status-version> </ak-admin-status-version>
|
||||
</div>
|
||||
<div
|
||||
class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-md pf-m-4-col-on-xl card-container"
|
||||
>
|
||||
<ak-admin-status-card-workers> </ak-admin-status-card-workers>
|
||||
</div>
|
||||
${this.renderCards()}
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-m-12-col pf-m-6-col-on-xl">
|
||||
<ak-recent-events pageSize="6"></ak-recent-events>
|
||||
@ -201,4 +156,70 @@ export class AdminOverviewPage extends AKElement {
|
||||
</div>
|
||||
</section>`;
|
||||
}
|
||||
|
||||
renderCards() {
|
||||
const isEnterprise = this.hasEnterpriseLicense;
|
||||
const classes = {
|
||||
"card-container": true,
|
||||
"pf-l-grid__item": true,
|
||||
"pf-m-6-col": true,
|
||||
"pf-m-4-col-on-md": !isEnterprise,
|
||||
"pf-m-4-col-on-xl": !isEnterprise,
|
||||
"pf-m-3-col-on-md": isEnterprise,
|
||||
"pf-m-3-col-on-xl": isEnterprise,
|
||||
};
|
||||
|
||||
return html`<div class=${classMap(classes)}>
|
||||
<ak-admin-status-system> </ak-admin-status-system>
|
||||
</div>
|
||||
<div class=${classMap(classes)}>
|
||||
<ak-admin-status-version> </ak-admin-status-version>
|
||||
</div>
|
||||
<div class=${classMap(classes)}>
|
||||
<ak-admin-status-card-workers> </ak-admin-status-card-workers>
|
||||
</div>
|
||||
${isEnterprise
|
||||
? html` <div class=${classMap(classes)}>
|
||||
<ak-admin-fips-status-system> </ak-admin-fips-status-system>
|
||||
</div>`
|
||||
: nothing} `;
|
||||
}
|
||||
|
||||
renderActions() {
|
||||
const release = `${versionFamily()}#fixed-in-${VERSION.replaceAll(".", "")}`;
|
||||
|
||||
const quickActions: [string, string][] = [
|
||||
[msg("Create a new application"), paramURL("/core/applications", { createForm: true })],
|
||||
[msg("Check the logs"), paramURL("/events/log")],
|
||||
[msg("Explore integrations"), "https://goauthentik.io/integrations/"],
|
||||
[msg("Manage users"), paramURL("/identity/users")],
|
||||
[msg("Check the release notes"), `https://goauthentik.io/docs/releases/${release}`],
|
||||
];
|
||||
|
||||
const action = ([label, url]: [string, string]) => {
|
||||
const isExternal = url.startsWith("https://");
|
||||
const ex = (truecase: Renderer, falsecase: Renderer) =>
|
||||
when(isExternal, truecase, falsecase);
|
||||
|
||||
const content = html`${label}${ex(
|
||||
() => html`<i class="fas fa-external-link-alt ak-external-link"></i>`,
|
||||
() => nothing,
|
||||
)}`;
|
||||
|
||||
return html`<li>
|
||||
${ex(
|
||||
() => html`<a href="${url}" class="pf-u-mb-xl" target="_blank">${content}</a>`,
|
||||
() => html`<a href="${url}" class="pf-u-mb-xl" )>${content}</a>`,
|
||||
)}
|
||||
</li>`;
|
||||
};
|
||||
|
||||
return html`${map(quickActions, action)}`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ak-admin-overview": AdminOverviewPage;
|
||||
}
|
||||
}
|
||||
|
56
web/src/admin/admin-overview/cards/FipsStatusCard.ts
Normal file
56
web/src/admin/admin-overview/cards/FipsStatusCard.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import {
|
||||
AdminStatus,
|
||||
AdminStatusCard,
|
||||
} from "@goauthentik/admin/admin-overview/cards/AdminStatusCard";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
|
||||
import { AdminApi, SystemInfo } from "@goauthentik/api";
|
||||
|
||||
type StatusContent = { icon: string; message: TemplateResult };
|
||||
|
||||
@customElement("ak-admin-fips-status-system")
|
||||
export class FipsStatusCard extends AdminStatusCard<SystemInfo> {
|
||||
icon = "pf-icon pf-icon-server";
|
||||
|
||||
@state()
|
||||
statusSummary?: string;
|
||||
|
||||
async getPrimaryValue(): Promise<SystemInfo> {
|
||||
return await new AdminApi(DEFAULT_CONFIG).adminSystemRetrieve();
|
||||
}
|
||||
|
||||
setStatus(summary: string, content: StatusContent): Promise<AdminStatus> {
|
||||
this.statusSummary = summary;
|
||||
return Promise.resolve<AdminStatus>(content);
|
||||
}
|
||||
|
||||
getStatus(value: SystemInfo): Promise<AdminStatus> {
|
||||
return value.runtime.opensslFipsEnabled
|
||||
? this.setStatus(msg("OK"), {
|
||||
icon: "fa fa-check-circle pf-m-success",
|
||||
message: html`${msg("FIPS compliance: passing")}`,
|
||||
})
|
||||
: this.setStatus(msg("Unverified"), {
|
||||
icon: "fa fa-info-circle pf-m-warning",
|
||||
message: html`${msg("FIPS compliance: unverified")}`,
|
||||
});
|
||||
}
|
||||
|
||||
renderHeader(): TemplateResult {
|
||||
return html`${msg("FIPS Status")}`;
|
||||
}
|
||||
|
||||
renderValue(): TemplateResult {
|
||||
return html`${this.statusSummary}`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ak-admin-fips-status-system": FipsStatusCard;
|
||||
}
|
||||
}
|
@ -126,6 +126,7 @@ export class PolicyTestForm extends Form<PropertyMappingTestRequest> {
|
||||
renderForm(): TemplateResult {
|
||||
return html`<ak-form-element-horizontal label=${msg("User")} name="user">
|
||||
<ak-search-select
|
||||
blankable
|
||||
.fetchObjects=${async (query?: string): Promise<User[]> => {
|
||||
const args: CoreUsersListRequest = {
|
||||
ordering: "username",
|
||||
@ -153,6 +154,7 @@ export class PolicyTestForm extends Form<PropertyMappingTestRequest> {
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Group")} name="group">
|
||||
<ak-search-select
|
||||
blankable
|
||||
.fetchObjects=${async (query?: string): Promise<Group[]> => {
|
||||
const args: CoreGroupsListRequest = {
|
||||
ordering: "name",
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { uiConfig } from "@goauthentik/common/ui/config";
|
||||
import "@goauthentik/elements/forms/DeleteBulkForm";
|
||||
import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
@ -19,6 +20,26 @@ export class GoogleWorkspaceProviderGroupList extends Table<GoogleWorkspaceProvi
|
||||
return true;
|
||||
}
|
||||
|
||||
checkbox = true;
|
||||
clearOnRefresh = true;
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${msg("Google Workspace Group(s)")}
|
||||
.objects=${this.selectedElements}
|
||||
.delete=${(item: GoogleWorkspaceProviderGroup) => {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersGoogleWorkspaceGroupsDestroy({
|
||||
id: item.id,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button ?disabled=${disabled} slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${msg("Delete")}
|
||||
</button>
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
async apiEndpoint(page: number): Promise<PaginatedResponse<GoogleWorkspaceProviderGroup>> {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersGoogleWorkspaceGroupsList({
|
||||
page: page,
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { uiConfig } from "@goauthentik/common/ui/config";
|
||||
import "@goauthentik/elements/forms/DeleteBulkForm";
|
||||
import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
@ -19,6 +20,26 @@ export class GoogleWorkspaceProviderUserList extends Table<GoogleWorkspaceProvid
|
||||
|
||||
expandable = true;
|
||||
|
||||
checkbox = true;
|
||||
clearOnRefresh = true;
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${msg("Google Workspace User(s)")}
|
||||
.objects=${this.selectedElements}
|
||||
.delete=${(item: GoogleWorkspaceProviderUser) => {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersGoogleWorkspaceUsersDestroy({
|
||||
id: item.id,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button ?disabled=${disabled} slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${msg("Delete")}
|
||||
</button>
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
async apiEndpoint(page: number): Promise<PaginatedResponse<GoogleWorkspaceProviderUser>> {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersGoogleWorkspaceUsersList({
|
||||
page: page,
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { uiConfig } from "@goauthentik/common/ui/config";
|
||||
import "@goauthentik/elements/forms/DeleteBulkForm";
|
||||
import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
@ -19,6 +20,23 @@ export class MicrosoftEntraProviderGroupList extends Table<MicrosoftEntraProvide
|
||||
return true;
|
||||
}
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${msg("Microsoft Entra Group(s)")}
|
||||
.objects=${this.selectedElements}
|
||||
.delete=${(item: MicrosoftEntraProviderGroup) => {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersMicrosoftEntraGroupsDestroy({
|
||||
id: item.id,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button ?disabled=${disabled} slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${msg("Delete")}
|
||||
</button>
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
async apiEndpoint(page: number): Promise<PaginatedResponse<MicrosoftEntraProviderGroup>> {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersMicrosoftEntraGroupsList({
|
||||
page: page,
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { uiConfig } from "@goauthentik/common/ui/config";
|
||||
import "@goauthentik/elements/forms/DeleteBulkForm";
|
||||
import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
@ -19,6 +20,26 @@ export class MicrosoftEntraProviderUserList extends Table<MicrosoftEntraProvider
|
||||
return true;
|
||||
}
|
||||
|
||||
checkbox = true;
|
||||
clearOnRefresh = true;
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${msg("Microsoft Entra User(s)")}
|
||||
.objects=${this.selectedElements}
|
||||
.delete=${(item: MicrosoftEntraProviderUser) => {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersMicrosoftEntraUsersDestroy({
|
||||
id: item.id,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button ?disabled=${disabled} slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${msg("Delete")}
|
||||
</button>
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
async apiEndpoint(page: number): Promise<PaginatedResponse<MicrosoftEntraProviderUser>> {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersMicrosoftEntraUsersList({
|
||||
page: page,
|
||||
|
63
web/src/admin/providers/scim/SCIMProviderGroupList.ts
Normal file
63
web/src/admin/providers/scim/SCIMProviderGroupList.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { uiConfig } from "@goauthentik/common/ui/config";
|
||||
import "@goauthentik/elements/forms/DeleteBulkForm";
|
||||
import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import { ProvidersApi, SCIMProviderGroup } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-provider-scim-groups-list")
|
||||
export class SCIMProviderGroupList extends Table<SCIMProviderGroup> {
|
||||
@property({ type: Number })
|
||||
providerId?: number;
|
||||
|
||||
searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
checkbox = true;
|
||||
clearOnRefresh = true;
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${msg("SCIM Group(s)")}
|
||||
.objects=${this.selectedElements}
|
||||
.delete=${(item: SCIMProviderGroup) => {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersScimGroupsDestroy({
|
||||
id: item.id,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button ?disabled=${disabled} slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${msg("Delete")}
|
||||
</button>
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
async apiEndpoint(page: number): Promise<PaginatedResponse<SCIMProviderGroup>> {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersScimGroupsList({
|
||||
page: page,
|
||||
pageSize: (await uiConfig()).pagination.perPage,
|
||||
ordering: this.order,
|
||||
search: this.search || "",
|
||||
providerId: this.providerId,
|
||||
});
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
return [new TableColumn(msg("Name")), new TableColumn(msg("ID"))];
|
||||
}
|
||||
|
||||
row(item: SCIMProviderGroup): TemplateResult[] {
|
||||
return [
|
||||
html`<a href="#/identity/groups/${item.groupObj.pk}">
|
||||
<div>${item.groupObj.name}</div>
|
||||
</a>`,
|
||||
html`${item.id}`,
|
||||
];
|
||||
}
|
||||
}
|
64
web/src/admin/providers/scim/SCIMProviderUserList.ts
Normal file
64
web/src/admin/providers/scim/SCIMProviderUserList.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { uiConfig } from "@goauthentik/common/ui/config";
|
||||
import "@goauthentik/elements/forms/DeleteBulkForm";
|
||||
import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import { ProvidersApi, SCIMProviderUser } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-provider-scim-users-list")
|
||||
export class SCIMProviderUserList extends Table<SCIMProviderUser> {
|
||||
@property({ type: Number })
|
||||
providerId?: number;
|
||||
|
||||
searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
checkbox = true;
|
||||
clearOnRefresh = true;
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${msg("SCIM User(s)")}
|
||||
.objects=${this.selectedElements}
|
||||
.delete=${(item: SCIMProviderUser) => {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersScimUsersDestroy({
|
||||
id: item.id,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button ?disabled=${disabled} slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${msg("Delete")}
|
||||
</button>
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
async apiEndpoint(page: number): Promise<PaginatedResponse<SCIMProviderUser>> {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersScimUsersList({
|
||||
page: page,
|
||||
pageSize: (await uiConfig()).pagination.perPage,
|
||||
ordering: this.order,
|
||||
search: this.search || "",
|
||||
providerId: this.providerId,
|
||||
});
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
return [new TableColumn(msg("Username")), new TableColumn(msg("ID"))];
|
||||
}
|
||||
|
||||
row(item: SCIMProviderUser): TemplateResult[] {
|
||||
return [
|
||||
html`<a href="#/identity/users/${item.userObj.pk}">
|
||||
<div>${item.userObj.username}</div>
|
||||
<small>${item.userObj.name}</small>
|
||||
</a>`,
|
||||
html`${item.id}`,
|
||||
];
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
import "@goauthentik/admin/providers/scim/SCIMProviderForm";
|
||||
import "@goauthentik/admin/providers/scim/SCIMProviderGroupList";
|
||||
import "@goauthentik/admin/providers/scim/SCIMProviderUserList";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import "@goauthentik/components/events/ObjectChangelog";
|
||||
@ -102,6 +104,28 @@ export class SCIMProviderViewPage extends AKElement {
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section
|
||||
slot="page-users"
|
||||
data-tab-title="${msg("Provisioned Users")}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-l-grid pf-m-gutter">
|
||||
<ak-provider-scim-users-list
|
||||
providerId=${this.provider.pk}
|
||||
></ak-provider-scim-users-list>
|
||||
</div>
|
||||
</section>
|
||||
<section
|
||||
slot="page-groups"
|
||||
data-tab-title="${msg("Provisioned Groups")}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-l-grid pf-m-gutter">
|
||||
<ak-provider-scim-groups-list
|
||||
providerId=${this.provider.pk}
|
||||
></ak-provider-scim-groups-list>
|
||||
</div>
|
||||
</section>
|
||||
<ak-rbac-object-permission-page
|
||||
slot="page-permissions"
|
||||
data-tab-title="${msg("Permissions")}"
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/components/ak-number-input";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
|
||||
@ -78,6 +80,40 @@ export class CaptchaStageForm extends BaseStageForm<CaptchaStage> {
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-number-input
|
||||
label=${msg("Score minimum threshold")}
|
||||
required
|
||||
name="scoreMinThreshold"
|
||||
value="${ifDefined(this.instance?.scoreMinThreshold || 0.5)}"
|
||||
help=${msg("Minimum required score to allow continuing")}
|
||||
></ak-number-input>
|
||||
<ak-number-input
|
||||
label=${msg("Score maximum threshold")}
|
||||
required
|
||||
name="scoreMaxThreshold"
|
||||
value="${ifDefined(this.instance?.scoreMaxThreshold || -1)}"
|
||||
help=${msg("Maximum allowed score to allow continuing")}
|
||||
></ak-number-input>
|
||||
<ak-form-element-horizontal name="errorOnInvalidScore">
|
||||
<label class="pf-c-switch">
|
||||
<input
|
||||
class="pf-c-switch__input"
|
||||
type="checkbox"
|
||||
?checked=${first(this.instance?.errorOnInvalidScore, true)}
|
||||
/>
|
||||
<span class="pf-c-switch__toggle">
|
||||
<span class="pf-c-switch__toggle-icon">
|
||||
<i class="fas fa-check" aria-hidden="true"></i>
|
||||
</span>
|
||||
</span>
|
||||
<span class="pf-c-switch__label">${msg("Error on invalid score")}</span>
|
||||
</label>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
"When enabled and the resultant score is outside the threshold, the user will not be able to continue. When disabled, the user will be able to continue and the score can be used in policies to customize further stages.",
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
<ak-form-group>
|
||||
|
@ -3,7 +3,7 @@ export const SUCCESS_CLASS = "pf-m-success";
|
||||
export const ERROR_CLASS = "pf-m-danger";
|
||||
export const PROGRESS_CLASS = "pf-m-in-progress";
|
||||
export const CURRENT_CLASS = "pf-m-current";
|
||||
export const VERSION = "2024.4.2";
|
||||
export const VERSION = "2024.6.0";
|
||||
export const TITLE_DEFAULT = "authentik";
|
||||
export const ROUTE_SEPARATOR = ";";
|
||||
|
||||
|
@ -15,7 +15,7 @@ import PFProgressStepper from "@patternfly/patternfly/components/ProgressStepper
|
||||
import PFStack from "@patternfly/patternfly/layouts/Stack/stack.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
import { FlowInspection, FlowsApi, Stage } from "@goauthentik/api";
|
||||
import { FlowInspection, FlowsApi, ResponseError, Stage } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-flow-inspector")
|
||||
export class FlowInspector extends AKElement {
|
||||
@ -25,7 +25,7 @@ export class FlowInspector extends AKElement {
|
||||
state?: FlowInspection;
|
||||
|
||||
@property({ attribute: false })
|
||||
error?: Response;
|
||||
error?: ResponseError;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
@ -70,6 +70,7 @@ export class FlowInspector extends AKElement {
|
||||
flowSlug: this.flowSlug,
|
||||
})
|
||||
.then((state) => {
|
||||
this.error = undefined;
|
||||
this.state = state;
|
||||
})
|
||||
.catch((exc) => {
|
||||
@ -100,7 +101,7 @@ export class FlowInspector extends AKElement {
|
||||
<div class="pf-l-stack pf-m-gutter">
|
||||
<div class="pf-l-stack__item">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">${this.error?.statusText}</div>
|
||||
<div class="pf-c-card__body">${this.error?.message}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -6641,6 +6641,54 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s001fd928369d4ddc">
|
||||
<source><x id="0" equiv-text="${versionString}"/> (FIPS)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sce17c2bdea3e6ace">
|
||||
<source>Score minimum threshold</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf2fcb974c62625fc">
|
||||
<source>Minimum required score to allow continuing</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7452199a73ce8b78">
|
||||
<source>Score maximum threshold</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2d3a6cd50b46b44d">
|
||||
<source>Maximum allowed score to allow continuing</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9d6e40d9a9a58fbf">
|
||||
<source>Error on invalid score</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2af59e2f9f12847a">
|
||||
<source>When enabled and the resultant score is outside the threshold, the user will not be able to continue. When disabled, the user will be able to continue and the score can be used in policies to customize further stages.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s10092327ad833090">
|
||||
<source>Microsoft Entra Group(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s811c45ad24a5feae">
|
||||
<source>Microsoft Entra User(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7d2d3d298fdccfc8">
|
||||
<source>Google Workspace Group(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8c9cf2c004755526">
|
||||
<source>Google Workspace User(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd000b42fea07c34a">
|
||||
<source>SCIM Group(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s146769fb55f1ee50">
|
||||
<source>SCIM User(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s35f47dbd321aaf15">
|
||||
<source>FIPS compliance: passing</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc94578030c702562">
|
||||
<source>Unverified</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s16749cce7c4c1589">
|
||||
<source>FIPS compliance: unverified</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0b2ad58c3deaa8dd">
|
||||
<source>FIPS Status</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -6907,6 +6907,54 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s001fd928369d4ddc">
|
||||
<source><x id="0" equiv-text="${versionString}"/> (FIPS)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sce17c2bdea3e6ace">
|
||||
<source>Score minimum threshold</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf2fcb974c62625fc">
|
||||
<source>Minimum required score to allow continuing</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7452199a73ce8b78">
|
||||
<source>Score maximum threshold</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2d3a6cd50b46b44d">
|
||||
<source>Maximum allowed score to allow continuing</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9d6e40d9a9a58fbf">
|
||||
<source>Error on invalid score</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2af59e2f9f12847a">
|
||||
<source>When enabled and the resultant score is outside the threshold, the user will not be able to continue. When disabled, the user will be able to continue and the score can be used in policies to customize further stages.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s10092327ad833090">
|
||||
<source>Microsoft Entra Group(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s811c45ad24a5feae">
|
||||
<source>Microsoft Entra User(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7d2d3d298fdccfc8">
|
||||
<source>Google Workspace Group(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8c9cf2c004755526">
|
||||
<source>Google Workspace User(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd000b42fea07c34a">
|
||||
<source>SCIM Group(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s146769fb55f1ee50">
|
||||
<source>SCIM User(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s35f47dbd321aaf15">
|
||||
<source>FIPS compliance: passing</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc94578030c702562">
|
||||
<source>Unverified</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s16749cce7c4c1589">
|
||||
<source>FIPS compliance: unverified</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0b2ad58c3deaa8dd">
|
||||
<source>FIPS Status</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -6558,6 +6558,54 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s001fd928369d4ddc">
|
||||
<source><x id="0" equiv-text="${versionString}"/> (FIPS)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sce17c2bdea3e6ace">
|
||||
<source>Score minimum threshold</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf2fcb974c62625fc">
|
||||
<source>Minimum required score to allow continuing</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7452199a73ce8b78">
|
||||
<source>Score maximum threshold</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2d3a6cd50b46b44d">
|
||||
<source>Maximum allowed score to allow continuing</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9d6e40d9a9a58fbf">
|
||||
<source>Error on invalid score</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2af59e2f9f12847a">
|
||||
<source>When enabled and the resultant score is outside the threshold, the user will not be able to continue. When disabled, the user will be able to continue and the score can be used in policies to customize further stages.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s10092327ad833090">
|
||||
<source>Microsoft Entra Group(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s811c45ad24a5feae">
|
||||
<source>Microsoft Entra User(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7d2d3d298fdccfc8">
|
||||
<source>Google Workspace Group(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8c9cf2c004755526">
|
||||
<source>Google Workspace User(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd000b42fea07c34a">
|
||||
<source>SCIM Group(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s146769fb55f1ee50">
|
||||
<source>SCIM User(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s35f47dbd321aaf15">
|
||||
<source>FIPS compliance: passing</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc94578030c702562">
|
||||
<source>Unverified</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s16749cce7c4c1589">
|
||||
<source>FIPS compliance: unverified</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0b2ad58c3deaa8dd">
|
||||
<source>FIPS Status</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -8604,108 +8604,203 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
|
||||
</trans-unit>
|
||||
<trans-unit id="s8cfd29891b2c93f0">
|
||||
<source>Google Workspace Provider</source>
|
||||
<target>Fournisseur Google Workspace</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa817aa9f6f88a991">
|
||||
<source>Credentials</source>
|
||||
<target>Identifiants</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc668815044e218c2">
|
||||
<source>Delegated Subject</source>
|
||||
<target>Sujet délégué</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="se89a9ecf275e4343">
|
||||
<source>Default group email domain</source>
|
||||
<target>Domaine de courriel de groupe par défaut</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s64a0a6da6ed6d680">
|
||||
<source>Default domain that is used to generate a group's email address. Can be customized using property mappings.</source>
|
||||
<target>Domain par défaut utilisé pour générer le courriel d'un groupe. Peut être personnalisé avec des mappages de propriété.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd061e65955e036ca">
|
||||
<source>User deletion action</source>
|
||||
<target>Action de suppression d'un utilisateur</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s87f86f65b5e19ea7">
|
||||
<source>User is deleted</source>
|
||||
<target>L'utilisateur est supprimé</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3906cd4a102867a3">
|
||||
<source>Suspend</source>
|
||||
<target>Suspendre</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0bf5b364e2c79392">
|
||||
<source>User is suspended, and connection to user in authentik is removed.</source>
|
||||
<target>L'utilisateur est suspendu, et la connection à authentik est supprimée.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4acb0250d735c2a9">
|
||||
<source>Do Nothing</source>
|
||||
<target>Ne rien faire</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5fe525d19499ed47">
|
||||
<source>The connection is removed but the user is not modified</source>
|
||||
<target>La connexion est supprimée mais l'utilisateur n'est pas modifié</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s676a0127c35d240a">
|
||||
<source>Determines what authentik will do when a User is deleted.</source>
|
||||
<target>Détermine ce qu'authentik fera si un utilisateur est supprimé.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd1aa04cc32caf2b0">
|
||||
<source>Group deletion action</source>
|
||||
<target>Action de suppression d'un groupe</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s874c788479481131">
|
||||
<source>Group is deleted</source>
|
||||
<target>Le groupe est supprimé</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0983fa9b84e4a9f7">
|
||||
<source>The connection is removed but the group is not modified</source>
|
||||
<target>La connexion est supprimée mais le groupe n'est pas modifié</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sdb872a9f425937fa">
|
||||
<source>Determines what authentik will do when a Group is deleted.</source>
|
||||
<target>Détermine ce qu'authentik fera si un groupe est supprimé.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa5f28f1ad0bee45b">
|
||||
<source>Google Workspace Provider is in preview.</source>
|
||||
<target>Le fournisseur Google Workspace est en aperçu technique.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s958928ab6208d748">
|
||||
<source>Microsoft Entra Provider</source>
|
||||
<target>Fournisseur Microsoft Entra</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc7d6bc4aebb58fe9">
|
||||
<source>Google Cloud credentials file.</source>
|
||||
<target>Fichier d'identifiants Google Cloud.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2e707072d5a9615d">
|
||||
<source>Email address of the user the actions of authentik will be delegated to.</source>
|
||||
<target>Courriel de l'utilisateur auquel les actions d'authentik seront déléguées.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s03a759e65ceb722d">
|
||||
<source>Client ID for the app registration.</source>
|
||||
<target>Client ID pour l'enregistrement de l'application.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s479980cf9252628c">
|
||||
<source>Client secret for the app registration.</source>
|
||||
<target>Client secret pour l'enregistrement de l'application.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s25c2392ffcb78df2">
|
||||
<source>Tenant ID</source>
|
||||
<target>Tenant ID</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s89e4e698cdb1187f">
|
||||
<source>ID of the tenant accounts will be synced into.</source>
|
||||
<target>ID du tenant dans lequel les comptes seront synchronisés.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf341f5dfc7a11633">
|
||||
<source>Microsoft Entra Provider is in preview.</source>
|
||||
<target>Le fournisseur Microsoft Entra est en aperçu technique.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s79dd6df244c05ae9">
|
||||
<source>Update Microsoft Entra Provider</source>
|
||||
<target>Mettre à jour le fournisseur Microsoft Entra</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="saf4b498d81141878">
|
||||
<source>Finished successfully</source>
|
||||
<target>Fini avec succès</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s46f1d86ffec6223c">
|
||||
<source>Finished with errors</source>
|
||||
<target>Fini avec erreurs</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sbd12ed8a1053a108">
|
||||
<source>Finished <x id="0" equiv-text="${getRelativeTime(task.finishTimestamp)}"/> (<x id="1" equiv-text="${task.finishTimestamp.toLocaleString()}"/>)</source>
|
||||
<target>Fini <x id="0" equiv-text="${getRelativeTime(task.finishTimestamp)}"/> (<x id="1" equiv-text="${task.finishTimestamp.toLocaleString()}"/>)</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc3c334d642866997">
|
||||
<source>Sync currently running</source>
|
||||
<target>Synchronisation en cours</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd520a4089006ca93">
|
||||
<source>Update Google Workspace Provider</source>
|
||||
<target>Mettre à jour le fournisseur Google Workspace</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sfdfa5bb4ddd99d70">
|
||||
<source>Enterprise only</source>
|
||||
<target>Entreprise uniquement</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="scffa59cfac1951f2">
|
||||
<source><x id="0" equiv-text="${type.name}"/> Icon</source>
|
||||
<target>Icône <x id="0" equiv-text="${type.name}"/></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1da3499258024860">
|
||||
<source><x id="0" equiv-text="${versionString}"/> (build <x id="1" equiv-text="${this.outpostHealth.buildHash.substring(0, 8)}"/>)</source>
|
||||
<target><x id="0" equiv-text="${versionString}"/> (build <x id="1" equiv-text="${this.outpostHealth.buildHash.substring(0, 8)}"/>)</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s001fd928369d4ddc">
|
||||
<source><x id="0" equiv-text="${versionString}"/> (FIPS)</source>
|
||||
<target><x id="0" equiv-text="${versionString}"/> (FIPS)</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sce17c2bdea3e6ace">
|
||||
<source>Score minimum threshold</source>
|
||||
<target>Seuil minimum du score</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf2fcb974c62625fc">
|
||||
<source>Minimum required score to allow continuing</source>
|
||||
<target>Score minimum requis pour continuer</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7452199a73ce8b78">
|
||||
<source>Score maximum threshold</source>
|
||||
<target>Seuil maximum du score</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2d3a6cd50b46b44d">
|
||||
<source>Maximum allowed score to allow continuing</source>
|
||||
<target>Score maximum requis pour continuer</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9d6e40d9a9a58fbf">
|
||||
<source>Error on invalid score</source>
|
||||
<target>Erreur sur score invalide</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2af59e2f9f12847a">
|
||||
<source>When enabled and the resultant score is outside the threshold, the user will not be able to continue. When disabled, the user will be able to continue and the score can be used in policies to customize further stages.</source>
|
||||
<target>Si activé et que le score résultant est hors des seuils, l'utilisateur ne pourra pas continuer. Si désactivé, l'utilisateur pourra continuer et le score pourra être utilisé dans des politiques pour configurer les étapes suivantes.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s10092327ad833090">
|
||||
<source>Microsoft Entra Group(s)</source>
|
||||
<target>Groupe(s) du fournisseur Microsoft Entra</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s811c45ad24a5feae">
|
||||
<source>Microsoft Entra User(s)</source>
|
||||
<target>Utilisateur(s) du fournisseur Microsoft Entra</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7d2d3d298fdccfc8">
|
||||
<source>Google Workspace Group(s)</source>
|
||||
<target>Groupe(s) du fournisseur Google Workspace</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8c9cf2c004755526">
|
||||
<source>Google Workspace User(s)</source>
|
||||
<target>Utilisateur(s) du fournisseur Google Workspace</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd000b42fea07c34a">
|
||||
<source>SCIM Group(s)</source>
|
||||
<target>Groupe(s) du fournisseur SCIM</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s146769fb55f1ee50">
|
||||
<source>SCIM User(s)</source>
|
||||
<target>Utilisateur(s) du fournisseur SCIM</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s35f47dbd321aaf15">
|
||||
<source>FIPS compliance: passing</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc94578030c702562">
|
||||
<source>Unverified</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s16749cce7c4c1589">
|
||||
<source>FIPS compliance: unverified</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0b2ad58c3deaa8dd">
|
||||
<source>FIPS Status</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user