authentik.enterprise.lifecycle.utils

 1from datetime import datetime
 2from urllib import parse
 3
 4from django.contrib.contenttypes.models import ContentType
 5from django.db.models import Model
 6from django.urls import reverse
 7from rest_framework.serializers import ChoiceField, Serializer, UUIDField
 8
 9from authentik.core.api.utils import ModelSerializer
10from authentik.core.models import Application, Group, User
11from authentik.rbac.models import Role
12
13
14def parse_content_type(value: str) -> dict:
15    app_label, model = value.split(".")
16    return {"app_label": app_label, "model": model}
17
18
19def model_choices() -> list[tuple[str, str]]:
20    return [
21        ("authentik_core.application", "Application"),
22        ("authentik_core.group", "Group"),
23        ("authentik_rbac.role", "Role"),
24    ]
25
26
27def admin_link_for_model(model: Model) -> str:
28    if isinstance(model, Application):
29        url = f"/core/applications/{model.slug}"
30    elif isinstance(model, Group):
31        url = f"/identity/groups/{model.pk}"
32    elif isinstance(model, Role):
33        url = f"/identity/roles/{model.pk}"
34    else:
35        raise TypeError("Unsupported model")
36    return url + ";" + parse.quote('{"page":"page-lifecycle"}')
37
38
39def link_for_model(model: Model) -> str:
40    return f"{reverse("authentik_core:if-admin")}#{admin_link_for_model(model)}"
41
42
43def start_of_day(dt: datetime) -> datetime:
44    return dt.replace(hour=0, minute=0, second=0, microsecond=0)
45
46
47class ContentTypeField(ChoiceField):
48    def __init__(self, **kwargs):
49        super().__init__(choices=model_choices(), **kwargs)
50
51    def to_representation(self, content_type: ContentType) -> str:
52        return f"{content_type.app_label}.{content_type.model}"
53
54    def to_internal_value(self, data: str) -> ContentType:
55        return ContentType.objects.get(**parse_content_type(data))
56
57
58class GenericForeignKeySerializer(Serializer):
59    content_type = ContentTypeField()
60    object_id = UUIDField()
61
62
63class ReviewerGroupSerializer(ModelSerializer):
64    class Meta:
65        model = Group
66        fields = [
67            "pk",
68            "name",
69        ]
70
71
72class ReviewerUserSerializer(ModelSerializer):
73    class Meta:
74        model = User
75        fields = ["pk", "uuid", "username", "name"]
def parse_content_type(value: str) -> dict:
15def parse_content_type(value: str) -> dict:
16    app_label, model = value.split(".")
17    return {"app_label": app_label, "model": model}
def model_choices() -> list[tuple[str, str]]:
20def model_choices() -> list[tuple[str, str]]:
21    return [
22        ("authentik_core.application", "Application"),
23        ("authentik_core.group", "Group"),
24        ("authentik_rbac.role", "Role"),
25    ]
def start_of_day(dt: datetime.datetime) -> datetime.datetime:
44def start_of_day(dt: datetime) -> datetime:
45    return dt.replace(hour=0, minute=0, second=0, microsecond=0)
class ContentTypeField(rest_framework.fields.ChoiceField):
48class ContentTypeField(ChoiceField):
49    def __init__(self, **kwargs):
50        super().__init__(choices=model_choices(), **kwargs)
51
52    def to_representation(self, content_type: ContentType) -> str:
53        return f"{content_type.app_label}.{content_type.model}"
54
55    def to_internal_value(self, data: str) -> ContentType:
56        return ContentType.objects.get(**parse_content_type(data))
ContentTypeField(*args, **kwargs)
621    def __new__(cls, *args, **kwargs):
622        """
623        When a field is instantiated, we store the arguments that were used,
624        so that we can present a helpful representation of the object.
625        """
626        instance = super().__new__(cls)
627        instance._args = args
628        instance._kwargs = kwargs
629        return instance

When a field is instantiated, we store the arguments that were used, so that we can present a helpful representation of the object.

def to_representation( self, content_type: django.contrib.contenttypes.models.ContentType) -> str:
52    def to_representation(self, content_type: ContentType) -> str:
53        return f"{content_type.app_label}.{content_type.model}"

Transform the outgoing native value into primitive data.

def to_internal_value(self, data: str) -> django.contrib.contenttypes.models.ContentType:
55    def to_internal_value(self, data: str) -> ContentType:
56        return ContentType.objects.get(**parse_content_type(data))

Transform the incoming primitive data into a native value.

class GenericForeignKeySerializer(rest_framework.serializers.Serializer):
59class GenericForeignKeySerializer(Serializer):
60    content_type = ContentTypeField()
61    object_id = UUIDField()

The BaseSerializer class provides a minimal class which may be used for writing custom serializer implementations.

Note that we strongly restrict the ordering of operations/properties that may be used on the serializer in order to enforce correct usage.

In particular, if a data= argument is passed then:

.is_valid() - Available. .initial_data - Available. .validated_data - Only available after calling is_valid() .errors - Only available after calling is_valid() .data - Only available after calling is_valid()

If a data= argument is not passed then:

.is_valid() - Not available. .initial_data - Not available. .validated_data - Not available. .errors - Not available. .data - Available.

content_type
object_id
class ReviewerGroupSerializer(authentik.core.api.utils.ModelSerializer):
64class ReviewerGroupSerializer(ModelSerializer):
65    class Meta:
66        model = Group
67        fields = [
68            "pk",
69            "name",
70        ]

A ModelSerializer is just a regular Serializer, except that:

  • A set of default fields are automatically populated.
  • A set of default validators are automatically populated.
  • Default .create() and .update() implementations are provided.

The process of automatically determining a set of serializer fields based on the model fields is reasonably complex, but you almost certainly don't need to dig into the implementation.

If the ModelSerializer class doesn't generate the set of fields that you need you should either declare the extra/differing fields explicitly on the serializer class, or simply use a Serializer class.

class ReviewerGroupSerializer.Meta:
65    class Meta:
66        model = Group
67        fields = [
68            "pk",
69            "name",
70        ]
model = <class 'authentik.core.models.Group'>
fields = ['pk', 'name']
class ReviewerUserSerializer(authentik.core.api.utils.ModelSerializer):
73class ReviewerUserSerializer(ModelSerializer):
74    class Meta:
75        model = User
76        fields = ["pk", "uuid", "username", "name"]

A ModelSerializer is just a regular Serializer, except that:

  • A set of default fields are automatically populated.
  • A set of default validators are automatically populated.
  • Default .create() and .update() implementations are provided.

The process of automatically determining a set of serializer fields based on the model fields is reasonably complex, but you almost certainly don't need to dig into the implementation.

If the ModelSerializer class doesn't generate the set of fields that you need you should either declare the extra/differing fields explicitly on the serializer class, or simply use a Serializer class.

class ReviewerUserSerializer.Meta:
74    class Meta:
75        model = User
76        fields = ["pk", "uuid", "username", "name"]
model = <class 'authentik.core.models.User'>
fields = ['pk', 'uuid', 'username', 'name']