core: add validator which allows for URLs with formatting (#4890)
This commit is contained in:
		@ -182,7 +182,9 @@ class Migration(migrations.Migration):
 | 
			
		||||
            model_name="application",
 | 
			
		||||
            name="meta_launch_url",
 | 
			
		||||
            field=models.TextField(
 | 
			
		||||
                blank=True, default="", validators=[authentik.lib.models.DomainlessURLValidator()]
 | 
			
		||||
                blank=True,
 | 
			
		||||
                default="",
 | 
			
		||||
                validators=[authentik.lib.models.DomainlessFormattedURLValidator()],
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.RunPython(
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,11 @@ from authentik.core.types import UILoginButton, UserSettingSerializer
 | 
			
		||||
from authentik.lib.avatars import get_avatar
 | 
			
		||||
from authentik.lib.config import CONFIG
 | 
			
		||||
from authentik.lib.generators import generate_id
 | 
			
		||||
from authentik.lib.models import CreatedUpdatedModel, DomainlessURLValidator, SerializerModel
 | 
			
		||||
from authentik.lib.models import (
 | 
			
		||||
    CreatedUpdatedModel,
 | 
			
		||||
    DomainlessFormattedURLValidator,
 | 
			
		||||
    SerializerModel,
 | 
			
		||||
)
 | 
			
		||||
from authentik.lib.utils.http import get_client_ip
 | 
			
		||||
from authentik.policies.models import PolicyBindingModel
 | 
			
		||||
 | 
			
		||||
@ -291,7 +295,7 @@ class Application(SerializerModel, PolicyBindingModel):
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    meta_launch_url = models.TextField(
 | 
			
		||||
        default="", blank=True, validators=[DomainlessURLValidator()]
 | 
			
		||||
        default="", blank=True, validators=[DomainlessFormattedURLValidator()]
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    open_in_new_tab = models.BooleanField(
 | 
			
		||||
 | 
			
		||||
@ -37,6 +37,22 @@ class TestApplicationsAPI(APITestCase):
 | 
			
		||||
            order=0,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_formatted_launch_url(self):
 | 
			
		||||
        """Test formatted launch URL"""
 | 
			
		||||
        self.client.force_login(self.user)
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            self.client.patch(
 | 
			
		||||
                reverse("authentik_api:application-detail", kwargs={"slug": self.allowed.slug}),
 | 
			
		||||
                {"meta_launch_url": "https://%(username)s.test.goauthentik.io/%(username)s"},
 | 
			
		||||
            ).status_code,
 | 
			
		||||
            200,
 | 
			
		||||
        )
 | 
			
		||||
        self.allowed.refresh_from_db()
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            self.allowed.get_launch_url(self.user),
 | 
			
		||||
            f"https://{self.user.username}.test.goauthentik.io/{self.user.username}",
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_set_icon(self):
 | 
			
		||||
        """Test set_icon"""
 | 
			
		||||
        file = ContentFile(b"text", "name")
 | 
			
		||||
 | 
			
		||||
@ -74,3 +74,21 @@ class DomainlessURLValidator(URLValidator):
 | 
			
		||||
        if scheme not in self.schemes:
 | 
			
		||||
            value = "default" + value
 | 
			
		||||
        super().__call__(value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DomainlessFormattedURLValidator(DomainlessURLValidator):
 | 
			
		||||
    """URL validator which allows for python format strings"""
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs) -> None:
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.host_re = r"([%\(\)a-zA-Z])+" + self.domain_re + self.domain_re
 | 
			
		||||
        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"] + list(self.schemes)
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user