authentik.core.api.object_types

API Utilities

 1"""API Utilities"""
 2
 3from drf_spectacular.utils import extend_schema
 4from rest_framework.decorators import action
 5from rest_framework.fields import (
 6    BooleanField,
 7    CharField,
 8)
 9from rest_framework.request import Request
10from rest_framework.response import Response
11
12from authentik.core.api.utils import PassiveSerializer
13from authentik.lib.models import DeprecatedMixin
14from authentik.lib.utils.reflection import all_subclasses
15
16
17class TypeCreateSerializer(PassiveSerializer):
18    """Types of an object that can be created"""
19
20    name = CharField(required=True)
21    description = CharField(required=True)
22    component = CharField(required=True)
23    model_name = CharField(required=True)
24
25    icon_url = CharField(required=False)
26    requires_enterprise = BooleanField(default=False)
27    deprecated = BooleanField(default=False)
28
29
30class CreatableType:
31    """Class to inherit from to mark a model as creatable, even if the model itself is marked
32    as abstract"""
33
34
35class NonCreatableType:
36    """Class to inherit from to mark a model as non-creatable even if it is not abstract"""
37
38
39class TypesMixin:
40    """Mixin which adds an API endpoint to list all possible types that can be created"""
41
42    @extend_schema(responses={200: TypeCreateSerializer(many=True)})
43    @action(detail=False, pagination_class=None, filter_backends=[])
44    def types(self, request: Request, additional: list[dict] | None = None) -> Response:
45        """Get all creatable types"""
46        data = []
47        for subclass in all_subclasses(self.queryset.model):
48            instance = None
49            if subclass._meta.abstract:
50                if not issubclass(subclass, CreatableType):
51                    continue
52                # Circumvent the django protection for not being able to instantiate
53                # abstract models. We need a model instance to access .component
54                # and further down .icon_url
55                instance = subclass.__new__(subclass)
56                # Django re-sets abstract = False so we need to override that
57                instance.Meta.abstract = True
58            else:
59                if issubclass(subclass, NonCreatableType):
60                    continue
61                instance = subclass()
62            try:
63                type_signature = {
64                    "name": subclass._meta.verbose_name,
65                    "description": subclass.__doc__,
66                    "component": instance.component,
67                    "model_name": subclass._meta.model_name,
68                    "icon_url": getattr(instance, "icon_url", None),
69                    "requires_enterprise": False,
70                    "deprecated": isinstance(instance, DeprecatedMixin),
71                }
72                try:
73                    from authentik.enterprise.apps import EnterpriseConfig
74
75                    type_signature["requires_enterprise"] = isinstance(
76                        subclass._meta.app_config, EnterpriseConfig
77                    )
78                except ModuleNotFoundError:
79                    pass
80
81                data.append(type_signature)
82            except NotImplementedError:
83                continue
84        if additional:
85            data.extend(additional)
86        data = sorted(data, key=lambda x: x["name"])
87        return Response(TypeCreateSerializer(data, many=True).data)
class TypeCreateSerializer(authentik.core.api.utils.PassiveSerializer):
18class TypeCreateSerializer(PassiveSerializer):
19    """Types of an object that can be created"""
20
21    name = CharField(required=True)
22    description = CharField(required=True)
23    component = CharField(required=True)
24    model_name = CharField(required=True)
25
26    icon_url = CharField(required=False)
27    requires_enterprise = BooleanField(default=False)
28    deprecated = BooleanField(default=False)

Types of an object that can be created

name
description
component
model_name
icon_url
requires_enterprise
deprecated
class CreatableType:
31class CreatableType:
32    """Class to inherit from to mark a model as creatable, even if the model itself is marked
33    as abstract"""

Class to inherit from to mark a model as creatable, even if the model itself is marked as abstract

class NonCreatableType:
36class NonCreatableType:
37    """Class to inherit from to mark a model as non-creatable even if it is not abstract"""

Class to inherit from to mark a model as non-creatable even if it is not abstract

