This repository has been archived on 2024-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
authentik/authentik/lib/models.py
Jens Langhammer 6ed7d842e4 *: allow URLs without domain and custom schemas
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 22:51:04 +01:00

78 lines
2.5 KiB
Python

"""Generic models"""
import re
from django.core.exceptions import ValidationError
from django.core.validators import URLValidator
from django.db import models
from django.utils.regex_helper import _lazy_re_compile
from model_utils.managers import InheritanceManager
from rest_framework.serializers import BaseSerializer
class SerializerModel(models.Model):
"""Base Abstract Model which has a serializer"""
@property
def serializer(self) -> BaseSerializer:
"""Get serializer for this model"""
raise NotImplementedError
class Meta:
abstract = True
class CreatedUpdatedModel(models.Model):
"""Base Abstract Model to save created and update"""
created = models.DateTimeField(auto_now_add=True)
last_updated = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class InheritanceAutoManager(InheritanceManager):
"""Object manager which automatically selects the subclass"""
def get_queryset(self):
return super().get_queryset().select_subclasses()
class InheritanceForwardManyToOneDescriptor(models.fields.related.ForwardManyToOneDescriptor):
"""Forward ManyToOne Descriptor that selects subclass. Requires InheritanceAutoManager."""
def get_queryset(self, **hints):
return self.field.remote_field.model.objects.db_manager(hints=hints).select_subclasses()
class InheritanceForeignKey(models.ForeignKey):
"""Custom ForeignKey that uses InheritanceForwardManyToOneDescriptor"""
forward_related_accessor_class = InheritanceForwardManyToOneDescriptor
class DomainlessURLValidator(URLValidator):
"""Subclass of URLValidator which doesn't check the domain
(to allow hostnames without domain)"""
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.host_re = "(" + self.hostname_re + self.domain_re + "|localhost)"
self.regex = _lazy_re_compile(
r"^(?:[a-z0-9.+-]*)://" # scheme is validated separately
r"(?:[^\s:@/]+(?::[^\s:@/]*)?@)?" # user:pass authentication
r"(?:" + self.ipv4_re + "|" + self.ipv6_re + "|" + self.host_re + ")"
r"(?::\d{2,5})?" # port
r"(?:[/?#][^\s]*)?" # resource path
r"\Z",
re.IGNORECASE,
)
self.schemes = ["http", "https", "blank"]
def __call__(self, value):
# Check if the scheme is valid.
scheme = value.split("://")[0].lower()
if scheme not in self.schemes:
value = "default" + value
return super().__call__(value)