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)
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)
Inherited Members
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']
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
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
Inherited Members
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
serializer_class =
<class 'BrandSerializer'>
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']
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