*: allow URLs without domain and custom schemas

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2021-11-23 22:51:04 +01:00
parent 8794c840cf
commit 6ed7d842e4
9 changed files with 29 additions and 15 deletions

View File

@ -3,7 +3,6 @@
import uuid import uuid
from os import environ from os import environ
import django.core.validators
import django.db.models.deletion import django.db.models.deletion
from django.apps.registry import Apps from django.apps.registry import Apps
from django.conf import settings from django.conf import settings
@ -12,6 +11,7 @@ from django.db.backends.base.schema import BaseDatabaseSchemaEditor
from django.db.models import Count from django.db.models import Count
import authentik.core.models import authentik.core.models
import authentik.lib.models
def migrate_sessions(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): def migrate_sessions(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
@ -161,7 +161,7 @@ class Migration(migrations.Migration):
model_name="application", model_name="application",
name="meta_launch_url", name="meta_launch_url",
field=models.TextField( field=models.TextField(
blank=True, default="", validators=[django.core.validators.URLValidator()] blank=True, default="", validators=[authentik.lib.models.DomainlessURLValidator()]
), ),
), ),
migrations.RunPython( migrations.RunPython(

View File

@ -1,8 +1,9 @@
# Generated by Django 3.2.3 on 2021-06-02 21:51 # Generated by Django 3.2.3 on 2021-06-02 21:51
import django.core.validators
from django.db import migrations, models from django.db import migrations, models
import authentik.lib.models
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -17,7 +18,7 @@ class Migration(migrations.Migration):
field=models.TextField( field=models.TextField(
blank=True, blank=True,
default="", default="",
validators=[django.core.validators.URLValidator()], validators=[authentik.lib.models.DomainlessURLValidator()],
), ),
), ),
] ]

View File

@ -9,7 +9,6 @@ from deepmerge import always_merger
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import AbstractUser
from django.contrib.auth.models import UserManager as DjangoUserManager from django.contrib.auth.models import UserManager as DjangoUserManager
from django.core import validators
from django.db import models from django.db import models
from django.db.models import Q, QuerySet, options from django.db.models import Q, QuerySet, options
from django.http import HttpRequest from django.http import HttpRequest
@ -29,7 +28,7 @@ from authentik.core.types import UILoginButton, UserSettingSerializer
from authentik.flows.models import Flow from authentik.flows.models import Flow
from authentik.lib.config import CONFIG from authentik.lib.config import CONFIG
from authentik.lib.generators import generate_id from authentik.lib.generators import generate_id
from authentik.lib.models import CreatedUpdatedModel, SerializerModel from authentik.lib.models import CreatedUpdatedModel, DomainlessURLValidator, SerializerModel
from authentik.lib.utils.http import get_client_ip from authentik.lib.utils.http import get_client_ip
from authentik.managed.models import ManagedModel from authentik.managed.models import ManagedModel
from authentik.policies.models import PolicyBindingModel from authentik.policies.models import PolicyBindingModel
@ -246,7 +245,7 @@ class Application(PolicyBindingModel):
) )
meta_launch_url = models.TextField( meta_launch_url = models.TextField(
default="", blank=True, validators=[validators.URLValidator()] default="", blank=True, validators=[DomainlessURLValidator()]
) )
# For template applications, this can be set to /static/authentik/applications/* # For template applications, this can be set to /static/authentik/applications/*
meta_icon = models.FileField( meta_icon = models.FileField(

View File

@ -22,7 +22,7 @@ def create_test_flow(designation: FlowDesignation = FlowDesignation.STAGE_CONFIG
) )
def create_test_admin_user(name: Optional[str] = None, set_password=False) -> User: def create_test_admin_user(name: Optional[str] = None) -> User:
"""Generate a test-admin user""" """Generate a test-admin user"""
uid = generate_id(20) if not name else name uid = generate_id(20) if not name else name
group = Group.objects.create(name=uid, is_superuser=True) group = Group.objects.create(name=uid, is_superuser=True)

View File

@ -4,7 +4,6 @@ import uuid
from datetime import timedelta from datetime import timedelta
from typing import Iterable from typing import Iterable
import django.core.validators
import django.db.models.deletion import django.db.models.deletion
from django.apps.registry import Apps from django.apps.registry import Apps
from django.conf import settings from django.conf import settings
@ -12,6 +11,7 @@ from django.db import migrations, models
from django.db.backends.base.schema import BaseDatabaseSchemaEditor from django.db.backends.base.schema import BaseDatabaseSchemaEditor
import authentik.events.models import authentik.events.models
import authentik.lib.models
from authentik.events.models import EventAction, NotificationSeverity, TransportMode from authentik.events.models import EventAction, NotificationSeverity, TransportMode
@ -826,6 +826,8 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name="notificationtransport", model_name="notificationtransport",
name="webhook_url", name="webhook_url",
field=models.TextField(blank=True, validators=[django.core.validators.URLValidator()]), field=models.TextField(
blank=True, validators=[authentik.lib.models.DomainlessURLValidator()]
),
), ),
] ]

