authentik.providers.scim.models

SCIM Provider models

  1"""SCIM Provider models"""
  2
  3from typing import Any, Self
  4from uuid import uuid4
  5
  6from django.db import models
  7from django.db.models import QuerySet
  8from django.templatetags.static import static
  9from django.utils.translation import gettext_lazy as _
 10from dramatiq.actor import Actor
 11from requests.auth import AuthBase
 12from rest_framework.serializers import Serializer
 13from structlog.stdlib import get_logger
 14
 15from authentik.core.apps import AppAccessWithoutBindings
 16from authentik.core.models import BackchannelProvider, Group, PropertyMapping, User, UserTypes
 17from authentik.lib.models import InternallyManagedMixin, SerializerModel
 18from authentik.lib.sync.outgoing.base import BaseOutgoingSyncClient
 19from authentik.lib.sync.outgoing.models import OutgoingSyncProvider
 20from authentik.lib.utils.time import timedelta_from_string, timedelta_string_validator
 21from authentik.policies.engine import PolicyEngine
 22from authentik.providers.scim.clients.auth import SCIMTokenAuth
 23
 24LOGGER = get_logger()
 25
 26
 27class SCIMProviderUser(InternallyManagedMixin, SerializerModel):
 28    """Mapping of a user and provider to a SCIM user ID"""
 29
 30    id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
 31    scim_id = models.TextField()
 32    user = models.ForeignKey(User, on_delete=models.CASCADE)
 33    provider = models.ForeignKey("SCIMProvider", on_delete=models.CASCADE)
 34    attributes = models.JSONField(default=dict)
 35
 36    @property
 37    def serializer(self) -> type[Serializer]:
 38        from authentik.providers.scim.api.users import SCIMProviderUserSerializer
 39
 40        return SCIMProviderUserSerializer
 41
 42    class Meta:
 43        unique_together = (("scim_id", "user", "provider"),)
 44
 45    def __str__(self) -> str:
 46        return f"SCIM Provider User {self.user_id} to {self.provider_id}"
 47
 48
 49class SCIMProviderGroup(InternallyManagedMixin, SerializerModel):
 50    """Mapping of a group and provider to a SCIM user ID"""
 51
 52    id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
 53    scim_id = models.TextField()
 54    group = models.ForeignKey(Group, on_delete=models.CASCADE)
 55    provider = models.ForeignKey("SCIMProvider", on_delete=models.CASCADE)
 56    attributes = models.JSONField(default=dict)
 57
 58    @property
 59    def serializer(self) -> type[Serializer]:
 60        from authentik.providers.scim.api.groups import SCIMProviderGroupSerializer
 61
 62        return SCIMProviderGroupSerializer
 63
 64    class Meta:
 65        unique_together = (("scim_id", "group", "provider"),)
 66
 67    def __str__(self) -> str:
 68        return f"SCIM Provider Group {self.group_id} to {self.provider_id}"
 69
 70
 71class SCIMAuthenticationMode(models.TextChoices):
 72    """SCIM authentication modes"""
 73
 74    TOKEN = "token", _("Token")
 75    OAUTH_SILENT = "oauth", _("OAuth (Silent)")
 76    OAUTH_INTERACTIVE = "oauth_interactive", _("OAuth (interactive)")
 77
 78
 79class SCIMCompatibilityMode(models.TextChoices):
 80    """SCIM compatibility mode"""
 81
 82    DEFAULT = "default", _("Default")
 83    AWS = "aws", _("AWS")
 84    SLACK = "slack", _("Slack")
 85    SALESFORCE = "sfdc", _("Salesforce")
 86    WEBEX = "webex", _("Webex")
 87    VCENTER = "vcenter", _("vCenter")
 88
 89
 90class SCIMProvider(OutgoingSyncProvider, BackchannelProvider):
 91    """SCIM 2.0 provider to create users and groups in external applications"""
 92
 93    exclude_users_service_account = models.BooleanField(default=False)
 94
 95    group_filters = models.ManyToManyField(
 96        "authentik_core.group",
 97        default=None,
 98        blank=True,
 99        help_text=_("Group filters used to define sync-scope for groups."),
100    )
101
102    url = models.TextField(help_text=_("Base URL to SCIM requests, usually ends in /v2"))
103
104    auth_mode = models.TextField(
105        choices=SCIMAuthenticationMode.choices, default=SCIMAuthenticationMode.TOKEN
106    )
107
108    token = models.TextField(help_text=_("Authentication token"), blank=True)
109    auth_oauth = models.ForeignKey(
110        "authentik_sources_oauth.OAuthSource",
111        on_delete=models.SET_DEFAULT,
112        default=None,
113        null=True,
114        help_text=_("OAuth Source used for authentication"),
115    )
116    auth_oauth_params = models.JSONField(
117        blank=True, default=dict, help_text=_("Additional OAuth parameters, such as grant_type")
118    )
119    auth_oauth_user = models.ForeignKey(
120        "authentik_core.User", on_delete=models.SET_NULL, default=None, null=True
121    )
122
123    verify_certificates = models.BooleanField(default=True)
124
125    property_mappings_group = models.ManyToManyField(
126        PropertyMapping,
127        default=None,
128        blank=True,
129        help_text=_("Property mappings used for group creation/updating."),
130    )
131
132    compatibility_mode = models.CharField(
133        max_length=30,
134        choices=SCIMCompatibilityMode.choices,
135        default=SCIMCompatibilityMode.DEFAULT,
136        verbose_name=_("SCIM Compatibility Mode"),
137        help_text=_("Alter authentik behavior for vendor-specific SCIM implementations."),
138    )
139    service_provider_config_cache_timeout = models.TextField(
140        default="hours=1",
141        validators=[timedelta_string_validator],
142        help_text=_(
143            "Cache duration for ServiceProviderConfig responses. Set minutes=0 to disable."
144        ),
145    )
146
147    def scim_auth(self) -> AuthBase:
148        if self.auth_mode in [
149            SCIMAuthenticationMode.OAUTH_SILENT,
150            SCIMAuthenticationMode.OAUTH_INTERACTIVE,
151        ]:
152            try:
153                from authentik.enterprise.providers.scim.auth_oauth2 import SCIMOAuthAuth
154
155                return SCIMOAuthAuth(self)
156            except ImportError:
157                LOGGER.warning("Failed to import SCIM OAuth Client")
158        return SCIMTokenAuth(self)
159
160    @property
161    def icon_url(self) -> str | None:
162        return static("authentik/sources/scim.png")
163
164    @property
165    def sync_actor(self) -> Actor:
166        from authentik.providers.scim.tasks import scim_sync
167
168        return scim_sync
169
170    def client_for_model(
171        self, model: type[User | Group | SCIMProviderUser | SCIMProviderGroup]
172    ) -> BaseOutgoingSyncClient[User | Group, Any, Any, Self]:
173        if issubclass(model, User | SCIMProviderUser):
174            from authentik.providers.scim.clients.users import SCIMUserClient
175
176            return SCIMUserClient(self)
177        if issubclass(model, Group | SCIMProviderGroup):
178            from authentik.providers.scim.clients.groups import SCIMGroupClient
179
180            return SCIMGroupClient(self)
181        raise ValueError(f"Invalid model {model}")
182
183    def save(self, *args, **kwargs):
184        from django.core.cache import cache
185
186        cache_key = f"goauthentik.io/providers/scim/{self.pk}/service_provider_config"
187        cache.delete(cache_key)
188        super().save(*args, **kwargs)
189
190    def get_object_qs(self, type: type[User | Group], **kwargs) -> QuerySet[User | Group]:
191        if type == User:
192            # Get queryset of all users with consistent ordering
193            # according to the provider's settings
194            base = User.objects.all().exclude_anonymous().filter(**kwargs)
195            if self.exclude_users_service_account:
196                base = base.exclude(type=UserTypes.SERVICE_ACCOUNT).exclude(
197                    type=UserTypes.INTERNAL_SERVICE_ACCOUNT
198                )
199
200            # Filter users by their access to the backchannel application if an application is set
201            # This handles both policy bindings and group_filters
202            if self.backchannel_application:
203                pks = []
204                for user in base:
205                    engine = PolicyEngine(self.backchannel_application, user, None)
206                    engine.empty_result = AppAccessWithoutBindings.get()
207                    engine.build()
208                    if engine.passing:
209                        pks.append(user.pk)
210                base = base.filter(pk__in=pks)
211            return base.order_by("pk")
212
213        if type == Group:
214            # Get queryset of all groups with consistent ordering
215            # according to the provider's settings
216            base = Group.objects.prefetch_related("scimprovidergroup_set").all().filter(**kwargs)
217
218            # Filter groups by group_filters if set
219            if self.group_filters.exists():
220                base = base.filter(pk__in=self.group_filters.values_list("pk", flat=True))
221
222            return base.order_by("pk")
223        raise ValueError(f"Invalid type {type}")
224
225    @classmethod
226    def get_object_mappings(cls, obj: User | Group) -> list[tuple[str, str]]:
227        if isinstance(obj, User):
228            return list(obj.scimprovideruser_set.values_list("provider__pk", "scim_id"))
229        if isinstance(obj, Group):
230            return list(obj.scimprovidergroup_set.values_list("provider__pk", "scim_id"))
231        raise ValueError(f"Invalid type {type(obj)}")
232
233    @property
234    def component(self) -> str:
235        return "ak-provider-scim-form"
236
237    @property
238    def service_provider_config_cache_timeout_seconds(self) -> int:
239        return max(
240            0,
241            int(timedelta_from_string(self.service_provider_config_cache_timeout).total_seconds()),
242        )
243
244    @property
245    def serializer(self) -> type[Serializer]:
246        from authentik.providers.scim.api.providers import SCIMProviderSerializer
247
248        return SCIMProviderSerializer
249
250    def __str__(self):
251        return f"SCIM Provider {self.name}"
252
253    class Meta:
254        verbose_name = _("SCIM Provider")
255        verbose_name_plural = _("SCIM Providers")
256
257
258class SCIMMapping(PropertyMapping):
259    """Map authentik data to outgoing SCIM requests"""
260
261    @property
262    def component(self) -> str:
263        return "ak-property-mapping-provider-scim-form"
264
265    @property
266    def serializer(self) -> type[Serializer]:
267        from authentik.providers.scim.api.property_mappings import SCIMMappingSerializer
268
269        return SCIMMappingSerializer
270
271    def __str__(self):
272        return f"SCIM Provider Mapping {self.name}"
273
274    class Meta:
275        verbose_name = _("SCIM Provider Mapping")
276        verbose_name_plural = _("SCIM Provider Mappings")
LOGGER = <BoundLoggerLazyProxy(logger=None, wrapper_class=None, processors=None, context_class=None, initial_values={}, logger_factory_args=())>
28class SCIMProviderUser(InternallyManagedMixin, SerializerModel):
29    """Mapping of a user and provider to a SCIM user ID"""
30
31    id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
32    scim_id = models.TextField()
33    user = models.ForeignKey(User, on_delete=models.CASCADE)
34    provider = models.ForeignKey("SCIMProvider", on_delete=models.CASCADE)
35    attributes = models.JSONField(default=dict)
36
37    @property
38    def serializer(self) -> type[Serializer]:
39        from authentik.providers.scim.api.users import SCIMProviderUserSerializer
40
41        return SCIMProviderUserSerializer
42
43    class Meta:
44        unique_together = (("scim_id", "user", "provider"),)
45
46    def __str__(self) -> str:
47        return f"SCIM Provider User {self.user_id} to {self.provider_id}"

