authentik.brands.api

Serializer for brands models

  1"""Serializer for brands models"""
  2
  3from typing import Any
  4
  5from django.db import models
  6from drf_spectacular.utils import extend_schema, extend_schema_field
  7from rest_framework.decorators import action
  8from rest_framework.exceptions import ValidationError
  9from rest_framework.fields import (
 10    CharField,
 11    ChoiceField,
 12    ListField,
 13    SerializerMethodField,
 14)
 15from rest_framework.filters import OrderingFilter, SearchFilter
 16from rest_framework.permissions import AllowAny
 17from rest_framework.request import Request
 18from rest_framework.response import Response
 19from rest_framework.validators import UniqueValidator
 20from rest_framework.viewsets import ModelViewSet
 21
 22from authentik.brands.models import Brand
 23from authentik.core.api.used_by import UsedByMixin
 24from authentik.core.api.utils import ModelSerializer, PassiveSerializer, ThemedUrlsSerializer
 25from authentik.rbac.filters import SecretKeyFilter
 26from authentik.tenants.api.settings import FlagJSONField
 27from authentik.tenants.flags import Flag
 28from authentik.tenants.utils import get_current_tenant
 29
 30
 31class FooterLinkSerializer(PassiveSerializer):
 32    """Links returned in Config API"""
 33
 34    href = CharField(read_only=True, allow_null=True)
 35    name = CharField(read_only=True)
 36
 37
 38class BrandSerializer(ModelSerializer):
 39    """Brand Serializer"""
 40
 41    def validate(self, attrs: dict[str, Any]) -> dict[str, Any]:
 42        if attrs.get("default", False):
 43            brands = Brand.objects.filter(default=True)
 44            if self.instance:
 45                brands = brands.exclude(pk=self.instance.pk)
 46            if brands.exists():
 47                raise ValidationError({"default": "Only a single brand can be set as default."})
 48        return super().validate(attrs)
 49
 50    class Meta:
 51        model = Brand
 52        fields = [
 53            "brand_uuid",
 54            "domain",
 55            "default",
 56            "branding_title",
 57            "branding_logo",
 58            "branding_favicon",
 59            "branding_custom_css",
 60            "branding_default_flow_background",
 61            "flow_authentication",
 62            "flow_invalidation",
 63            "flow_recovery",
 64            "flow_unenrollment",
 65            "flow_user_settings",
 66            "flow_device_code",
 67            "default_application",
 68            "web_certificate",
 69            "client_certificates",
 70            "attributes",
 71        ]
 72        extra_kwargs = {
 73            # TODO: This field isn't unique on the database which is hard to backport
 74            # hence we just validate the uniqueness here
 75            "domain": {"validators": [UniqueValidator(Brand.objects.all())]},
 76        }
 77
 78
 79class Themes(models.TextChoices):
 80    """Themes"""
 81
 82    AUTOMATIC = "automatic"
 83    LIGHT = "light"
 84    DARK = "dark"
 85
 86
 87def get_default_ui_footer_links():
 88    """Get default UI footer links based on current tenant settings"""
 89    return get_current_tenant().footer_links
 90
 91
 92class CurrentBrandSerializer(PassiveSerializer):
 93    """Partial brand information for styling"""
 94
 95    matched_domain = CharField(source="domain")
 96    branding_title = CharField()
 97    branding_logo = CharField(source="branding_logo_url")
 98    branding_logo_themed_urls = ThemedUrlsSerializer(read_only=True, allow_null=True)
 99    branding_favicon = CharField(source="branding_favicon_url")
100    branding_favicon_themed_urls = ThemedUrlsSerializer(read_only=True, allow_null=True)
101    branding_custom_css = CharField()
102    ui_footer_links = ListField(
103        child=FooterLinkSerializer(),
104        read_only=True,
105        default=get_default_ui_footer_links,
106    )
107    ui_theme = ChoiceField(
108        choices=Themes.choices,
109        source="attributes.settings.theme.base",
110        default=Themes.AUTOMATIC,
111        read_only=True,
112    )
113
114    flow_authentication = CharField(source="flow_authentication.slug", required=False)
115    flow_invalidation = CharField(source="flow_invalidation.slug", required=False)
116    flow_recovery = CharField(source="flow_recovery.slug", required=False)
117    flow_unenrollment = CharField(source="flow_unenrollment.slug", required=False)
118    flow_user_settings = CharField(source="flow_user_settings.slug", required=False)
119    flow_device_code = CharField(source="flow_device_code.slug", required=False)
120
121    default_locale = CharField(read_only=True)
122    flags = SerializerMethodField()
123
124    @extend_schema_field(field=FlagJSONField)
125    def get_flags(self, _):
126        values = {}
127        for flag in Flag.available(visibility="public"):
128            values[flag().key] = flag.get()
129        return values
130
131
132class BrandViewSet(UsedByMixin, ModelViewSet):
133    """Brand Viewset"""
134
135    queryset = Brand.objects.all()
136    serializer_class = BrandSerializer
137    search_fields = [
138        "domain",
139        "branding_title",
140        "web_certificate__name",
141        "client_certificates__name",
142    ]
143    filterset_fields = [
144        "brand_uuid",
145        "domain",
146        "default",
147        "branding_title",
148        "branding_logo",
149        "branding_favicon",
150        "branding_default_flow_background",
151        "flow_authentication",
152        "flow_invalidation",
153        "flow_recovery",
154        "flow_unenrollment",
155        "flow_user_settings",
156        "flow_device_code",
157        "web_certificate",
158        "client_certificates",
159    ]
160    ordering = ["domain"]
161
162    filter_backends = [SecretKeyFilter, OrderingFilter, SearchFilter]
163
164    @extend_schema(
165        responses=CurrentBrandSerializer(many=False),
166    )
167    @action(methods=["GET"], detail=False, permission_classes=[AllowAny])
168    def current(self, request: Request) -> Response:
169        """Get current brand"""
170        brand: Brand = request._request.brand
171        return Response(CurrentBrandSerializer(brand, context={"request": request}).data)
class FooterLinkSerializer(authentik.core.api.utils.PassiveSerializer):
32class FooterLinkSerializer(PassiveSerializer):
33    """Links returned in Config API"""
34
35    href = CharField(read_only=True, allow_null=True)
36    name = CharField(read_only=True)

