authentik.sources.scim.models

SCIM Source

  1"""SCIM Source"""
  2
  3from typing import Any
  4from uuid import uuid4
  5
  6from django.db import models
  7from django.templatetags.static import static
  8from django.utils.translation import gettext_lazy as _
  9from rest_framework.serializers import BaseSerializer, Serializer
 10
 11from authentik.core.models import Group, PropertyMapping, Source, Token, User
 12from authentik.lib.models import InternallyManagedMixin, SerializerModel
 13
 14
 15class SCIMSource(Source):
 16    """System for Cross-domain Identity Management Source, allows for
 17    cross-system user provisioning"""
 18
 19    token = models.ForeignKey(Token, on_delete=models.CASCADE, null=True, default=None)
 20
 21    @property
 22    def service_account_identifier(self) -> str:
 23        return f"ak-source-scim-{self.pk}"
 24
 25    @property
 26    def component(self) -> str:
 27        """Return component used to edit this object"""
 28        return "ak-source-scim-form"
 29
 30    @property
 31    def icon_url(self) -> str:
 32        return static("authentik/sources/scim.png")
 33
 34    @property
 35    def serializer(self) -> BaseSerializer:
 36        from authentik.sources.scim.api.sources import SCIMSourceSerializer
 37
 38        return SCIMSourceSerializer
 39
 40    @property
 41    def property_mapping_type(self) -> type[PropertyMapping]:
 42        return SCIMSourcePropertyMapping
 43
 44    def get_base_user_properties(self, data: dict[str, Any]) -> dict[str, Any | dict[str, Any]]:
 45        properties = {}
 46
 47        def get_email(data: list[dict]) -> str:
 48            """Wrapper to get primary email or first email"""
 49            for email in data:
 50                if email.get("primary", False):
 51                    return email.get("value")
 52            if len(data) < 1:
 53                return ""
 54            return data[0].get("value")
 55
 56        if "userName" in data:
 57            properties["username"] = data.get("userName")
 58        if "name" in data:
 59            properties["name"] = data.get("name", {}).get("formatted", data.get("displayName"))
 60        if "emails" in data:
 61            properties["email"] = get_email(data.get("emails"))
 62        if "active" in data:
 63            properties["is_active"] = data.get("active")
 64
 65        return properties
 66
 67    def get_base_group_properties(self, data: dict[str, Any]) -> dict[str, Any | dict[str, Any]]:
 68        properties = {}
 69
 70        if "displayName" in data:
 71            properties["name"] = data.get("displayName")
 72
 73        return properties
 74
 75    def __str__(self) -> str:
 76        return f"SCIM Source {self.name}"
 77
 78    class Meta:
 79
 80        verbose_name = _("SCIM Source")
 81        verbose_name_plural = _("SCIM Sources")
 82
 83
 84class SCIMSourcePropertyMapping(PropertyMapping):
 85    """Map SCIM properties to User or Group object attributes"""
 86
 87    @property
 88    def component(self) -> str:
 89        return "ak-property-mapping-source-scim-form"
 90
 91    @property
 92    def serializer(self) -> type[Serializer]:
 93        from authentik.sources.scim.api.property_mappings import (
 94            SCIMSourcePropertyMappingSerializer,
 95        )
 96
 97        return SCIMSourcePropertyMappingSerializer
 98
 99    class Meta:
