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.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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -19,6 +19,7 @@ class GoogleWorkspaceProviderGroupSerializer(ModelSerializer):
|
||||
model = GoogleWorkspaceProviderGroup
|
||||
fields = [
|
||||
"id",
|
||||
"google_id",
|
||||
"group",
|
||||
"group_obj",
|
||||
"provider",
|
||||
|
||||
@ -19,6 +19,7 @@ class GoogleWorkspaceProviderUserSerializer(ModelSerializer):
|
||||
model = GoogleWorkspaceProviderUser
|
||||
fields = [
|
||||
"id",
|
||||
"google_id",
|
||||
"user",
|
||||
"user_obj",
|
||||
"provider",
|
||||
|
||||
@ -19,6 +19,7 @@ class MicrosoftEntraProviderGroupSerializer(ModelSerializer):
|
||||
model = MicrosoftEntraProviderGroup
|
||||
fields = [
|
||||
"id",
|
||||
"microsoft_id",
|
||||
"group",
|
||||
"group_obj",
|
||||
"provider",
|
||||
|
||||
@ -19,6 +19,7 @@ class MicrosoftEntraProviderUserSerializer(ModelSerializer):
|
||||
model = MicrosoftEntraProviderUser
|
||||
fields = [
|
||||
"id",
|
||||
"microsoft_id",
|
||||
"user",
|
||||
"user_obj",
|
||||
"provider",
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
|
||||
@ -125,6 +125,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)
|
||||
|
||||
@ -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,
|
||||
|
||||
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 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,6 +10,7 @@ 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
|
||||
|
||||
@ -106,7 +107,7 @@ class SCIMMapping(PropertyMapping):
|
||||
verbose_name_plural = _("SCIM Mappings")
|
||||
|
||||
|
||||
class SCIMUser(models.Model):
|
||||
class SCIMProviderUser(SerializerModel):
|
||||
"""Mapping of a user and provider to a SCIM user ID"""
|
||||
|
||||
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)
|
||||
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 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"""
|
||||
|
||||
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)
|
||||
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 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"""
|
||||
|
||||
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),
|
||||
]
|
||||
|
||||
@ -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(
|
||||
|
||||
531
schema.yml
531
schema.yml
@ -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
|
||||
@ -36335,6 +36732,8 @@ components:
|
||||
type: string
|
||||
format: uuid
|
||||
readOnly: true
|
||||
google_id:
|
||||
type: string
|
||||
group:
|
||||
type: string
|
||||
format: uuid
|
||||
@ -36348,6 +36747,7 @@ components:
|
||||
readOnly: true
|
||||
required:
|
||||
- attributes
|
||||
- google_id
|
||||
- group
|
||||
- group_obj
|
||||
- id
|
||||
@ -36356,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:
|
||||
@ -36484,6 +36888,8 @@ components:
|
||||
type: string
|
||||
format: uuid
|
||||
readOnly: true
|
||||
google_id:
|
||||
type: string
|
||||
user:
|
||||
type: integer
|
||||
user_obj:
|
||||
@ -36496,6 +36902,7 @@ components:
|
||||
readOnly: true
|
||||
required:
|
||||
- attributes
|
||||
- google_id
|
||||
- id
|
||||
- provider
|
||||
- user
|
||||
@ -36504,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:
|
||||
@ -38003,6 +38414,8 @@ components:
|
||||
type: string
|
||||
format: uuid
|
||||
readOnly: true
|
||||
microsoft_id:
|
||||
type: string
|
||||
group:
|
||||
type: string
|
||||
format: uuid
|
||||
@ -38019,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
|
||||
@ -38031,6 +38448,7 @@ components:
|
||||
type: integer
|
||||
required:
|
||||
- group
|
||||
- microsoft_id
|
||||
- provider
|
||||
MicrosoftEntraProviderMapping:
|
||||
type: object
|
||||
@ -38149,6 +38567,8 @@ components:
|
||||
type: string
|
||||
format: uuid
|
||||
readOnly: true
|
||||
microsoft_id:
|
||||
type: string
|
||||
user:
|
||||
type: integer
|
||||
user_obj:
|
||||
@ -38162,6 +38582,7 @@ components:
|
||||
required:
|
||||
- attributes
|
||||
- id
|
||||
- microsoft_id
|
||||
- provider
|
||||
- user
|
||||
- user_obj
|
||||
@ -38169,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:
|
||||
@ -40166,6 +40591,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:
|
||||
@ -40178,6 +40615,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:
|
||||
@ -44147,12 +44596,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
|
||||
@ -45906,6 +46357,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
|
||||
@ -45942,6 +46434,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
|
||||
|
||||
@ -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-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/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")}"
|
||||
|
||||
Reference in New Issue
Block a user