View File

@ -1,8 +1,9 @@
# Generated by Django 3.2.7 on 2021-10-04 15:31 # Generated by Django 3.2.7 on 2021-10-04 15:31
import django.core.validators
from django.db import migrations, models from django.db import migrations, models
import authentik.lib.models
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -14,6 +15,8 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name="notificationtransport", model_name="notificationtransport",
name="webhook_url", name="webhook_url",
field=models.TextField(blank=True, validators=[django.core.validators.URLValidator()]), field=models.TextField(
blank=True, validators=[authentik.lib.models.DomainlessURLValidator()]
),
), ),
] ]

View File

@ -6,7 +6,6 @@ from typing import TYPE_CHECKING, Optional, Type, Union
from uuid import uuid4 from uuid import uuid4
from django.conf import settings from django.conf import settings
from django.core.validators import URLValidator
from django.db import models from django.db import models
from django.http import HttpRequest from django.http import HttpRequest
from django.http.request import QueryDict from django.http.request import QueryDict
@ -20,6 +19,7 @@ from authentik.core.middleware import SESSION_IMPERSONATE_ORIGINAL_USER, SESSION
from authentik.core.models import ExpiringModel, Group, PropertyMapping, User from authentik.core.models import ExpiringModel, Group, PropertyMapping, User
from authentik.events.geo import GEOIP_READER from authentik.events.geo import GEOIP_READER
from authentik.events.utils import cleanse_dict, get_user, model_to_dict, sanitize_dict from authentik.events.utils import cleanse_dict, get_user, model_to_dict, sanitize_dict
from authentik.lib.models import DomainlessURLValidator
from authentik.lib.sentry import SentryIgnoredException from authentik.lib.sentry import SentryIgnoredException
from authentik.lib.utils.http import get_client_ip, get_http_session from authentik.lib.utils.http import get_client_ip, get_http_session
from authentik.lib.utils.time import timedelta_from_string from authentik.lib.utils.time import timedelta_from_string
@ -224,7 +224,7 @@ class NotificationTransport(models.Model):
name = models.TextField(unique=True) name = models.TextField(unique=True)
mode = models.TextField(choices=TransportMode.choices) mode = models.TextField(choices=TransportMode.choices)
webhook_url = models.TextField(blank=True, validators=[URLValidator()]) webhook_url = models.TextField(blank=True, validators=[DomainlessURLValidator()])
webhook_mapping = models.ForeignKey( webhook_mapping = models.ForeignKey(
"NotificationWebhookMapping", on_delete=models.SET_DEFAULT, null=True, default=None "NotificationWebhookMapping", on_delete=models.SET_DEFAULT, null=True, default=None
) )

View File

@ -1,6 +1,7 @@
"""Generic models""" """Generic models"""
import re import re
from django.core.exceptions import ValidationError
from django.core.validators import URLValidator from django.core.validators import URLValidator
from django.db import models from django.db import models
from django.utils.regex_helper import _lazy_re_compile from django.utils.regex_helper import _lazy_re_compile
@ -66,3 +67,11 @@ class DomainlessURLValidator(URLValidator):
r"\Z", r"\Z",
re.IGNORECASE, 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)

View File

@ -61,7 +61,7 @@ class SeleniumTestCase(ChannelsLiveServerTestCase):
self.driver.implicitly_wait(30) self.driver.implicitly_wait(30)
self.wait = WebDriverWait(self.driver, self.wait_timeout) self.wait = WebDriverWait(self.driver, self.wait_timeout)
self.logger = get_logger() self.logger = get_logger()
self.user = create_test_admin_user(set_password=True) self.user = create_test_admin_user()
if specs := self.get_container_specs(): if specs := self.get_container_specs():
self.container = self._start_container(specs) self.container = self._start_container(specs)