Links returned in Config API

href
name
class BrandSerializer(authentik.core.api.utils.ModelSerializer):
39class BrandSerializer(ModelSerializer):
40    """Brand Serializer"""
41
42    def validate(self, attrs: dict[str, Any]) -> dict[str, Any]:
43        if attrs.get("default", False):
44            brands = Brand.objects.filter(default=True)
45            if self.instance:
46                brands = brands.exclude(pk=self.instance.pk)
47            if brands.exists():
48                raise ValidationError({"default": "Only a single brand can be set as default."})
49        return super().validate(attrs)
50
51    class Meta:
52        model = Brand
53        fields = [
54            "brand_uuid",
55            "domain",
56            "default",
57            "branding_title",
58            "branding_logo",
59            "branding_favicon",
60            "branding_custom_css",
61            "branding_default_flow_background",
62            "flow_authentication",
63            "flow_invalidation",
64            "flow_recovery",
65            "flow_unenrollment",
66            "flow_user_settings",
67            "flow_device_code",
68            "default_application",
69            "web_certificate",
70            "client_certificates",
71            "attributes",
72        ]
73        extra_kwargs = {
74            # TODO: This field isn't unique on the database which is hard to backport
75            # hence we just validate the uniqueness here
76            "domain": {"validators": [UniqueValidator(Brand.objects.all())]},
77        }

Brand Serializer

def validate(self, attrs: dict[str, typing.Any]) -> dict[str, typing.Any]:
42    def validate(self, attrs: dict[str, Any]) -> dict[str, Any]:
43        if attrs.get("default", False):
44            brands = Brand.objects.filter(default=True)
45            if self.instance:
46                brands = brands.exclude(pk=self.instance.pk)
47            if brands.exists():
48                raise ValidationError({"default": "Only a single brand can be set as default."})
49        return super().validate(attrs)
class BrandSerializer.Meta:
51    class Meta:
52        model = Brand
53        fields = [
54            "brand_uuid",
55            "domain",
56            "default",
57            "branding_title",
58            "branding_logo",
59            "branding_favicon",
60            "branding_custom_css",
61            "branding_default_flow_background",
62            "flow_authentication",
63            "flow_invalidation",
64            "flow_recovery",
65            "flow_unenrollment",
66            "flow_user_settings",
67            "flow_device_code",
68            "default_application",
69            "web_certificate",
70            "client_certificates",
71            "attributes",
72        ]
73        extra_kwargs = {
74            # TODO: This field isn't unique on the database which is hard to backport
75            # hence we just validate the uniqueness here
76            "domain": {"validators": [UniqueValidator(Brand.objects.all())]},
77        }
model = <class 'authentik.brands.models.Brand'>
fields = ['brand_uuid', 'domain', 'default', 'branding_title', 'branding_logo', 'branding_favicon', 'branding_custom_css', 'branding_default_flow_background', 'flow_authentication', 'flow_invalidation', 'flow_recovery', 'flow_unenrollment', 'flow_user_settings', 'flow_device_code', 'default_application', 'web_certificate', 'client_certificates', 'attributes']
extra_kwargs = {'domain': {'validators': [<UniqueValidator(queryset=<QuerySet []>)>]}}
class Themes(django.db.models.enums.TextChoices):
80class Themes(models.TextChoices):
81    """Themes"""
82
83    AUTOMATIC = "automatic"
84    LIGHT = "light"
85    DARK = "dark"

Themes