class TypesMixin:
40class TypesMixin:
41    """Mixin which adds an API endpoint to list all possible types that can be created"""
42
43    @extend_schema(responses={200: TypeCreateSerializer(many=True)})
44    @action(detail=False, pagination_class=None, filter_backends=[])
45    def types(self, request: Request, additional: list[dict] | None = None) -> Response:
46        """Get all creatable types"""
47        data = []
48        for subclass in all_subclasses(self.queryset.model):
49            instance = None
50            if subclass._meta.abstract:
51                if not issubclass(subclass, CreatableType):
52                    continue
53                # Circumvent the django protection for not being able to instantiate
54                # abstract models. We need a model instance to access .component
55                # and further down .icon_url
56                instance = subclass.__new__(subclass)
57                # Django re-sets abstract = False so we need to override that
58                instance.Meta.abstract = True
59            else:
60                if issubclass(subclass, NonCreatableType):
61                    continue
62                instance = subclass()
63            try:
64                type_signature = {
65                    "name": subclass._meta.verbose_name,
66                    "description": subclass.__doc__,
67                    "component": instance.component,
68                    "model_name": subclass._meta.model_name,
69                    "icon_url": getattr(instance, "icon_url", None),
70                    "requires_enterprise": False,
71                    "deprecated": isinstance(instance, DeprecatedMixin),
72                }
73                try:
74                    from authentik.enterprise.apps import EnterpriseConfig
75
76                    type_signature["requires_enterprise"] = isinstance(
77                        subclass._meta.app_config, EnterpriseConfig
78                    )
79                except ModuleNotFoundError:
80                    pass
81
82                data.append(type_signature)
83            except NotImplementedError:
84                continue
85        if additional:
86            data.extend(additional)
87        data = sorted(data, key=lambda x: x["name"])
88        return Response(TypeCreateSerializer(data, many=True).data)

Mixin which adds an API endpoint to list all possible types that can be created

@extend_schema(responses={200: TypeCreateSerializer(many=True)})
@action(detail=False, pagination_class=None, filter_backends=[])
def types( self, request: rest_framework.request.Request, additional: list[dict] | None = None) -> rest_framework.response.Response:
43    @extend_schema(responses={200: TypeCreateSerializer(many=True)})
44    @action(detail=False, pagination_class=None, filter_backends=[])
45    def types(self, request: Request, additional: list[dict] | None = None) -> Response:
46        """Get all creatable types"""
47        data = []
48        for subclass in all_subclasses(self.queryset.model):
49            instance = None
50            if subclass._meta.abstract:
51                if not issubclass(subclass, CreatableType):
52                    continue
53                # Circumvent the django protection for not being able to instantiate
54                # abstract models. We need a model instance to access .component
55                # and further down .icon_url
56                instance = subclass.__new__(subclass)
57                # Django re-sets abstract = False so we need to override that
58                instance.Meta.abstract = True
59            else:
60                if issubclass(subclass, NonCreatableType):
61                    continue
62                instance = subclass()
63            try:
64                type_signature = {
65                    "name": subclass._meta.verbose_name,
66                    "description": subclass.__doc__,
67                    "component": instance.component,
68                    "model_name": subclass._meta.model_name,
69                    "icon_url": getattr(instance, "icon_url", None),
70                    "requires_enterprise": False,
71                    "deprecated": isinstance(instance, DeprecatedMixin),
72                }
73                try:
74                    from authentik.enterprise.apps import EnterpriseConfig
75
76                    type_signature["requires_enterprise"] = isinstance(
77                        subclass._meta.app_config, EnterpriseConfig
78                    )
79                except ModuleNotFoundError:
80                    pass
81
82                data.append(type_signature)
83            except NotImplementedError:
84                continue
85        if additional:
86            data.extend(additional)
87        data = sorted(data, key=lambda x: x["name"])
88        return Response(TypeCreateSerializer(data, many=True).data)

Get all creatable types