100        verbose_name = _("SCIM Source Property Mapping")
101        verbose_name_plural = _("SCIM Source Property Mappings")
102
103
104class SCIMSourceUser(InternallyManagedMixin, SerializerModel):
105    """Mapping of a user and source to a SCIM user ID"""
106
107    id = models.TextField(primary_key=True, default=uuid4)
108    external_id = models.TextField()
109    user = models.ForeignKey(User, on_delete=models.CASCADE)
110    source = models.ForeignKey(SCIMSource, on_delete=models.CASCADE)
111    attributes = models.JSONField(default=dict)
112    last_update = models.DateTimeField(auto_now=True)
113
114    @property
115    def serializer(self) -> BaseSerializer:
116        from authentik.sources.scim.api.users import SCIMSourceUserSerializer
117
118        return SCIMSourceUserSerializer
119
120    class Meta:
121        unique_together = (("external_id", "source"),)
122        indexes = [
123            models.Index(fields=["external_id"]),
124        ]
125
126    def __str__(self) -> str:
127        return f"SCIM User {self.user_id} to {self.source_id}"
128
129
130class SCIMSourceGroup(InternallyManagedMixin, SerializerModel):
131    """Mapping of a group and source to a SCIM user ID"""
132
133    id = models.TextField(primary_key=True, default=uuid4)
134    external_id = models.TextField()
135    group = models.ForeignKey(Group, on_delete=models.CASCADE)
136    source = models.ForeignKey(SCIMSource, on_delete=models.CASCADE)
137    attributes = models.JSONField(default=dict)
138    last_update = models.DateTimeField(auto_now=True)
139
140    @property
141    def serializer(self) -> BaseSerializer:
142        from authentik.sources.scim.api.groups import SCIMSourceGroupSerializer
143
144        return SCIMSourceGroupSerializer
145
146    class Meta:
147        unique_together = (("external_id", "source"),)
148        indexes = [
149            models.Index(fields=["external_id"]),
150        ]
151
152    def __str__(self) -> str:
153        return f"SCIM Group {self.group_id} to {self.source_id}"
class SCIMSource(authentik.core.models.Source):
16class SCIMSource(Source):
17    """System for Cross-domain Identity Management Source, allows for
18    cross-system user provisioning"""
19
20    token = models.ForeignKey(Token, on_delete=models.CASCADE, null=True, default=None)
21
22    @property
23    def service_account_identifier(self) -> str:
24        return f"ak-source-scim-{self.pk}"
25
26    @property
27    def component(self) -> str:
28        """Return component used to edit this object"""
29        return "ak-source-scim-form"
30
31    @property
32    def icon_url(self) -> str:
33        return static("authentik/sources/scim.png")
34
35    @property
36    def serializer(self) -> BaseSerializer:
37        from authentik.sources.scim.api.sources import SCIMSourceSerializer
38
39        return SCIMSourceSerializer
40
41    @property
42    def property_mapping_type(self) -> type[PropertyMapping]:
43        return SCIMSourcePropertyMapping
44
45    def get_base_user_properties(self, data: dict[str, Any]) -> dict[str, Any | dict[str, Any]]:
46        properties = {}
47
48        def get_email(data: list[dict]) -> str:
49            """Wrapper to get primary email or first email"""
50            for email in data:
51                if email.get("primary", False):
52                    return email.get("value")
53            if len(data) < 1:
54                return ""
55            return data[0].get("value")
56
57        if "userName" in data:
58            properties["username"] = data.get("userName")
59        if "name" in data:
60            properties["name"] = data.get("name", {}).get("formatted", data.get("displayName"))
61        if "emails" in data:
62            properties["email"] = get_email(data.get("emails"))
63        if "active" in data:
64            properties["is_active"] = data.get("active")
65
66        return properties
67
68    def get_base_group_properties(self, data: dict[str, Any]) -> dict[str, Any | dict[str, Any]]:
69        properties = {}
70
71        if "displayName" in data:
72            properties["name"] = data.get("displayName")
73
74        return properties
75
76    def __str__(self) -> str:
77        return f"SCIM Source {self.name}"
78
79    class Meta:
80
81        verbose_name = _("SCIM Source")
82        verbose_name_plural = _("SCIM Sources")

System for Cross-domain Identity Management Source, allows for cross-system user provisioning

token

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.

service_account_identifier: str
22    @property
23    def service_account_identifier(self) -> str:
24        return f"ak-source-scim-{self.pk}"
component: str
26    @property
27    def component(self) -> str:
28        """Return component used to edit this object"""
29        return "ak-source-scim-form"

Return component used to edit this object

icon_url: str
31    @property
32    def icon_url(self) -> str:
33        return static("authentik/sources/scim.png")

Get the URL to the source icon

serializer: rest_framework.serializers.BaseSerializer
35    @property
36    def serializer(self) -> BaseSerializer:
37        from authentik.sources.scim.api.sources import SCIMSourceSerializer
38
39        return SCIMSourceSerializer

Get serializer for this model

property_mapping_type: type[authentik.core.models.PropertyMapping]
41    @property
42    def property_mapping_type(self) -> type[PropertyMapping]:
43        return SCIMSourcePropertyMapping

Return property mapping type used by this object

def get_base_user_properties( self, data: dict[str, typing.Any]) -> dict[str, typing.Any | dict[str, typing.Any]]:
45    def get_base_user_properties(self, data: dict[str, Any]) -> dict[str, Any | dict[str, Any]]:
46        properties = {}
47
48        def get_email(data: list[dict]) -> str:
49            """Wrapper to get primary email or first email"""
50            for email in data:
51                if email.get("primary", False):
52                    return email.get("value")
53            if len(data) < 1:
54                return ""
55            return data[0].get("value")
56
57        if "userName" in data:
58            properties["username"] = data.get("userName")
59        if "name" in data:
60            properties["name"] = data.get("name", {}).get("formatted", data.get("displayName"))
61        if "emails" in data:
62            properties["email"] = get_email(data.get("emails"))
63        if "active" in data:
64            properties["is_active"] = data.get("active")
65
66        return properties

Get base properties for a user to build final properties upon.

def get_base_group_properties( self, data: dict[str, typing.Any]) -> dict[str, typing.Any | dict[str, typing.Any]]:
68    def get_base_group_properties(self, data: dict[str, Any]) -> dict[str, Any | dict[str, Any]]:
69        properties = {}
70
71        if "displayName" in data:
72            properties["name"] = data.get("displayName")
73
74        return properties

Get base properties for a group to build final properties upon.