AUTOMATIC = Themes.AUTOMATIC
LIGHT = Themes.LIGHT
DARK = Themes.DARK
class CurrentBrandSerializer(authentik.core.api.utils.PassiveSerializer):
 93class CurrentBrandSerializer(PassiveSerializer):
 94    """Partial brand information for styling"""
 95
 96    matched_domain = CharField(source="domain")
 97    branding_title = CharField()
 98    branding_logo = CharField(source="branding_logo_url")
 99    branding_logo_themed_urls = ThemedUrlsSerializer(read_only=True, allow_null=True)
100    branding_favicon = CharField(source="branding_favicon_url")
101    branding_favicon_themed_urls = ThemedUrlsSerializer(read_only=True, allow_null=True)
102    branding_custom_css = CharField()
103    ui_footer_links = ListField(
104        child=FooterLinkSerializer(),
105        read_only=True,
106        default=get_default_ui_footer_links,
107    )
108    ui_theme = ChoiceField(
109        choices=Themes.choices,
110        source="attributes.settings.theme.base",
111        default=Themes.AUTOMATIC,
112        read_only=True,
113    )
114
115    flow_authentication = CharField(source="flow_authentication.slug", required=False)
116    flow_invalidation = CharField(source="flow_invalidation.slug", required=False)
117    flow_recovery = CharField(source="flow_recovery.slug", required=False)
118    flow_unenrollment = CharField(source="flow_unenrollment.slug", required=False)
119    flow_user_settings = CharField(source="flow_user_settings.slug", required=False)
120    flow_device_code = CharField(source="flow_device_code.slug", required=False)
121
122    default_locale = CharField(read_only=True)
123    flags = SerializerMethodField()
124
125    @extend_schema_field(field=FlagJSONField)
126    def get_flags(self, _):
127        values = {}
128        for flag in Flag.available(visibility="public"):
129            values[flag().key] = flag.get()
130        return values

Partial brand information for styling

matched_domain
branding_title
branding_logo_themed_urls
branding_favicon
branding_favicon_themed_urls
branding_custom_css
ui_theme
flow_authentication
flow_invalidation
flow_recovery
flow_unenrollment
flow_user_settings
flow_device_code
default_locale
flags
@extend_schema_field(field=FlagJSONField)
def get_flags(self, _):
125    @extend_schema_field(field=FlagJSONField)
126    def get_flags(self, _):
127        values = {}
128        for flag in Flag.available(visibility="public"):
129            values[flag().key] = flag.get()
130        return values
class BrandViewSet(authentik.core.api.used_by.UsedByMixin, rest_framework.viewsets.ModelViewSet):
133class BrandViewSet(UsedByMixin, ModelViewSet):
134    """Brand Viewset"""
135
136    queryset = Brand.objects.all()
137    serializer_class = BrandSerializer
138    search_fields = [
139        "domain",
140        "branding_title",
141        "web_certificate__name",
142        "client_certificates__name",
143    ]
144    filterset_fields = [
145        "brand_uuid",
146        "domain",
147        "default",
148        "branding_title",
149        "branding_logo",
150        "branding_favicon",
151        "branding_default_flow_background",
152        "flow_authentication",
153        "flow_invalidation",
154        "flow_recovery",
155        "flow_unenrollment",
156        "flow_user_settings",
157        "flow_device_code",
158        "web_certificate",
159        "client_certificates",
160    ]
161    ordering = ["domain"]
162
163    filter_backends = [SecretKeyFilter, OrderingFilter, SearchFilter]
164
165    @extend_schema(
166        responses=CurrentBrandSerializer(many=False),
167    )
168    @action(methods=["GET"], detail=False, permission_classes=[AllowAny])
169    def current(self, request: Request) -> Response:
170        """Get current brand"""
171        brand: Brand = request._request.brand
172        return Response(CurrentBrandSerializer(brand, context={"request": request}).data)

Brand Viewset

queryset = <QuerySet []>
serializer_class = <class 'BrandSerializer'>
search_fields = ['domain', 'branding_title', 'web_certificate__name', 'client_certificates__name']
filterset_fields = ['brand_uuid', 'domain', 'default', 'branding_title', 'branding_logo', 'branding_favicon', 'branding_default_flow_background', 'flow_authentication', 'flow_invalidation', 'flow_recovery', 'flow_unenrollment', 'flow_user_settings', 'flow_device_code', 'web_certificate', 'client_certificates']
ordering = ['domain']
filter_backends = [<class 'authentik.rbac.filters.SecretKeyFilter'>, <class 'rest_framework.filters.OrderingFilter'>, <class 'rest_framework.filters.SearchFilter'>]
@extend_schema(responses=CurrentBrandSerializer(many=False))
@action(methods=['GET'], detail=False, permission_classes=[AllowAny])
def current( self, request: rest_framework.request.Request) -> rest_framework.response.Response:
165    @extend_schema(
166        responses=CurrentBrandSerializer(many=False),
167    )
168    @action(methods=["GET"], detail=False, permission_classes=[AllowAny])
169    def current(self, request: Request) -> Response:
170        """Get current brand"""
171        brand: Brand = request._request.brand
172        return Response(CurrentBrandSerializer(brand, context={"request": request}).data)

Get current brand

name = None
description = None
suffix = None
detail = None
basename = None