providers/sync: improve v3 (#9966)
* make external id field externally visible Signed-off-by: Jens Langhammer <jens@goauthentik.io> * catch up scim provider Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add missing views to scim provider Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make neither user nor group required for mapping testing Signed-off-by: Jens Langhammer <jens@goauthentik.io> * improve SkipObject handling Signed-off-by: Jens Langhammer <jens@goauthentik.io> * allow deletion of connection objects Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make entra logs less noisy Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make event_matcher less noisy Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
		| @ -58,7 +58,7 @@ from authentik.outposts.models import OutpostServiceConnection | |||||||
| from authentik.policies.models import Policy, PolicyBindingModel | from authentik.policies.models import Policy, PolicyBindingModel | ||||||
| from authentik.policies.reputation.models import Reputation | from authentik.policies.reputation.models import Reputation | ||||||
| from authentik.providers.oauth2.models import AccessToken, AuthorizationCode, RefreshToken | 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.sources.scim.models import SCIMSourceGroup, SCIMSourceUser | ||||||
| from authentik.stages.authenticator_webauthn.models import WebAuthnDeviceType | from authentik.stages.authenticator_webauthn.models import WebAuthnDeviceType | ||||||
| from authentik.tenants.models import Tenant | 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 |         # FIXME: these shouldn't need to be explicitly listed, but rather based off of a mixin | ||||||
|         FlowToken, |         FlowToken, | ||||||
|         LicenseUsage, |         LicenseUsage, | ||||||
|         SCIMGroup, |         SCIMProviderGroup, | ||||||
|         SCIMUser, |         SCIMProviderUser, | ||||||
|         Tenant, |         Tenant, | ||||||
|         SystemTask, |         SystemTask, | ||||||
|         ConnectionToken, |         ConnectionToken, | ||||||
|  | |||||||
| @ -80,8 +80,10 @@ class PropertyMappingViewSet( | |||||||
|     class PropertyMappingTestSerializer(PolicyTestSerializer): |     class PropertyMappingTestSerializer(PolicyTestSerializer): | ||||||
|         """Test property mapping execution for a user/group with context""" |         """Test property mapping execution for a user/group with context""" | ||||||
|  |  | ||||||
|         user = PrimaryKeyRelatedField(queryset=User.objects.all(), required=False) |         user = PrimaryKeyRelatedField(queryset=User.objects.all(), required=False, allow_null=True) | ||||||
|         group = PrimaryKeyRelatedField(queryset=Group.objects.all(), required=False) |         group = PrimaryKeyRelatedField( | ||||||
|  |             queryset=Group.objects.all(), required=False, allow_null=True | ||||||
|  |         ) | ||||||
|  |  | ||||||
|     queryset = PropertyMapping.objects.select_subclasses() |     queryset = PropertyMapping.objects.select_subclasses() | ||||||
|     serializer_class = PropertyMappingSerializer |     serializer_class = PropertyMappingSerializer | ||||||
|  | |||||||
| @ -19,6 +19,7 @@ class GoogleWorkspaceProviderGroupSerializer(ModelSerializer): | |||||||
|         model = GoogleWorkspaceProviderGroup |         model = GoogleWorkspaceProviderGroup | ||||||
|         fields = [ |         fields = [ | ||||||
|             "id", |             "id", | ||||||
|  |             "google_id", | ||||||
|             "group", |             "group", | ||||||
|             "group_obj", |             "group_obj", | ||||||
|             "provider", |             "provider", | ||||||
|  | |||||||
| @ -19,6 +19,7 @@ class GoogleWorkspaceProviderUserSerializer(ModelSerializer): | |||||||
|         model = GoogleWorkspaceProviderUser |         model = GoogleWorkspaceProviderUser | ||||||
|         fields = [ |         fields = [ | ||||||
|             "id", |             "id", | ||||||
|  |             "google_id", | ||||||
|             "user", |             "user", | ||||||
|             "user_obj", |             "user_obj", | ||||||
|             "provider", |             "provider", | ||||||
|  | |||||||
| @ -19,6 +19,7 @@ class MicrosoftEntraProviderGroupSerializer(ModelSerializer): | |||||||
|         model = MicrosoftEntraProviderGroup |         model = MicrosoftEntraProviderGroup | ||||||
|         fields = [ |         fields = [ | ||||||
|             "id", |             "id", | ||||||
|  |             "microsoft_id", | ||||||
|             "group", |             "group", | ||||||
|             "group_obj", |             "group_obj", | ||||||
|             "provider", |             "provider", | ||||||
|  | |||||||
| @ -19,6 +19,7 @@ class MicrosoftEntraProviderUserSerializer(ModelSerializer): | |||||||
|         model = MicrosoftEntraProviderUser |         model = MicrosoftEntraProviderUser | ||||||
|         fields = [ |         fields = [ | ||||||
|             "id", |             "id", | ||||||
|  |             "microsoft_id", | ||||||
|             "user", |             "user", | ||||||
|             "user_obj", |             "user_obj", | ||||||
|             "provider", |             "provider", | ||||||
|  | |||||||
| @ -102,6 +102,8 @@ def get_logger_config(): | |||||||
|         "gunicorn": "INFO", |         "gunicorn": "INFO", | ||||||
|         "requests_mock": "WARNING", |         "requests_mock": "WARNING", | ||||||
|         "hpack": "WARNING", |         "hpack": "WARNING", | ||||||
|  |         "httpx": "WARNING", | ||||||
|  |         "azure": "WARNING", | ||||||
|     } |     } | ||||||
|     for handler_name, level in handler_level_map.items(): |     for handler_name, level in handler_level_map.items(): | ||||||
|         base_config["loggers"][handler_name] = { |         base_config["loggers"][handler_name] = { | ||||||
|  | |||||||
| @ -57,6 +57,8 @@ class PropertyMappingManager: | |||||||
|             mapping.set_context(user, request, **kwargs) |             mapping.set_context(user, request, **kwargs) | ||||||
|             try: |             try: | ||||||
|                 value = mapping.evaluate(mapping.model.expression) |                 value = mapping.evaluate(mapping.model.expression) | ||||||
|  |             except PropertyMappingExpressionException as exc: | ||||||
|  |                 raise exc from exc | ||||||
|             except Exception as exc: |             except Exception as exc: | ||||||
|                 raise PropertyMappingExpressionException(exc, mapping.model) from exc |                 raise PropertyMappingExpressionException(exc, mapping.model) from exc | ||||||
|             if value is None: |             if value is None: | ||||||
|  | |||||||
| @ -91,7 +91,6 @@ class BaseOutgoingSyncClient[ | |||||||
|             } |             } | ||||||
|             eval_kwargs.setdefault("user", None) |             eval_kwargs.setdefault("user", None) | ||||||
|             for value in self.mapper.iter_eval(**eval_kwargs): |             for value in self.mapper.iter_eval(**eval_kwargs): | ||||||
|                 try: |  | ||||||
|                 always_merger.merge(raw_final_object, value) |                 always_merger.merge(raw_final_object, value) | ||||||
|         except SkipObjectException as exc: |         except SkipObjectException as exc: | ||||||
|             raise exc from exc |             raise exc from exc | ||||||
| @ -104,7 +103,7 @@ class BaseOutgoingSyncClient[ | |||||||
|             ).save() |             ).save() | ||||||
|             raise StopSync(exc, obj, exc.mapping) from exc |             raise StopSync(exc, obj, exc.mapping) from exc | ||||||
|         if not raw_final_object: |         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(): |         for key, value in defaults.items(): | ||||||
|             raw_final_object.setdefault(key, value) |             raw_final_object.setdefault(key, value) | ||||||
|         return raw_final_object |         return raw_final_object | ||||||
|  | |||||||
| @ -125,6 +125,7 @@ class SyncTasks: | |||||||
|             try: |             try: | ||||||
|                 client.write(obj) |                 client.write(obj) | ||||||
|             except SkipObjectException: |             except SkipObjectException: | ||||||
|  |                 self.logger.debug("skipping object due to SkipObject", obj=obj) | ||||||
|                 continue |                 continue | ||||||
|             except BadRequestSyncException as exc: |             except BadRequestSyncException as exc: | ||||||
|                 self.logger.warning("failed to sync object", exc=exc, obj=obj) |                 self.logger.warning("failed to sync object", exc=exc, obj=obj) | ||||||
|  | |||||||
| @ -102,7 +102,7 @@ class EventMatcherPolicy(Policy): | |||||||
|             result = checker(request, event) |             result = checker(request, event) | ||||||
|             if result is None: |             if result is None: | ||||||
|                 continue |                 continue | ||||||
|             LOGGER.info( |             LOGGER.debug( | ||||||
|                 "Event matcher check result", |                 "Event matcher check result", | ||||||
|                 checker=checker.__name__, |                 checker=checker.__name__, | ||||||
|                 result=result, |                 result=result, | ||||||
|  | |||||||
							
								
								
									
										43
									
								
								authentik/providers/scim/api/groups.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								authentik/providers/scim/api/groups.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | |||||||
|  | """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.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, | ||||||
|  |     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"] | ||||||
							
								
								
									
										43
									
								
								authentik/providers/scim/api/users.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								authentik/providers/scim/api/users.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | |||||||
|  | """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.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, | ||||||
|  |     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 SCIM_GROUP_SCHEMA, PatchRequest | ||||||
| from authentik.providers.scim.clients.schema import Group as SCIMGroupSchema | 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""" |     """SCIM client for groups""" | ||||||
|  |  | ||||||
|     connection_type = SCIMGroup |     connection_type = SCIMProviderGroup | ||||||
|     connection_type_query = "group" |     connection_type_query = "group" | ||||||
|     mapper: PropertyMappingManager |     mapper: PropertyMappingManager | ||||||
|  |  | ||||||
| @ -37,7 +42,7 @@ class SCIMGroupClient(SCIMClient[Group, SCIMGroup, SCIMGroupSchema]): | |||||||
|             ["group", "provider", "connection"], |             ["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""" |         """Convert authentik user into SCIM""" | ||||||
|         raw_scim_group = super().to_schema( |         raw_scim_group = super().to_schema( | ||||||
|             obj, |             obj, | ||||||
| @ -52,7 +57,7 @@ class SCIMGroupClient(SCIMClient[Group, SCIMGroup, SCIMGroupSchema]): | |||||||
|             scim_group.externalId = str(obj.pk) |             scim_group.externalId = str(obj.pk) | ||||||
|  |  | ||||||
|         users = list(obj.users.order_by("id").values_list("id", flat=True)) |         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 = [] |         members = [] | ||||||
|         for user in connections: |         for user in connections: | ||||||
|             members.append( |             members.append( | ||||||
| @ -66,7 +71,7 @@ class SCIMGroupClient(SCIMClient[Group, SCIMGroup, SCIMGroupSchema]): | |||||||
|  |  | ||||||
|     def delete(self, obj: Group): |     def delete(self, obj: Group): | ||||||
|         """Delete 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: |         if not scim_group: | ||||||
|             self.logger.debug("Group does not exist in SCIM, skipping") |             self.logger.debug("Group does not exist in SCIM, skipping") | ||||||
|             return None |             return None | ||||||
| @ -88,9 +93,11 @@ class SCIMGroupClient(SCIMClient[Group, SCIMGroup, SCIMGroupSchema]): | |||||||
|         scim_id = response.get("id") |         scim_id = response.get("id") | ||||||
|         if not scim_id or scim_id == "": |         if not scim_id or scim_id == "": | ||||||
|             raise StopSync("SCIM Response with missing or invalid `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""" |         """Update existing group""" | ||||||
|         scim_group = self.to_schema(group, connection) |         scim_group = self.to_schema(group, connection) | ||||||
|         scim_group.id = connection.scim_id |         scim_group.id = connection.scim_id | ||||||
| @ -158,16 +165,16 @@ class SCIMGroupClient(SCIMClient[Group, SCIMGroup, SCIMGroupSchema]): | |||||||
|         """Add users in users_set to group""" |         """Add users in users_set to group""" | ||||||
|         if len(users_set) < 1: |         if len(users_set) < 1: | ||||||
|             return |             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: |         if not scim_group: | ||||||
|             self.logger.warning( |             self.logger.warning( | ||||||
|                 "could not sync group membership, group does not exist", group=group |                 "could not sync group membership, group does not exist", group=group | ||||||
|             ) |             ) | ||||||
|             return |             return | ||||||
|         user_ids = list( |         user_ids = list( | ||||||
|             SCIMUser.objects.filter(user__pk__in=users_set, provider=self.provider).values_list( |             SCIMProviderUser.objects.filter( | ||||||
|                 "scim_id", flat=True |                 user__pk__in=users_set, provider=self.provider | ||||||
|             ) |             ).values_list("scim_id", flat=True) | ||||||
|         ) |         ) | ||||||
|         if len(user_ids) < 1: |         if len(user_ids) < 1: | ||||||
|             return |             return | ||||||
| @ -184,16 +191,16 @@ class SCIMGroupClient(SCIMClient[Group, SCIMGroup, SCIMGroupSchema]): | |||||||
|         """Remove users in users_set from group""" |         """Remove users in users_set from group""" | ||||||
|         if len(users_set) < 1: |         if len(users_set) < 1: | ||||||
|             return |             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: |         if not scim_group: | ||||||
|             self.logger.warning( |             self.logger.warning( | ||||||
|                 "could not sync group membership, group does not exist", group=group |                 "could not sync group membership, group does not exist", group=group | ||||||
|             ) |             ) | ||||||
|             return |             return | ||||||
|         user_ids = list( |         user_ids = list( | ||||||
|             SCIMUser.objects.filter(user__pk__in=users_set, provider=self.provider).values_list( |             SCIMProviderUser.objects.filter( | ||||||
|                 "scim_id", flat=True |                 user__pk__in=users_set, provider=self.provider | ||||||
|             ) |             ).values_list("scim_id", flat=True) | ||||||
|         ) |         ) | ||||||
|         if len(user_ids) < 1: |         if len(user_ids) < 1: | ||||||
|             return |             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.base import SCIMClient | ||||||
| from authentik.providers.scim.clients.schema import SCIM_USER_SCHEMA | from authentik.providers.scim.clients.schema import SCIM_USER_SCHEMA | ||||||
| from authentik.providers.scim.clients.schema import User as SCIMUserSchema | 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""" |     """SCIM client for users""" | ||||||
|  |  | ||||||
|     connection_type = SCIMUser |     connection_type = SCIMProviderUser | ||||||
|     connection_type_query = "user" |     connection_type_query = "user" | ||||||
|     mapper: PropertyMappingManager |     mapper: PropertyMappingManager | ||||||
|  |  | ||||||
| @ -27,7 +27,7 @@ class SCIMUserClient(SCIMClient[User, SCIMUser, SCIMUserSchema]): | |||||||
|             ["provider", "connection"], |             ["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""" |         """Convert authentik user into SCIM""" | ||||||
|         raw_scim_user = super().to_schema( |         raw_scim_user = super().to_schema( | ||||||
|             obj, |             obj, | ||||||
| @ -44,7 +44,7 @@ class SCIMUserClient(SCIMClient[User, SCIMUser, SCIMUserSchema]): | |||||||
|  |  | ||||||
|     def delete(self, obj: User): |     def delete(self, obj: User): | ||||||
|         """Delete 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: |         if not scim_user: | ||||||
|             self.logger.debug("User does not exist in SCIM, skipping") |             self.logger.debug("User does not exist in SCIM, skipping") | ||||||
|             return None |             return None | ||||||
| @ -66,9 +66,9 @@ class SCIMUserClient(SCIMClient[User, SCIMUser, SCIMUserSchema]): | |||||||
|         scim_id = response.get("id") |         scim_id = response.get("id") | ||||||
|         if not scim_id or scim_id == "": |         if not scim_id or scim_id == "": | ||||||
|             raise StopSync("SCIM Response with missing or invalid `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""" |         """Update existing user""" | ||||||
|         scim_user = self.to_schema(user, connection) |         scim_user = self.to_schema(user, connection) | ||||||
|         scim_user.id = connection.scim_id |         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,6 +10,7 @@ from django.utils.translation import gettext_lazy as _ | |||||||
| from rest_framework.serializers import Serializer | from rest_framework.serializers import Serializer | ||||||
|  |  | ||||||
| from authentik.core.models import BackchannelProvider, Group, PropertyMapping, User, UserTypes | 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.base import BaseOutgoingSyncClient | ||||||
| from authentik.lib.sync.outgoing.models import OutgoingSyncProvider | from authentik.lib.sync.outgoing.models import OutgoingSyncProvider | ||||||
|  |  | ||||||
| @ -106,7 +107,7 @@ class SCIMMapping(PropertyMapping): | |||||||
|         verbose_name_plural = _("SCIM Mappings") |         verbose_name_plural = _("SCIM Mappings") | ||||||
|  |  | ||||||
|  |  | ||||||
| class SCIMUser(models.Model): | class SCIMProviderUser(SerializerModel): | ||||||
|     """Mapping of a user and provider to a SCIM user ID""" |     """Mapping of a user and provider to a SCIM user ID""" | ||||||
|  |  | ||||||
|     id = models.UUIDField(primary_key=True, editable=False, default=uuid4) |     id = models.UUIDField(primary_key=True, editable=False, default=uuid4) | ||||||
| @ -114,14 +115,20 @@ class SCIMUser(models.Model): | |||||||
|     user = models.ForeignKey(User, on_delete=models.CASCADE) |     user = models.ForeignKey(User, on_delete=models.CASCADE) | ||||||
|     provider = models.ForeignKey(SCIMProvider, 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: |     class Meta: | ||||||
|         unique_together = (("scim_id", "user", "provider"),) |         unique_together = (("scim_id", "user", "provider"),) | ||||||
|  |  | ||||||
|     def __str__(self) -> str: |     def __str__(self) -> str: | ||||||
|         return f"SCIM User {self.user_id} to {self.provider_id}" |         return f"SCIM Provider User {self.user_id} to {self.provider_id}" | ||||||
|  |  | ||||||
|  |  | ||||||
| class SCIMGroup(models.Model): | class SCIMProviderGroup(SerializerModel): | ||||||
|     """Mapping of a group and provider to a SCIM user ID""" |     """Mapping of a group and provider to a SCIM user ID""" | ||||||
|  |  | ||||||
|     id = models.UUIDField(primary_key=True, editable=False, default=uuid4) |     id = models.UUIDField(primary_key=True, editable=False, default=uuid4) | ||||||
| @ -129,8 +136,14 @@ class SCIMGroup(models.Model): | |||||||
|     group = models.ForeignKey(Group, on_delete=models.CASCADE) |     group = models.ForeignKey(Group, on_delete=models.CASCADE) | ||||||
|     provider = models.ForeignKey(SCIMProvider, 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: |     class Meta: | ||||||
|         unique_together = (("scim_id", "group", "provider"),) |         unique_together = (("scim_id", "group", "provider"),) | ||||||
|  |  | ||||||
|     def __str__(self) -> str: |     def __str__(self) -> str: | ||||||
|         return f"SCIM Group {self.group_id} to {self.provider_id}" |         return f"SCIM Provider Group {self.group_id} to {self.provider_id}" | ||||||
|  | |||||||
| @ -1,9 +1,17 @@ | |||||||
| """API URLs""" | """API URLs""" | ||||||
|  |  | ||||||
|  | from authentik.providers.scim.api.groups import ( | ||||||
|  |     SCIMProviderGroupViewSet, | ||||||
|  | ) | ||||||
| from authentik.providers.scim.api.property_mappings import SCIMMappingViewSet | from authentik.providers.scim.api.property_mappings import SCIMMappingViewSet | ||||||
| from authentik.providers.scim.api.providers import SCIMProviderViewSet | from authentik.providers.scim.api.providers import SCIMProviderViewSet | ||||||
|  | from authentik.providers.scim.api.users import ( | ||||||
|  |     SCIMProviderUserViewSet, | ||||||
|  | ) | ||||||
|  |  | ||||||
| api_urlpatterns = [ | api_urlpatterns = [ | ||||||
|     ("providers/scim", SCIMProviderViewSet), |     ("providers/scim", SCIMProviderViewSet), | ||||||
|  |     ("providers/scim_users", SCIMProviderUserViewSet), | ||||||
|  |     ("providers/scim_groups", SCIMProviderGroupViewSet), | ||||||
|     ("propertymappings/scim", SCIMMappingViewSet), |     ("propertymappings/scim", SCIMMappingViewSet), | ||||||
| ] | ] | ||||||
|  | |||||||
| @ -161,7 +161,6 @@ class BaseLDAPSynchronizer: | |||||||
|                 dn=object_dn, |                 dn=object_dn, | ||||||
|                 source=self._source, |                 source=self._source, | ||||||
|             ): |             ): | ||||||
|                 try: |  | ||||||
|                 if isinstance(value, (bytes)): |                 if isinstance(value, (bytes)): | ||||||
|                     self._logger.warning("property mapping returned bytes", mapping=mapping) |                     self._logger.warning("property mapping returned bytes", mapping=mapping) | ||||||
|                     continue |                     continue | ||||||
|  | |||||||
							
								
								
									
										531
									
								
								schema.yml
									
									
									
									
									
								
							
							
						
						
									
										531
									
								
								schema.yml
									
									
									
									
									
								
							| @ -19726,6 +19726,403 @@ paths: | |||||||
|               schema: |               schema: | ||||||
|                 $ref: '#/components/schemas/GenericError' |                 $ref: '#/components/schemas/GenericError' | ||||||
|           description: '' |           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/: |   /rac/connection_tokens/: | ||||||
|     get: |     get: | ||||||
|       operationId: rac_connection_tokens_list |       operationId: rac_connection_tokens_list | ||||||
| @ -36335,6 +36732,8 @@ components: | |||||||
|           type: string |           type: string | ||||||
|           format: uuid |           format: uuid | ||||||
|           readOnly: true |           readOnly: true | ||||||
|  |         google_id: | ||||||
|  |           type: string | ||||||
|         group: |         group: | ||||||
|           type: string |           type: string | ||||||
|           format: uuid |           format: uuid | ||||||
| @ -36348,6 +36747,7 @@ components: | |||||||
|           readOnly: true |           readOnly: true | ||||||
|       required: |       required: | ||||||
|       - attributes |       - attributes | ||||||
|  |       - google_id | ||||||
|       - group |       - group | ||||||
|       - group_obj |       - group_obj | ||||||
|       - id |       - id | ||||||
| @ -36356,12 +36756,16 @@ components: | |||||||
|       type: object |       type: object | ||||||
|       description: GoogleWorkspaceProviderGroup Serializer |       description: GoogleWorkspaceProviderGroup Serializer | ||||||
|       properties: |       properties: | ||||||
|  |         google_id: | ||||||
|  |           type: string | ||||||
|  |           minLength: 1 | ||||||
|         group: |         group: | ||||||
|           type: string |           type: string | ||||||
|           format: uuid |           format: uuid | ||||||
|         provider: |         provider: | ||||||
|           type: integer |           type: integer | ||||||
|       required: |       required: | ||||||
|  |       - google_id | ||||||
|       - group |       - group | ||||||
|       - provider |       - provider | ||||||
|     GoogleWorkspaceProviderMapping: |     GoogleWorkspaceProviderMapping: | ||||||
| @ -36484,6 +36888,8 @@ components: | |||||||
|           type: string |           type: string | ||||||
|           format: uuid |           format: uuid | ||||||
|           readOnly: true |           readOnly: true | ||||||
|  |         google_id: | ||||||
|  |           type: string | ||||||
|         user: |         user: | ||||||
|           type: integer |           type: integer | ||||||
|         user_obj: |         user_obj: | ||||||
| @ -36496,6 +36902,7 @@ components: | |||||||
|           readOnly: true |           readOnly: true | ||||||
|       required: |       required: | ||||||
|       - attributes |       - attributes | ||||||
|  |       - google_id | ||||||
|       - id |       - id | ||||||
|       - provider |       - provider | ||||||
|       - user |       - user | ||||||
| @ -36504,11 +36911,15 @@ components: | |||||||
|       type: object |       type: object | ||||||
|       description: GoogleWorkspaceProviderUser Serializer |       description: GoogleWorkspaceProviderUser Serializer | ||||||
|       properties: |       properties: | ||||||
|  |         google_id: | ||||||
|  |           type: string | ||||||
|  |           minLength: 1 | ||||||
|         user: |         user: | ||||||
|           type: integer |           type: integer | ||||||
|         provider: |         provider: | ||||||
|           type: integer |           type: integer | ||||||
|       required: |       required: | ||||||
|  |       - google_id | ||||||
|       - provider |       - provider | ||||||
|       - user |       - user | ||||||
|     Group: |     Group: | ||||||
| @ -38003,6 +38414,8 @@ components: | |||||||
|           type: string |           type: string | ||||||
|           format: uuid |           format: uuid | ||||||
|           readOnly: true |           readOnly: true | ||||||
|  |         microsoft_id: | ||||||
|  |           type: string | ||||||
|         group: |         group: | ||||||
|           type: string |           type: string | ||||||
|           format: uuid |           format: uuid | ||||||
| @ -38019,11 +38432,15 @@ components: | |||||||
|       - group |       - group | ||||||
|       - group_obj |       - group_obj | ||||||
|       - id |       - id | ||||||
|  |       - microsoft_id | ||||||
|       - provider |       - provider | ||||||
|     MicrosoftEntraProviderGroupRequest: |     MicrosoftEntraProviderGroupRequest: | ||||||
|       type: object |       type: object | ||||||
|       description: MicrosoftEntraProviderGroup Serializer |       description: MicrosoftEntraProviderGroup Serializer | ||||||
|       properties: |       properties: | ||||||
|  |         microsoft_id: | ||||||
|  |           type: string | ||||||
|  |           minLength: 1 | ||||||
|         group: |         group: | ||||||
|           type: string |           type: string | ||||||
|           format: uuid |           format: uuid | ||||||
| @ -38031,6 +38448,7 @@ components: | |||||||
|           type: integer |           type: integer | ||||||
|       required: |       required: | ||||||
|       - group |       - group | ||||||
|  |       - microsoft_id | ||||||
|       - provider |       - provider | ||||||
|     MicrosoftEntraProviderMapping: |     MicrosoftEntraProviderMapping: | ||||||
|       type: object |       type: object | ||||||
| @ -38149,6 +38567,8 @@ components: | |||||||
|           type: string |           type: string | ||||||
|           format: uuid |           format: uuid | ||||||
|           readOnly: true |           readOnly: true | ||||||
|  |         microsoft_id: | ||||||
|  |           type: string | ||||||
|         user: |         user: | ||||||
|           type: integer |           type: integer | ||||||
|         user_obj: |         user_obj: | ||||||
| @ -38162,6 +38582,7 @@ components: | |||||||
|       required: |       required: | ||||||
|       - attributes |       - attributes | ||||||
|       - id |       - id | ||||||
|  |       - microsoft_id | ||||||
|       - provider |       - provider | ||||||
|       - user |       - user | ||||||
|       - user_obj |       - user_obj | ||||||
| @ -38169,11 +38590,15 @@ components: | |||||||
|       type: object |       type: object | ||||||
|       description: MicrosoftEntraProviderUser Serializer |       description: MicrosoftEntraProviderUser Serializer | ||||||
|       properties: |       properties: | ||||||
|  |         microsoft_id: | ||||||
|  |           type: string | ||||||
|  |           minLength: 1 | ||||||
|         user: |         user: | ||||||
|           type: integer |           type: integer | ||||||
|         provider: |         provider: | ||||||
|           type: integer |           type: integer | ||||||
|       required: |       required: | ||||||
|  |       - microsoft_id | ||||||
|       - provider |       - provider | ||||||
|       - user |       - user | ||||||
|     ModelEnum: |     ModelEnum: | ||||||
| @ -40166,6 +40591,18 @@ components: | |||||||
|       required: |       required: | ||||||
|       - pagination |       - pagination | ||||||
|       - results |       - results | ||||||
|  |     PaginatedSCIMProviderGroupList: | ||||||
|  |       type: object | ||||||
|  |       properties: | ||||||
|  |         pagination: | ||||||
|  |           $ref: '#/components/schemas/Pagination' | ||||||
|  |         results: | ||||||
|  |           type: array | ||||||
|  |           items: | ||||||
|  |             $ref: '#/components/schemas/SCIMProviderGroup' | ||||||
|  |       required: | ||||||
|  |       - pagination | ||||||
|  |       - results | ||||||
|     PaginatedSCIMProviderList: |     PaginatedSCIMProviderList: | ||||||
|       type: object |       type: object | ||||||
|       properties: |       properties: | ||||||
| @ -40178,6 +40615,18 @@ components: | |||||||
|       required: |       required: | ||||||
|       - pagination |       - pagination | ||||||
|       - results |       - results | ||||||
|  |     PaginatedSCIMProviderUserList: | ||||||
|  |       type: object | ||||||
|  |       properties: | ||||||
|  |         pagination: | ||||||
|  |           $ref: '#/components/schemas/Pagination' | ||||||
|  |         results: | ||||||
|  |           type: array | ||||||
|  |           items: | ||||||
|  |             $ref: '#/components/schemas/SCIMProviderUser' | ||||||
|  |       required: | ||||||
|  |       - pagination | ||||||
|  |       - results | ||||||
|     PaginatedSCIMSourceGroupList: |     PaginatedSCIMSourceGroupList: | ||||||
|       type: object |       type: object | ||||||
|       properties: |       properties: | ||||||
| @ -44147,12 +44596,14 @@ components: | |||||||
|       properties: |       properties: | ||||||
|         user: |         user: | ||||||
|           type: integer |           type: integer | ||||||
|  |           nullable: true | ||||||
|         context: |         context: | ||||||
|           type: object |           type: object | ||||||
|           additionalProperties: {} |           additionalProperties: {} | ||||||
|         group: |         group: | ||||||
|           type: string |           type: string | ||||||
|           format: uuid |           format: uuid | ||||||
|  |           nullable: true | ||||||
|     PropertyMappingTestResult: |     PropertyMappingTestResult: | ||||||
|       type: object |       type: object | ||||||
|       description: Result of a Property-mapping test |       description: Result of a Property-mapping test | ||||||
| @ -45906,6 +46357,47 @@ components: | |||||||
|       - url |       - url | ||||||
|       - verbose_name |       - verbose_name | ||||||
|       - verbose_name_plural |       - 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: |     SCIMProviderRequest: | ||||||
|       type: object |       type: object | ||||||
|       description: SCIMProvider Serializer |       description: SCIMProvider Serializer | ||||||
| @ -45942,6 +46434,45 @@ components: | |||||||
|       - name |       - name | ||||||
|       - token |       - token | ||||||
|       - url |       - 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: |     SCIMSource: | ||||||
|       type: object |       type: object | ||||||
|       description: SCIMSource Serializer |       description: SCIMSource Serializer | ||||||
|  | |||||||
| @ -126,6 +126,7 @@ export class PolicyTestForm extends Form<PropertyMappingTestRequest> { | |||||||
|     renderForm(): TemplateResult { |     renderForm(): TemplateResult { | ||||||
|         return html`<ak-form-element-horizontal label=${msg("User")} name="user"> |         return html`<ak-form-element-horizontal label=${msg("User")} name="user"> | ||||||
|                 <ak-search-select |                 <ak-search-select | ||||||
|  |                     blankable | ||||||
|                     .fetchObjects=${async (query?: string): Promise<User[]> => { |                     .fetchObjects=${async (query?: string): Promise<User[]> => { | ||||||
|                         const args: CoreUsersListRequest = { |                         const args: CoreUsersListRequest = { | ||||||
|                             ordering: "username", |                             ordering: "username", | ||||||
| @ -153,6 +154,7 @@ export class PolicyTestForm extends Form<PropertyMappingTestRequest> { | |||||||
|             </ak-form-element-horizontal> |             </ak-form-element-horizontal> | ||||||
|             <ak-form-element-horizontal label=${msg("Group")} name="group"> |             <ak-form-element-horizontal label=${msg("Group")} name="group"> | ||||||
|                 <ak-search-select |                 <ak-search-select | ||||||
|  |                     blankable | ||||||
|                     .fetchObjects=${async (query?: string): Promise<Group[]> => { |                     .fetchObjects=${async (query?: string): Promise<Group[]> => { | ||||||
|                         const args: CoreGroupsListRequest = { |                         const args: CoreGroupsListRequest = { | ||||||
|                             ordering: "name", |                             ordering: "name", | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||||
| import { uiConfig } from "@goauthentik/common/ui/config"; | import { uiConfig } from "@goauthentik/common/ui/config"; | ||||||
|  | import "@goauthentik/elements/forms/DeleteBulkForm"; | ||||||
| import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table"; | import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table"; | ||||||
|  |  | ||||||
| import { msg } from "@lit/localize"; | import { msg } from "@lit/localize"; | ||||||
| @ -19,6 +20,26 @@ export class GoogleWorkspaceProviderGroupList extends Table<GoogleWorkspaceProvi | |||||||
|         return true; |         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>> { |     async apiEndpoint(page: number): Promise<PaginatedResponse<GoogleWorkspaceProviderGroup>> { | ||||||
|         return new ProvidersApi(DEFAULT_CONFIG).providersGoogleWorkspaceGroupsList({ |         return new ProvidersApi(DEFAULT_CONFIG).providersGoogleWorkspaceGroupsList({ | ||||||
|             page: page, |             page: page, | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||||
| import { uiConfig } from "@goauthentik/common/ui/config"; | import { uiConfig } from "@goauthentik/common/ui/config"; | ||||||
|  | import "@goauthentik/elements/forms/DeleteBulkForm"; | ||||||
| import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table"; | import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table"; | ||||||
|  |  | ||||||
| import { msg } from "@lit/localize"; | import { msg } from "@lit/localize"; | ||||||
| @ -19,6 +20,26 @@ export class GoogleWorkspaceProviderUserList extends Table<GoogleWorkspaceProvid | |||||||
|  |  | ||||||
|     expandable = true; |     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>> { |     async apiEndpoint(page: number): Promise<PaginatedResponse<GoogleWorkspaceProviderUser>> { | ||||||
|         return new ProvidersApi(DEFAULT_CONFIG).providersGoogleWorkspaceUsersList({ |         return new ProvidersApi(DEFAULT_CONFIG).providersGoogleWorkspaceUsersList({ | ||||||
|             page: page, |             page: page, | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||||
| import { uiConfig } from "@goauthentik/common/ui/config"; | import { uiConfig } from "@goauthentik/common/ui/config"; | ||||||
|  | import "@goauthentik/elements/forms/DeleteBulkForm"; | ||||||
| import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table"; | import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table"; | ||||||
|  |  | ||||||
| import { msg } from "@lit/localize"; | import { msg } from "@lit/localize"; | ||||||
| @ -19,6 +20,23 @@ export class MicrosoftEntraProviderGroupList extends Table<MicrosoftEntraProvide | |||||||
|         return true; |         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>> { |     async apiEndpoint(page: number): Promise<PaginatedResponse<MicrosoftEntraProviderGroup>> { | ||||||
|         return new ProvidersApi(DEFAULT_CONFIG).providersMicrosoftEntraGroupsList({ |         return new ProvidersApi(DEFAULT_CONFIG).providersMicrosoftEntraGroupsList({ | ||||||
|             page: page, |             page: page, | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||||
| import { uiConfig } from "@goauthentik/common/ui/config"; | import { uiConfig } from "@goauthentik/common/ui/config"; | ||||||
|  | import "@goauthentik/elements/forms/DeleteBulkForm"; | ||||||
| import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table"; | import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table"; | ||||||
|  |  | ||||||
| import { msg } from "@lit/localize"; | import { msg } from "@lit/localize"; | ||||||
| @ -19,6 +20,26 @@ export class MicrosoftEntraProviderUserList extends Table<MicrosoftEntraProvider | |||||||
|         return true; |         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>> { |     async apiEndpoint(page: number): Promise<PaginatedResponse<MicrosoftEntraProviderUser>> { | ||||||
|         return new ProvidersApi(DEFAULT_CONFIG).providersMicrosoftEntraUsersList({ |         return new ProvidersApi(DEFAULT_CONFIG).providersMicrosoftEntraUsersList({ | ||||||
|             page: page, |             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-google-workspace-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/SCIMProviderForm"; | ||||||
|  | import "@goauthentik/admin/providers/scim/SCIMProviderGroupList"; | ||||||
|  | import "@goauthentik/admin/providers/scim/SCIMProviderUserList"; | ||||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||||
| import { EVENT_REFRESH } from "@goauthentik/common/constants"; | import { EVENT_REFRESH } from "@goauthentik/common/constants"; | ||||||
| import "@goauthentik/components/events/ObjectChangelog"; | import "@goauthentik/components/events/ObjectChangelog"; | ||||||
| @ -102,6 +104,28 @@ export class SCIMProviderViewPage extends AKElement { | |||||||
|                     </div> |                     </div> | ||||||
|                 </div> |                 </div> | ||||||
|             </section> |             </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 |             <ak-rbac-object-permission-page | ||||||
|                 slot="page-permissions" |                 slot="page-permissions" | ||||||
|                 data-tab-title="${msg("Permissions")}" |                 data-tab-title="${msg("Permissions")}" | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Jens L
					Jens L