mirror of
https://github.com/goauthentik/authentik.git
synced 2026-06-17 19:09:11 +03:00
start adding system settings model
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
"""Serializer for settings"""
|
||||
|
||||
from typing import get_args
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from drf_spectacular.extensions import OpenApiSerializerFieldExtension
|
||||
from drf_spectacular.plumbing import build_basic_type, build_object_type
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.fields import JSONField
|
||||
from rest_framework.generics import RetrieveUpdateAPIView
|
||||
from rest_framework.permissions import SAFE_METHODS
|
||||
|
||||
from authentik.admin.models import SystemSettings
|
||||
from authentik.core.api.utils import JSONDictField, ModelSerializer
|
||||
from authentik.rbac.permissions import HasPermission
|
||||
from authentik.tenants.flags import Flag
|
||||
|
||||
|
||||
class FlagJSONField(JSONDictField):
|
||||
def to_internal_value(self, data: str):
|
||||
flags = super().to_internal_value(data)
|
||||
for flag in Flag.available(visibility="system", exclude_system=False):
|
||||
flags[flag().key] = flag.get()
|
||||
return flags
|
||||
|
||||
def to_representation(self, value: dict) -> dict:
|
||||
new_value = value.copy()
|
||||
for flag in Flag.available(exclude_system=False):
|
||||
_flag = flag()
|
||||
# Exclude any system flags that aren't modifiable
|
||||
if _flag.visibility == "system":
|
||||
new_value.pop(_flag.key, None)
|
||||
# Explicitly present unset flags as if they were set to default
|
||||
if _flag.key not in value:
|
||||
value[_flag.key] = _flag.default
|
||||
return super().to_representation(new_value)
|
||||
|
||||
def run_validators(self, value: dict):
|
||||
super().run_validators(value)
|
||||
for flag in Flag.available():
|
||||
_flag = flag()
|
||||
if _flag.key not in value:
|
||||
continue
|
||||
flag_value = value.get(_flag.key)
|
||||
flag_type = get_args(_flag.__orig_bases__[0])[0]
|
||||
if flag_value and not isinstance(flag_value, flag_type):
|
||||
raise ValidationError(
|
||||
_("Value for flag {flag_key} needs to be of type {type}.").format(
|
||||
flag_key=_flag.key, type=flag_type.__name__
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class FlagsJSONExtension(OpenApiSerializerFieldExtension):
|
||||
"""Generate API Schema for JSON fields as"""
|
||||
|
||||
target_class = "authentik.tenants.api.settings.FlagJSONField"
|
||||
|
||||
def map_serializer_field(self, auto_schema, direction):
|
||||
props = {}
|
||||
for flag in Flag.available():
|
||||
_flag = flag()
|
||||
props[_flag.key] = build_basic_type(get_args(_flag.__orig_bases__[0])[0])
|
||||
if _flag.description:
|
||||
props[_flag.key]["description"] = _flag.description
|
||||
if _flag.deprecated:
|
||||
props[_flag.key]["deprecated"] = _flag.deprecated
|
||||
return build_object_type(props, required=props.keys())
|
||||
|
||||
|
||||
class SettingsSerializer(ModelSerializer):
|
||||
"""Settings Serializer"""
|
||||
|
||||
footer_links = JSONField(required=False)
|
||||
flags = FlagJSONField()
|
||||
|
||||
class Meta:
|
||||
model = SystemSettings
|
||||
fields = [
|
||||
"avatars",
|
||||
"default_user_change_name",
|
||||
"default_user_change_email",
|
||||
"default_user_change_username",
|
||||
"event_retention",
|
||||
"reputation_lower_limit",
|
||||
"reputation_upper_limit",
|
||||
"footer_links",
|
||||
"gdpr_compliance",
|
||||
"impersonation",
|
||||
"impersonation_require_reason",
|
||||
"default_token_duration",
|
||||
"default_token_length",
|
||||
"pagination_default_page_size",
|
||||
"pagination_max_page_size",
|
||||
"flags",
|
||||
]
|
||||
|
||||
|
||||
class SettingsView(RetrieveUpdateAPIView):
|
||||
"""Settings view"""
|
||||
|
||||
queryset = SystemSettings.objects.filter(pk=True)
|
||||
serializer_class = SettingsSerializer
|
||||
filter_backends = []
|
||||
|
||||
def get_permissions(self):
|
||||
return [
|
||||
HasPermission(
|
||||
"authentik_rbac.view_system_settings"
|
||||
if self.request.method in SAFE_METHODS
|
||||
else "authentik_rbac.edit_system_settings"
|
||||
)()
|
||||
]
|
||||
@@ -0,0 +1,152 @@
|
||||
# Generated by Django 5.2.15 on 2026-06-17 14:56
|
||||
|
||||
import authentik.lib.models
|
||||
import authentik.lib.utils.time
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = []
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="VersionHistory",
|
||||
fields=[
|
||||
("id", models.BigAutoField(primary_key=True, serialize=False)),
|
||||
("timestamp", models.DateTimeField()),
|
||||
("version", models.TextField()),
|
||||
("build", models.TextField()),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Version history",
|
||||
"verbose_name_plural": "Version history",
|
||||
"db_table": "authentik_version_history",
|
||||
"ordering": ("-timestamp",),
|
||||
"managed": False,
|
||||
"default_permissions": [],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="SystemSettings",
|
||||
fields=[
|
||||
("id", models.BooleanField(default=True, primary_key=True, serialize=False)),
|
||||
(
|
||||
"avatars",
|
||||
models.TextField(
|
||||
default="gravatar,initials",
|
||||
help_text="Configure how authentik should show avatars for users.",
|
||||
),
|
||||
),
|
||||
(
|
||||
"default_user_change_name",
|
||||
models.BooleanField(
|
||||
default=True, help_text="Enable the ability for users to change their name."
|
||||
),
|
||||
),
|
||||
(
|
||||
"default_user_change_email",
|
||||
models.BooleanField(
|
||||
default=False,
|
||||
help_text="Enable the ability for users to change their email address.",
|
||||
),
|
||||
),
|
||||
(
|
||||
"default_user_change_username",
|
||||
models.BooleanField(
|
||||
default=False,
|
||||
help_text="Enable the ability for users to change their username.",
|
||||
),
|
||||
),
|
||||
(
|
||||
"event_retention",
|
||||
models.TextField(
|
||||
default="days=365",
|
||||
help_text="Events will be deleted after this duration.(Format: weeks=3;days=2;hours=3,seconds=2).",
|
||||
validators=[authentik.lib.utils.time.timedelta_string_validator],
|
||||
),
|
||||
),
|
||||
(
|
||||
"reputation_lower_limit",
|
||||
models.IntegerField(
|
||||
default=-5,
|
||||
help_text="Reputation cannot decrease lower than this value. Zero or negative.",
|
||||
validators=[django.core.validators.MaxValueValidator(0)],
|
||||
),
|
||||
),
|
||||
(
|
||||
"reputation_upper_limit",
|
||||
models.IntegerField(
|
||||
default=5,
|
||||
help_text="Reputation cannot increase higher than this value. Zero or positive.",
|
||||
validators=[django.core.validators.MinValueValidator(0)],
|
||||
),
|
||||
),
|
||||
(
|
||||
"footer_links",
|
||||
models.JSONField(
|
||||
blank=True,
|
||||
default=list,
|
||||
help_text="The option configures the footer links on the flow executor pages.",
|
||||
),
|
||||
),
|
||||
(
|
||||
"gdpr_compliance",
|
||||
models.BooleanField(
|
||||
default=True,
|
||||
help_text="When enabled, all the events caused by a user will be deleted upon the user's deletion.",
|
||||
),
|
||||
),
|
||||
(
|
||||
"impersonation",
|
||||
models.BooleanField(
|
||||
default=True, help_text="Globally enable/disable impersonation."
|
||||
),
|
||||
),
|
||||
(
|
||||
"impersonation_require_reason",
|
||||
models.BooleanField(
|
||||
default=True,
|
||||
help_text="Require administrators to provide a reason for impersonating a user.",
|
||||
),
|
||||
),
|
||||
(
|
||||
"default_token_duration",
|
||||
models.TextField(
|
||||
default="days=1",
|
||||
help_text="Default token duration",
|
||||
validators=[authentik.lib.utils.time.timedelta_string_validator],
|
||||
),
|
||||
),
|
||||
(
|
||||
"default_token_length",
|
||||
models.PositiveIntegerField(
|
||||
default=60,
|
||||
help_text="Default token length",
|
||||
validators=[django.core.validators.MinValueValidator(1)],
|
||||
),
|
||||
),
|
||||
(
|
||||
"pagination_default_page_size",
|
||||
models.PositiveIntegerField(
|
||||
default=20,
|
||||
help_text="Default page size for API responses, if no size was requested.",
|
||||
),
|
||||
),
|
||||
(
|
||||
"pagination_max_page_size",
|
||||
models.PositiveIntegerField(default=100, help_text="Maximum page size"),
|
||||
),
|
||||
("flags", models.JSONField(default=dict)),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "System settings",
|
||||
"verbose_name_plural": "System settings",
|
||||
"default_permissions": [],
|
||||
},
|
||||
bases=(authentik.lib.models.InternallyManagedMixin, models.Model),
|
||||
),
|
||||
]
|
||||
+105
-1
@@ -1,7 +1,111 @@
|
||||
"""authentik admin models"""
|
||||
|
||||
from django.db import models
|
||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||
from django.db import IntegrityError, models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework.serializers import Serializer
|
||||
|
||||
from authentik.lib.models import InternallyManagedMixin, SerializerModel
|
||||
from authentik.lib.utils.time import timedelta_string_validator
|
||||
|
||||
DEFAULT_TOKEN_DURATION = "days=1" # nosec
|
||||
DEFAULT_TOKEN_LENGTH = 60
|
||||
DEFAULT_REPUTATION_LOWER_LIMIT = -5
|
||||
DEFAULT_REPUTATION_UPPER_LIMIT = 5
|
||||
|
||||
|
||||
class SystemSettings(InternallyManagedMixin, SerializerModel):
|
||||
id = models.BooleanField(primary_key=True, default=True)
|
||||
|
||||
avatars = models.TextField(
|
||||
help_text=_("Configure how authentik should show avatars for users."),
|
||||
default="gravatar,initials",
|
||||
)
|
||||
default_user_change_name = models.BooleanField(
|
||||
help_text=_("Enable the ability for users to change their name."), default=True
|
||||
)
|
||||
default_user_change_email = models.BooleanField(
|
||||
help_text=_("Enable the ability for users to change their email address."), default=False
|
||||
)
|
||||
default_user_change_username = models.BooleanField(
|
||||
help_text=_("Enable the ability for users to change their username."), default=False
|
||||
)
|
||||
event_retention = models.TextField(
|
||||
default="days=365",
|
||||
validators=[timedelta_string_validator],
|
||||
help_text=_(
|
||||
"Events will be deleted after this duration.(Format: weeks=3;days=2;hours=3,seconds=2)."
|
||||
),
|
||||
)
|
||||
reputation_lower_limit = models.IntegerField(
|
||||
help_text=_("Reputation cannot decrease lower than this value. Zero or negative."),
|
||||
default=DEFAULT_REPUTATION_LOWER_LIMIT,
|
||||
validators=[MaxValueValidator(0)],
|
||||
)
|
||||
reputation_upper_limit = models.IntegerField(
|
||||
help_text=_("Reputation cannot increase higher than this value. Zero or positive."),
|
||||
default=DEFAULT_REPUTATION_UPPER_LIMIT,
|
||||
validators=[MinValueValidator(0)],
|
||||
)
|
||||
footer_links = models.JSONField(
|
||||
help_text=_("The option configures the footer links on the flow executor pages."),
|
||||
default=list,
|
||||
blank=True,
|
||||
)
|
||||
gdpr_compliance = models.BooleanField(
|
||||
help_text=_(
|
||||
"When enabled, all the events caused by a user "
|
||||
"will be deleted upon the user's deletion."
|
||||
),
|
||||
default=True,
|
||||
)
|
||||
impersonation = models.BooleanField(
|
||||
help_text=_("Globally enable/disable impersonation."), default=True
|
||||
)
|
||||
impersonation_require_reason = models.BooleanField(
|
||||
help_text=_("Require administrators to provide a reason for impersonating a user."),
|
||||
default=True,
|
||||
)
|
||||
default_token_duration = models.TextField(
|
||||
help_text=_("Default token duration"),
|
||||
default=DEFAULT_TOKEN_DURATION,
|
||||
validators=[timedelta_string_validator],
|
||||
)
|
||||
default_token_length = models.PositiveIntegerField(
|
||||
help_text=_("Default token length"),
|
||||
default=DEFAULT_TOKEN_LENGTH,
|
||||
validators=[MinValueValidator(1)],
|
||||
)
|
||||
|
||||
pagination_default_page_size = models.PositiveIntegerField(
|
||||
help_text=_("Default page size for API responses, if no size was requested."),
|
||||
default=20,
|
||||
)
|
||||
pagination_max_page_size = models.PositiveIntegerField(
|
||||
help_text=_("Maximum page size"),
|
||||
default=100,
|
||||
)
|
||||
|
||||
flags = models.JSONField(default=dict)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("System settings")
|
||||
verbose_name_plural = _("System settings")
|
||||
default_permissions = []
|
||||
|
||||
def __str__(self):
|
||||
return "System settings"
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.pk:
|
||||
raise IntegrityError("Only one instance of system settings is allowed")
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
@property
|
||||
def serializer(self) -> Serializer:
|
||||
from authentik.admin.api.settings import SettingsSerializer
|
||||
|
||||
return SettingsSerializer
|
||||
|
||||
|
||||
class VersionHistory(models.Model):
|
||||
|
||||
Reference in New Issue
Block a user