token_id
source_ptr_id
source_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.

scimsourceuser_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.

scimsourcegroup_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 SCIMSource.DoesNotExist(authentik.core.models.Source.DoesNotExist):

The requested object does not exist

class SCIMSource.MultipleObjectsReturned(authentik.core.models.Source.MultipleObjectsReturned):

The query returned multiple objects when only one was expected.

class SCIMSourcePropertyMapping(authentik.core.models.PropertyMapping):
 85class SCIMSourcePropertyMapping(PropertyMapping):
 86    """Map SCIM properties to User or Group object attributes"""
 87
 88    @property
 89    def component(self) -> str:
 90        return "ak-property-mapping-source-scim-form"
 91
 92    @property
 93    def serializer(self) -> type[Serializer]:
 94        from authentik.sources.scim.api.property_mappings import (
 95            SCIMSourcePropertyMappingSerializer,
 96        )
 97
 98        return SCIMSourcePropertyMappingSerializer
 99
100    class Meta:
101        verbose_name = _("SCIM Source Property Mapping")
102        verbose_name_plural = _("SCIM Source Property Mappings")

Map SCIM properties to User or Group object attributes

component: str
88    @property
89    def component(self) -> str:
90        return "ak-property-mapping-source-scim-form"

Return component used to edit this object

serializer: type[rest_framework.serializers.Serializer]
92    @property
93    def serializer(self) -> type[Serializer]:
94        from authentik.sources.scim.api.property_mappings import (
95            SCIMSourcePropertyMappingSerializer,
96        )
97
98        return SCIMSourcePropertyMappingSerializer

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 SCIMSourcePropertyMapping.DoesNotExist(authentik.core.models.PropertyMapping.DoesNotExist):

The requested object does not exist

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

The query returned multiple objects when only one was expected.

105class SCIMSourceUser(InternallyManagedMixin, SerializerModel):
106    """Mapping of a user and source to a SCIM user ID"""
107
108    id = models.TextField(primary_key=True, default=uuid4)
109    external_id = models.TextField()
110    user = models.ForeignKey(User, on_delete=models.CASCADE)
111    source = models.ForeignKey(SCIMSource, on_delete=models.CASCADE)
112    attributes = models.JSONField(default=dict)
113    last_update = models.DateTimeField(auto_now=True)
114
115    @property
116    def serializer(self) -> BaseSerializer:
117        from authentik.sources.scim.api.users import SCIMSourceUserSerializer
118
119        return SCIMSourceUserSerializer
120
121    class Meta:
122        unique_together = (("external_id", "source"),)
123        indexes = [
124            models.Index(fields=["external_id"]),
125        ]
126
127    def __str__(self) -> str:
128        return f"SCIM User {self.user_id} to {self.source_id}"

Mapping of a user and source 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 external_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.

source

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.

def last_update(unknown):

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

serializer: rest_framework.serializers.BaseSerializer
115    @property
116    def serializer(self) -> BaseSerializer:
117        from authentik.sources.scim.api.users import SCIMSourceUserSerializer
118
119        return SCIMSourceUserSerializer

Get serializer for this model

user_id
source_id
def get_next_by_last_update(unknown):

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

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

def get_previous_by_last_update(unknown):

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

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

def objects(unknown):

The type of the None singleton.

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

The requested object does not exist

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

The query returned multiple objects when only one was expected.

131class SCIMSourceGroup(InternallyManagedMixin, SerializerModel):
132    """Mapping of a group and source to a SCIM user ID"""
133
134    id = models.TextField(primary_key=True, default=uuid4)
135    external_id = models.TextField()
136    group = models.ForeignKey(Group, on_delete=models.CASCADE)
137    source = models.ForeignKey(SCIMSource, on_delete=models.CASCADE)
138    attributes = models.JSONField(default=dict)
139    last_update = models.DateTimeField(auto_now=True)
140
141    @property
142    def serializer(self) -> BaseSerializer:
143        from authentik.sources.scim.api.groups import SCIMSourceGroupSerializer
144
145        return SCIMSourceGroupSerializer
146
147    class Meta:
148        unique_together = (("external_id", "source"),)
149        indexes = [
150            models.Index(fields=["external_id"]),
151        ]
152
153    def __str__(self) -> str:
154        return f"SCIM Group {self.group_id} to {self.source_id}"

Mapping of a group and source 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 external_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.

source

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.

def last_update(unknown):

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

serializer: rest_framework.serializers.BaseSerializer
141    @property
142    def serializer(self) -> BaseSerializer:
143        from authentik.sources.scim.api.groups import SCIMSourceGroupSerializer
144
145        return SCIMSourceGroupSerializer

Get serializer for this model

group_id
source_id
def get_next_by_last_update(unknown):

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

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

def get_previous_by_last_update(unknown):

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

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

def objects(unknown):

The type of the None singleton.

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

The requested object does not exist

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

The query returned multiple objects when only one was expected.