Mapping of a user and provider to a SCIM user ID

def id(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

def scim_id(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

user

Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.

In the example::

class Child(Model):
    parent = ForeignKey(Parent, related_name='children')

Child.parent is a ForwardManyToOneDescriptor instance.

provider

Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.

In the example::

class Child(Model):
    parent = ForeignKey(Parent, related_name='children')

Child.parent is a ForwardManyToOneDescriptor instance.

def attributes(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

serializer: type[rest_framework.serializers.Serializer]
37    @property
38    def serializer(self) -> type[Serializer]:
39        from authentik.providers.scim.api.users import SCIMProviderUserSerializer
40
41        return SCIMProviderUserSerializer

Get serializer for this model

user_id
provider_id
def objects(unknown):

The type of the None singleton.

class SCIMProviderUser.DoesNotExist(django.core.exceptions.ObjectDoesNotExist):

The requested object does not exist

class SCIMProviderUser.MultipleObjectsReturned(django.core.exceptions.MultipleObjectsReturned):

The query returned multiple objects when only one was expected.

50class SCIMProviderGroup(InternallyManagedMixin, SerializerModel):
51    """Mapping of a group and provider to a SCIM user ID"""
52
53    id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
54    scim_id = models.TextField()
55    group = models.ForeignKey(Group, on_delete=models.CASCADE)
56    provider = models.ForeignKey("SCIMProvider", on_delete=models.CASCADE)
57    attributes = models.JSONField(default=dict)
58
59    @property
60    def serializer(self) -> type[Serializer]:
61        from authentik.providers.scim.api.groups import SCIMProviderGroupSerializer
62
63        return SCIMProviderGroupSerializer
64
65    class Meta:
66        unique_together = (("scim_id", "group", "provider"),)
67
68    def __str__(self) -> str:
69        return f"SCIM Provider Group {self.group_id} to {self.provider_id}"

Mapping of a group and provider to a SCIM user ID

def id(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

def scim_id(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

group

Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.

In the example::

class Child(Model):
    parent = ForeignKey(Parent, related_name='children')

Child.parent is a ForwardManyToOneDescriptor instance.

provider

Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.

In the example::

class Child(Model):
    parent = ForeignKey(Parent, related_name='children')

Child.parent is a ForwardManyToOneDescriptor instance.

def attributes(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

serializer: type[rest_framework.serializers.Serializer]
59    @property
60    def serializer(self) -> type[Serializer]:
61        from authentik.providers.scim.api.groups import SCIMProviderGroupSerializer
62
63        return SCIMProviderGroupSerializer

Get serializer for this model

group_id
provider_id
def objects(unknown):

The type of the None singleton.

class SCIMProviderGroup.DoesNotExist(django.core.exceptions.ObjectDoesNotExist):

The requested object does not exist

class SCIMProviderGroup.MultipleObjectsReturned(django.core.exceptions.MultipleObjectsReturned):

The query returned multiple objects when only one was expected.

class SCIMAuthenticationMode(django.db.models.enums.TextChoices):
72class SCIMAuthenticationMode(models.TextChoices):
73    """SCIM authentication modes"""
74
75    TOKEN = "token", _("Token")
76    OAUTH_SILENT = "oauth", _("OAuth (Silent)")
77    OAUTH_INTERACTIVE = "oauth_interactive", _("OAuth (interactive)")

SCIM authentication modes

class SCIMCompatibilityMode(django.db.models.enums.TextChoices):
80class SCIMCompatibilityMode(models.TextChoices):
81    """SCIM compatibility mode"""
82
83    DEFAULT = "default", _("Default")
84    AWS = "aws", _("AWS")
85    SLACK = "slack", _("Slack")
86    SALESFORCE = "sfdc", _("Salesforce")
87    WEBEX = "webex", _("Webex")
88    VCENTER = "vcenter", _("vCenter")

SCIM compatibility mode

 91class SCIMProvider(OutgoingSyncProvider, BackchannelProvider):
 92    """SCIM 2.0 provider to create users and groups in external applications"""
 93
 94    exclude_users_service_account = models.BooleanField(default=False)
 95
 96    group_filters = models.ManyToManyField(
 97        "authentik_core.group",
 98        default=None,
 99        blank=True,
100        help_text=_("Group filters used to define sync-scope for groups."),
101    )
102
103    url = models.TextField(help_text=_("Base URL to SCIM requests, usually ends in /v2"))
104
105    auth_mode = models.TextField(
106        choices=SCIMAuthenticationMode.choices, default=SCIMAuthenticationMode.TOKEN
107    )
108
109    token = models.TextField(help_text=_("Authentication token"), blank=True)
110    auth_oauth = models.ForeignKey(
111        "authentik_sources_oauth.OAuthSource",
112        on_delete=models.SET_DEFAULT,
113        default=None,
114        null=True,
115        help_text=_("OAuth Source used for authentication"),
116    )
117    auth_oauth_params = models.JSONField(
118        blank=True, default=dict, help_text=_("Additional OAuth parameters, such as grant_type")
119    )
120    auth_oauth_user = models.ForeignKey(
121        "authentik_core.User", on_delete=models.SET_NULL, default=None, null=True
122    )
123
124    verify_certificates = models.BooleanField(default=True)
125
126    property_mappings_group = models.ManyToManyField(
127        PropertyMapping,
128        default=None,
129        blank=True,
130        help_text=_("Property mappings used for group creation/updating."),
131    )
132
133    compatibility_mode = models.CharField(
134        max_length=30,
135        choices=SCIMCompatibilityMode.choices,
136        default=SCIMCompatibilityMode.DEFAULT,
137        verbose_name=_("SCIM Compatibility Mode"),
138        help_text=_("Alter authentik behavior for vendor-specific SCIM implementations."),
139    )
140    service_provider_config_cache_timeout = models.TextField(
141        default="hours=1",
142        validators=[timedelta_string_validator],
143        help_text=_(
144            "Cache duration for ServiceProviderConfig responses. Set minutes=0 to disable."
145        ),
146    )
147
148    def scim_auth(self) -> AuthBase:
149        if self.auth_mode in [
150            SCIMAuthenticationMode.OAUTH_SILENT,
151            SCIMAuthenticationMode.OAUTH_INTERACTIVE,
152        ]:
153            try:
154                from authentik.enterprise.providers.scim.auth_oauth2 import SCIMOAuthAuth
155
156                return SCIMOAuthAuth(self)
157            except ImportError:
158                LOGGER.warning("Failed to import SCIM OAuth Client")
159        return SCIMTokenAuth(self)
160
161    @property
162    def icon_url(self) -> str | None:
163        return static("authentik/sources/scim.png")
164
165    @property
166    def sync_actor(self) -> Actor:
167        from authentik.providers.scim.tasks import scim_sync
168
169        return scim_sync
170
171    def client_for_model(
172        self, model: type[User | Group | SCIMProviderUser | SCIMProviderGroup]
173    ) -> BaseOutgoingSyncClient[User | Group, Any, Any, Self]:
174        if issubclass(model, User | SCIMProviderUser):
175            from authentik.providers.scim.clients.users import SCIMUserClient
176
177            return SCIMUserClient(self)
178        if issubclass(model, Group | SCIMProviderGroup):
179            from authentik.providers.scim.clients.groups import SCIMGroupClient
180
181            return SCIMGroupClient(self)
182        raise ValueError(f"Invalid model {model}")
183
184    def save(self, *args, **kwargs):
185        from django.core.cache import cache
186
187        cache_key = f"goauthentik.io/providers/scim/{self.pk}/service_provider_config"
188        cache.delete(cache_key)
189        super().save(*args, **kwargs)
190
191    def get_object_qs(self, type: type[User | Group], **kwargs) -> QuerySet[User | Group]:
192        if type == User:
193            # Get queryset of all users with consistent ordering
194            # according to the provider's settings
195            base = User.objects.all().exclude_anonymous().filter(**kwargs)
196            if self.exclude_users_service_account:
197                base = base.exclude(type=UserTypes.SERVICE_ACCOUNT).exclude(
198                    type=UserTypes.INTERNAL_SERVICE_ACCOUNT
199                )
200
201            # Filter users by their access to the backchannel application if an application is set
202            # This handles both policy bindings and group_filters
203            if self.backchannel_application:
204                pks = []
205                for user in base:
206                    engine = PolicyEngine(self.backchannel_application, user, None)
207                    engine.empty_result = AppAccessWithoutBindings.get()
208                    engine.build()
209                    if engine.passing:
210                        pks.append(user.pk)
211                base = base.filter(pk__in=pks)
212            return base.order_by("pk")
213
214        if type == Group:
215            # Get queryset of all groups with consistent ordering
216            # according to the provider's settings
217            base = Group.objects.prefetch_related("scimprovidergroup_set").all().filter(**kwargs)
218
219            # Filter groups by group_filters if set
220            if self.group_filters.exists():
221                base = base.filter(pk__in=self.group_filters.values_list("pk", flat=True))
222
223            return base.order_by("pk")
224        raise ValueError(f"Invalid type {type}")
225
226    @classmethod
227    def get_object_mappings(cls, obj: User | Group) -> list[tuple[str, str]]:
228        if isinstance(obj, User):
229            return list(obj.scimprovideruser_set.values_list("provider__pk", "scim_id"))
230        if isinstance(obj, Group):
231            return list(obj.scimprovidergroup_set.values_list("provider__pk", "scim_id"))
232        raise ValueError(f"Invalid type {type(obj)}")
233
234    @property
235    def component(self) -> str:
236        return "ak-provider-scim-form"
237
238    @property
239    def service_provider_config_cache_timeout_seconds(self) -> int:
240        return max(
241            0,
242            int(timedelta_from_string(self.service_provider_config_cache_timeout).total_seconds()),
243        )
244
245    @property
246    def serializer(self) -> type[Serializer]:
247        from authentik.providers.scim.api.providers import SCIMProviderSerializer
248
249        return SCIMProviderSerializer
250
251    def __str__(self):
252        return f"SCIM Provider {self.name}"
253
254    class Meta:
255        verbose_name = _("SCIM Provider")
256        verbose_name_plural = _("SCIM Providers")

SCIM 2.0 provider to create users and groups in external applications

def exclude_users_service_account(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

group_filters

Accessor to the related objects manager on the forward and reverse sides of a many-to-many relation.

In the example::

class Pizza(Model):
    toppings = ManyToManyField(Topping, related_name='pizzas')

Pizza.toppings and Topping.pizzas are ManyToManyDescriptor instances.

Most of the implementation is delegated to a dynamically defined manager class built by create_forward_many_to_many_manager() defined below.

def url(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

def auth_mode(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

def token(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

auth_oauth

Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.

In the example::

class Child(Model):
    parent = ForeignKey(Parent, related_name='children')

Child.parent is a ForwardManyToOneDescriptor instance.

def auth_oauth_params(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

auth_oauth_user

Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.

In the example::

class Child(Model):
    parent = ForeignKey(Parent, related_name='children')

Child.parent is a ForwardManyToOneDescriptor instance.

def verify_certificates(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

property_mappings_group

Accessor to the related objects manager on the forward and reverse sides of a many-to-many relation.

In the example::

class Pizza(Model):
    toppings = ManyToManyField(Topping, related_name='pizzas')

Pizza.toppings and Topping.pizzas are ManyToManyDescriptor instances.

Most of the implementation is delegated to a dynamically defined manager class built by create_forward_many_to_many_manager() defined below.

def compatibility_mode(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

def service_provider_config_cache_timeout(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

def scim_auth(self) -> requests.auth.AuthBase:
148    def scim_auth(self) -> AuthBase:
149        if self.auth_mode in [
150            SCIMAuthenticationMode.OAUTH_SILENT,
151            SCIMAuthenticationMode.OAUTH_INTERACTIVE,
152        ]:
153            try:
154                from authentik.enterprise.providers.scim.auth_oauth2 import SCIMOAuthAuth
155
156                return SCIMOAuthAuth(self)
157            except ImportError:
158                LOGGER.warning("Failed to import SCIM OAuth Client")
159        return SCIMTokenAuth(self)
icon_url: str | None
161    @property
162    def icon_url(self) -> str | None:
163        return static("authentik/sources/scim.png")
sync_actor: dramatiq.actor.Actor
165    @property
166    def sync_actor(self) -> Actor:
167        from authentik.providers.scim.tasks import scim_sync
168
169        return scim_sync
171    def client_for_model(
172        self, model: type[User | Group | SCIMProviderUser | SCIMProviderGroup]
173    ) -> BaseOutgoingSyncClient[User | Group, Any, Any, Self]:
174        if issubclass(model, User | SCIMProviderUser):
175            from authentik.providers.scim.clients.users import SCIMUserClient
176
177            return SCIMUserClient(self)
178        if issubclass(model, Group | SCIMProviderGroup):
179            from authentik.providers.scim.clients.groups import SCIMGroupClient
180
181            return SCIMGroupClient(self)
182        raise ValueError(f"Invalid model {model}")
def save(self, *args, **kwargs):
184    def save(self, *args, **kwargs):
185        from django.core.cache import cache
186
187        cache_key = f"goauthentik.io/providers/scim/{self.pk}/service_provider_config"
188        cache.delete(cache_key)
189        super().save(*args, **kwargs)

Save the current instance. Override this in a subclass if you want to control the saving process.

The 'force_insert' and 'force_update' parameters can be used to insist that the "save" must be an SQL insert or update (or equivalent for non-SQL backends), respectively. Normally, they should not be set.

def get_object_qs( self, type: type[authentik.core.models.User | authentik.core.models.Group], **kwargs) -> django.db.models.query.QuerySet:
191    def get_object_qs(self, type: type[User | Group], **kwargs) -> QuerySet[User | Group]:
192        if type == User:
193            # Get queryset of all users with consistent ordering
194            # according to the provider's settings
195            base = User.objects.all().exclude_anonymous().filter(**kwargs)
196            if self.exclude_users_service_account:
197                base = base.exclude(type=UserTypes.SERVICE_ACCOUNT).exclude(
198                    type=UserTypes.INTERNAL_SERVICE_ACCOUNT
199                )
200
201            # Filter users by their access to the backchannel application if an application is set
202            # This handles both policy bindings and group_filters
203            if self.backchannel_application:
204                pks = []
205                for user in base:
206                    engine = PolicyEngine(self.backchannel_application, user, None)
207                    engine.empty_result = AppAccessWithoutBindings.get()
208                    engine.build()
209                    if engine.passing:
210                        pks.append(user.pk)
211                base = base.filter(pk__in=pks)
212            return base.order_by("pk")
213
214        if type == Group:
215            # Get queryset of all groups with consistent ordering
216            # according to the provider's settings
217            base = Group.objects.prefetch_related("scimprovidergroup_set").all().filter(**kwargs)
218
219            # Filter groups by group_filters if set
220            if self.group_filters.exists():
221                base = base.filter(pk__in=self.group_filters.values_list("pk", flat=True))
222
223            return base.order_by("pk")
224        raise ValueError(f"Invalid type {type}")
@classmethod
def get_object_mappings( cls, obj: authentik.core.models.User | authentik.core.models.Group) -> list[tuple[str, str]]:
226    @classmethod
227    def get_object_mappings(cls, obj: User | Group) -> list[tuple[str, str]]:
228        if isinstance(obj, User):
229            return list(obj.scimprovideruser_set.values_list("provider__pk", "scim_id"))
230        if isinstance(obj, Group):
231            return list(obj.scimprovidergroup_set.values_list("provider__pk", "scim_id"))
232        raise ValueError(f"Invalid type {type(obj)}")

Get a list of mapping between User/Group and ProviderUser/Group: [("provider_pk", "obj_pk")]

component: str
234    @property
235    def component(self) -> str:
236        return "ak-provider-scim-form"

Return component used to edit this object

service_provider_config_cache_timeout_seconds: int
238    @property
239    def service_provider_config_cache_timeout_seconds(self) -> int:
240        return max(
241            0,
242            int(timedelta_from_string(self.service_provider_config_cache_timeout).total_seconds()),
243        )
serializer: type[rest_framework.serializers.Serializer]
245    @property
246    def serializer(self) -> type[Serializer]:
247        from authentik.providers.scim.api.providers import SCIMProviderSerializer
248
249        return SCIMProviderSerializer

Get serializer for this model

def sync_page_size(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

def sync_page_timeout(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

def dry_run(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

schedules

Accessor to the related objects manager on the one-to-many relation created by GenericRelation.

In the example::

class Post(Model):
    comments = GenericRelation(Comment)

post.comments is a ReverseGenericManyToOneDescriptor instance.

tasks

Accessor to the related objects manager on the one-to-many relation created by GenericRelation.

In the example::

class Post(Model):
    comments = GenericRelation(Comment)

post.comments is a ReverseGenericManyToOneDescriptor instance.

def get_auth_mode_display(unknown):

Method descriptor with partial application of the given arguments and keywords.

Supports wrapping existing descriptors and handles non-descriptor callables as instance methods.

auth_oauth_id
auth_oauth_user_id
def get_compatibility_mode_display(unknown):

Method descriptor with partial application of the given arguments and keywords.

Supports wrapping existing descriptors and handles non-descriptor callables as instance methods.

provider_ptr_id
provider_ptr

Accessor to the related object on the forward side of a one-to-one relation.

In the example::

class Restaurant(Model):
    place = OneToOneField(Place, related_name='restaurant')

Restaurant.place is a ForwardOneToOneDescriptor instance.

scimprovideruser_set

Accessor to the related objects manager on the reverse side of a many-to-one relation.

In the example::

class Child(Model):
    parent = ForeignKey(Parent, related_name='children')

Parent.children is a ReverseManyToOneDescriptor instance.

Most of the implementation is delegated to a dynamically defined manager class built by create_forward_many_to_many_manager() defined below.

scimprovidergroup_set

Accessor to the related objects manager on the reverse side of a many-to-one relation.

In the example::

class Child(Model):
    parent = ForeignKey(Parent, related_name='children')

Parent.children is a ReverseManyToOneDescriptor instance.

Most of the implementation is delegated to a dynamically defined manager class built by create_forward_many_to_many_manager() defined below.

class SCIMProvider.DoesNotExist(django.core.exceptions.ObjectDoesNotExist):

The requested object does not exist

class SCIMProvider.MultipleObjectsReturned(django.core.exceptions.MultipleObjectsReturned):

The query returned multiple objects when only one was expected.

class SCIMMapping(authentik.core.models.PropertyMapping):
259class SCIMMapping(PropertyMapping):
260    """Map authentik data to outgoing SCIM requests"""
261
262    @property
263    def component(self) -> str:
264        return "ak-property-mapping-provider-scim-form"
265
266    @property
267    def serializer(self) -> type[Serializer]:
268        from authentik.providers.scim.api.property_mappings import SCIMMappingSerializer
269
270        return SCIMMappingSerializer
271
272    def __str__(self):
273        return f"SCIM Provider Mapping {self.name}"
274
275    class Meta:
276        verbose_name = _("SCIM Provider Mapping")
277        verbose_name_plural = _("SCIM Provider Mappings")

Map authentik data to outgoing SCIM requests

component: str
262    @property
263    def component(self) -> str:
264        return "ak-property-mapping-provider-scim-form"

Return component used to edit this object

serializer: type[rest_framework.serializers.Serializer]
266    @property
267    def serializer(self) -> type[Serializer]:
268        from authentik.providers.scim.api.property_mappings import SCIMMappingSerializer
269
270        return SCIMMappingSerializer

Get serializer for this model

propertymapping_ptr_id
propertymapping_ptr

Accessor to the related object on the forward side of a one-to-one relation.

In the example::

class Restaurant(Model):
    place = OneToOneField(Place, related_name='restaurant')

Restaurant.place is a ForwardOneToOneDescriptor instance.

class SCIMMapping.DoesNotExist(authentik.core.models.PropertyMapping.DoesNotExist):

The requested object does not exist

class SCIMMapping.MultipleObjectsReturned(authentik.core.models.PropertyMapping.MultipleObjectsReturned):

The query returned multiple objects when only one was expected.