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            "flow_lockdown",
 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        }
 78
 79
 80class Themes(models.TextChoices):
 81    """Themes"""
 82
 83    AUTOMATIC = "automatic"
 84    LIGHT = "light"
 85    DARK = "dark"
 86
 87
 88def get_default_ui_footer_links():
 89    """Get default UI footer links based on current tenant settings"""
 90    return get_current_tenant().footer_links
 91
 92
 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    flow_lockdown = CharField(source="flow_lockdown.slug", required=False)
122
123    default_locale = CharField(read_only=True)
124    flags = SerializerMethodField()
125
126    @extend_schema_field(field=FlagJSONField)
127    def get_flags(self, _):
128        values = {}
129        for flag in Flag.available(visibility="public"):
130            values[flag().key] = flag.get()
131        return values
132
133
134class BrandViewSet(UsedByMixin, ModelViewSet):
135    """Brand Viewset"""
136
137    queryset = Brand.objects.all()
138    serializer_class = BrandSerializer
139    search_fields = [
140        "domain",
141        "branding_title",
142        "web_certificate__name",
143        "client_certificates__name",
144    ]
145    filterset_fields = [
146        "brand_uuid",
147        "domain",
148        "default",
149        "branding_title",
150        "branding_logo",
151        "branding_favicon",
152        "branding_default_flow_background",
153        "flow_authentication",
154        "flow_invalidation",
155        "flow_recovery",
156        "flow_unenrollment",
157        "flow_user_settings",
158        "flow_device_code",
159        "flow_lockdown",
160        "web_certificate",
161        "client_certificates",
162    ]
163    ordering = ["domain"]
164
165    filter_backends = [SecretKeyFilter, OrderingFilter, SearchFilter]
166
167    @extend_schema(
168        responses=CurrentBrandSerializer(many=False),
169    )
170    @action(methods=["GET"], detail=False, permission_classes=[AllowAny])
171    def current(self, request: Request) -> Response:
172        """Get current brand"""
173        brand: Brand = request._request.brand
174        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            "flow_lockdown",
69            "default_application",
70            "web_certificate",
71            "client_certificates",
72            "attributes",
73        ]
74        extra_kwargs = {
75            # TODO: This field isn't unique on the database which is hard to backport
76            # hence we just validate the uniqueness here
77            "domain": {"validators": [UniqueValidator(Brand.objects.all())]},
78        }

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            "flow_lockdown",
69            "default_application",
70            "web_certificate",
71            "client_certificates",
72            "attributes",
73        ]
74        extra_kwargs = {
75            # TODO: This field isn't unique on the database which is hard to backport
76            # hence we just validate the uniqueness here
77            "domain": {"validators": [UniqueValidator(Brand.objects.all())]},
78        }
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', 'flow_lockdown', 'default_application', 'web_certificate', 'client_certificates', 'attributes']
extra_kwargs = {'domain': {'validators': [<UniqueValidator(queryset=<QuerySet []>)>]}}
class Themes(django.db.models.enums.TextChoices):
81class Themes(models.TextChoices):
82    """Themes"""
83
84    AUTOMATIC = "automatic"
85    LIGHT = "light"
86    DARK = "dark"

Themes

AUTOMATIC = Themes.AUTOMATIC
LIGHT = Themes.LIGHT
DARK = Themes.DARK
class CurrentBrandSerializer(authentik.core.api.utils.PassiveSerializer):
 94class CurrentBrandSerializer(PassiveSerializer):
 95    """Partial brand information for styling"""
 96
 97    matched_domain = CharField(source="domain")
 98    branding_title = CharField()
 99    branding_logo = CharField(source="branding_logo_url")
100    branding_logo_themed_urls = ThemedUrlsSerializer(read_only=True, allow_null=True)
101    branding_favicon = CharField(source="branding_favicon_url")
102    branding_favicon_themed_urls = ThemedUrlsSerializer(read_only=True, allow_null=True)
103    branding_custom_css = CharField()
104    ui_footer_links = ListField(
105        child=FooterLinkSerializer(),
106        read_only=True,
107        default=get_default_ui_footer_links,
108    )
109    ui_theme = ChoiceField(
110        choices=Themes.choices,
111        source="attributes.settings.theme.base",
112        default=Themes.AUTOMATIC,
113        read_only=True,
114    )
115
116    flow_authentication = CharField(source="flow_authentication.slug", required=False)
117    flow_invalidation = CharField(source="flow_invalidation.slug", required=False)
118    flow_recovery = CharField(source="flow_recovery.slug", required=False)
119    flow_unenrollment = CharField(source="flow_unenrollment.slug", required=False)
120    flow_user_settings = CharField(source="flow_user_settings.slug", required=False)
121    flow_device_code = CharField(source="flow_device_code.slug", required=False)
122    flow_lockdown = CharField(source="flow_lockdown.slug", required=False)
123
124    default_locale = CharField(read_only=True)
125    flags = SerializerMethodField()
126
127    @extend_schema_field(field=FlagJSONField)
128    def get_flags(self, _):
129        values = {}
130        for flag in Flag.available(visibility="public"):
131            values[flag().key] = flag.get()
132        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
flow_lockdown
default_locale
flags
@extend_schema_field(field=FlagJSONField)
def get_flags(self, _):
127    @extend_schema_field(field=FlagJSONField)
128    def get_flags(self, _):
129        values = {}
130        for flag in Flag.available(visibility="public"):
131            values[flag().key] = flag.get()
132        return values
class BrandViewSet(authentik.core.api.used_by.UsedByMixin, rest_framework.viewsets.ModelViewSet):
135class BrandViewSet(UsedByMixin, ModelViewSet):
136    """Brand Viewset"""
137
138    queryset = Brand.objects.all()
139    serializer_class = BrandSerializer
140    search_fields = [
141        "domain",
142        "branding_title",
143        "web_certificate__name",
144        "client_certificates__name",
145    ]
146    filterset_fields = [
147        "brand_uuid",
148        "domain",
149        "default",
150        "branding_title",
151        "branding_logo",
152        "branding_favicon",
153        "branding_default_flow_background",
154        "flow_authentication",
155        "flow_invalidation",
156        "flow_recovery",
157        "flow_unenrollment",
158        "flow_user_settings",
159        "flow_device_code",
160        "flow_lockdown",
161        "web_certificate",
162        "client_certificates",
163    ]
164    ordering = ["domain"]
165
166    filter_backends = [SecretKeyFilter, OrderingFilter, SearchFilter]
167
168    @extend_schema(
169        responses=CurrentBrandSerializer(many=False),
170    )
171    @action(methods=["GET"], detail=False, permission_classes=[AllowAny])
172    def current(self, request: Request) -> Response:
173        """Get current brand"""
174        brand: Brand = request._request.brand
175        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', 'flow_lockdown', '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:
168    @extend_schema(
169        responses=CurrentBrandSerializer(many=False),
170    )
171    @action(methods=["GET"], detail=False, permission_classes=[AllowAny])
172    def current(self, request: Request) -> Response:
173        """Get current brand"""
174        brand: Brand = request._request.brand
175        return Response(CurrentBrandSerializer(brand, context={"request": request}).data)

Get current brand

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