start fixing tests
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
		@ -10,7 +10,6 @@ from django.db.models.functions import ExtractHour
 | 
			
		||||
from django.db.models.query import QuerySet
 | 
			
		||||
from django.db.transaction import atomic
 | 
			
		||||
from django.db.utils import IntegrityError
 | 
			
		||||
from django.urls import reverse_lazy
 | 
			
		||||
from django.utils.http import urlencode
 | 
			
		||||
from django.utils.text import slugify
 | 
			
		||||
from django.utils.timezone import now
 | 
			
		||||
@ -72,6 +71,8 @@ from authentik.flows.exceptions import FlowNonApplicableException
 | 
			
		||||
from authentik.flows.models import FlowToken
 | 
			
		||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlanner
 | 
			
		||||
from authentik.flows.views.executor import QS_KEY_TOKEN
 | 
			
		||||
from authentik.interfaces.models import InterfaceType
 | 
			
		||||
from authentik.interfaces.views import reverse_interface
 | 
			
		||||
from authentik.stages.email.models import EmailStage
 | 
			
		||||
from authentik.stages.email.tasks import send_mails
 | 
			
		||||
from authentik.stages.email.utils import TemplateEmailMessage
 | 
			
		||||
@ -350,8 +351,12 @@ class UserViewSet(UsedByMixin, ModelViewSet):
 | 
			
		||||
        )
 | 
			
		||||
        querystring = urlencode({QS_KEY_TOKEN: token.key})
 | 
			
		||||
        link = self.request.build_absolute_uri(
 | 
			
		||||
            reverse_lazy("authentik_core:if-flow", kwargs={"flow_slug": flow.slug})
 | 
			
		||||
            + f"?{querystring}"
 | 
			
		||||
            reverse_interface(
 | 
			
		||||
                self.request,
 | 
			
		||||
                InterfaceType.FLOW,
 | 
			
		||||
                flow_slug=flow.slug,
 | 
			
		||||
            ),
 | 
			
		||||
            +f"?{querystring}",
 | 
			
		||||
        )
 | 
			
		||||
        return link, token
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -60,5 +60,5 @@ class TestImpersonation(TestCase):
 | 
			
		||||
 | 
			
		||||
        response = self.client.get(reverse("authentik_core:impersonate-end"))
 | 
			
		||||
        self.assertRedirects(
 | 
			
		||||
            response, reverse("authentik_interfaces:if", kwargs={"if_name", "user"})
 | 
			
		||||
            response, reverse("authentik_interfaces:if", kwargs={"if_name": "user"})
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
@ -25,6 +25,8 @@ from authentik.flows.exceptions import FlowNonApplicableException
 | 
			
		||||
from authentik.flows.models import Flow
 | 
			
		||||
from authentik.flows.planner import CACHE_PREFIX, PLAN_CONTEXT_PENDING_USER, FlowPlanner, cache_key
 | 
			
		||||
from authentik.flows.views.executor import SESSION_KEY_HISTORY, SESSION_KEY_PLAN
 | 
			
		||||
from authentik.interfaces.models import InterfaceType
 | 
			
		||||
from authentik.interfaces.views import reverse_interface
 | 
			
		||||
from authentik.lib.utils.file import (
 | 
			
		||||
    FilePathSerializer,
 | 
			
		||||
    FileUploadSerializer,
 | 
			
		||||
@ -294,7 +296,10 @@ class FlowViewSet(UsedByMixin, ModelViewSet):
 | 
			
		||||
        return Response(
 | 
			
		||||
            {
 | 
			
		||||
                "link": request._request.build_absolute_uri(
 | 
			
		||||
                    reverse("authentik_core:if-flow", kwargs={"flow_slug": flow.slug})
 | 
			
		||||
                    reverse_interface(
 | 
			
		||||
                        InterfaceType.FLOW,
 | 
			
		||||
                        flow_slug=flow.slug,
 | 
			
		||||
                    ),
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,8 @@ from authentik.core.tests.utils import create_test_flow
 | 
			
		||||
from authentik.flows.models import Flow, FlowDesignation
 | 
			
		||||
from authentik.flows.planner import FlowPlan
 | 
			
		||||
from authentik.flows.views.executor import SESSION_KEY_APPLICATION_PRE, SESSION_KEY_PLAN
 | 
			
		||||
from authentik.interfaces.models import InterfaceType
 | 
			
		||||
from authentik.interfaces.tests import reverse_interface
 | 
			
		||||
from authentik.lib.generators import generate_id
 | 
			
		||||
from authentik.providers.oauth2.models import OAuth2Provider
 | 
			
		||||
 | 
			
		||||
@ -21,7 +23,10 @@ class TestHelperView(TestCase):
 | 
			
		||||
        response = self.client.get(
 | 
			
		||||
            reverse("authentik_flows:default-invalidation"),
 | 
			
		||||
        )
 | 
			
		||||
        expected_url = reverse("authentik_core:if-flow", kwargs={"flow_slug": flow.slug})
 | 
			
		||||
        expected_url = reverse_interface(
 | 
			
		||||
            InterfaceType.FLOW,
 | 
			
		||||
            flow_slug=flow.slug,
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
        self.assertEqual(response.url, expected_url)
 | 
			
		||||
 | 
			
		||||
@ -72,6 +77,9 @@ class TestHelperView(TestCase):
 | 
			
		||||
        response = self.client.get(
 | 
			
		||||
            reverse("authentik_flows:default-invalidation"),
 | 
			
		||||
        )
 | 
			
		||||
        expected_url = reverse("authentik_core:if-flow", kwargs={"flow_slug": flow.slug})
 | 
			
		||||
        expected_url = reverse_interface(
 | 
			
		||||
            InterfaceType.FLOW,
 | 
			
		||||
            flow_slug=flow.slug,
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(response.status_code, 302)
 | 
			
		||||
        self.assertEqual(response.url, expected_url)
 | 
			
		||||
 | 
			
		||||
@ -25,3 +25,4 @@ class InterfaceViewSet(UsedByMixin, ModelViewSet):
 | 
			
		||||
    queryset = Interface.objects.all()
 | 
			
		||||
    serializer_class = InterfaceSerializer
 | 
			
		||||
    filterset_fields = ["url_name", "type", "template"]
 | 
			
		||||
    search_fields = ["url_name", "type", "template"]
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										12
									
								
								authentik/interfaces/tests.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								authentik/interfaces/tests.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
"""Interface tests"""
 | 
			
		||||
from django.test import RequestFactory
 | 
			
		||||
 | 
			
		||||
from authentik.interfaces.models import InterfaceType
 | 
			
		||||
from authentik.interfaces.views import reverse_interface as full_reverse_interface
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def reverse_interface(interface_type: InterfaceType, **kwargs):
 | 
			
		||||
    """reverse_interface wrapper for tests"""
 | 
			
		||||
    factory = RequestFactory()
 | 
			
		||||
    request = factory.get("/")
 | 
			
		||||
    return full_reverse_interface(request, interface_type, **kwargs)
 | 
			
		||||
@ -1,11 +1,13 @@
 | 
			
		||||
"""Interface views"""
 | 
			
		||||
from json import dumps
 | 
			
		||||
from typing import Any, Optional
 | 
			
		||||
from urllib.parse import urlencode
 | 
			
		||||
 | 
			
		||||
from django.http import Http404, HttpRequest, HttpResponse
 | 
			
		||||
from django.shortcuts import get_object_or_404
 | 
			
		||||
from django.shortcuts import get_object_or_404, redirect
 | 
			
		||||
from django.template import Template, TemplateSyntaxError, engines
 | 
			
		||||
from django.template.response import TemplateResponse
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
from django.utils.decorators import method_decorator
 | 
			
		||||
from django.views import View
 | 
			
		||||
from django.views.decorators.cache import cache_page
 | 
			
		||||
@ -40,30 +42,37 @@ def redirect_to_default_interface(request: HttpRequest, interface_type: Interfac
 | 
			
		||||
    return RedirectToInterface.as_view(type=interface_type)(request, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def reverse_interface(request: HttpRequest, interface_type: InterfaceType, **kwargs):
 | 
			
		||||
    """Reverse URL to configured default interface"""
 | 
			
		||||
    tenant: Tenant = request.tenant
 | 
			
		||||
    interface: Interface = None
 | 
			
		||||
 | 
			
		||||
    if interface_type == InterfaceType.USER:
 | 
			
		||||
        interface = tenant.interface_user
 | 
			
		||||
    if interface_type == InterfaceType.ADMIN:
 | 
			
		||||
        interface = tenant.interface_admin
 | 
			
		||||
    if interface_type == InterfaceType.FLOW:
 | 
			
		||||
        interface = tenant.interface_flow
 | 
			
		||||
 | 
			
		||||
    if not interface:
 | 
			
		||||
        raise Http404()
 | 
			
		||||
    kwargs["if_name"] = interface.url_name
 | 
			
		||||
    return reverse(
 | 
			
		||||
        "authentik_interfaces:if",
 | 
			
		||||
        kwargs=kwargs,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RedirectToInterface(View):
 | 
			
		||||
    """Redirect to tenant's configured view for specified type"""
 | 
			
		||||
 | 
			
		||||
    type: Optional[InterfaceType] = None
 | 
			
		||||
 | 
			
		||||
    def dispatch(self, request: HttpRequest, **kwargs: Any) -> HttpResponse:
 | 
			
		||||
        tenant: Tenant = request.tenant
 | 
			
		||||
        interface: Interface = None
 | 
			
		||||
 | 
			
		||||
        if self.type == InterfaceType.USER:
 | 
			
		||||
            interface = tenant.interface_user
 | 
			
		||||
        if self.type == InterfaceType.ADMIN:
 | 
			
		||||
            interface = tenant.interface_admin
 | 
			
		||||
        if self.type == InterfaceType.FLOW:
 | 
			
		||||
            interface = tenant.interface_flow
 | 
			
		||||
 | 
			
		||||
        if not interface:
 | 
			
		||||
            raise Http404()
 | 
			
		||||
        return redirect_with_qs(
 | 
			
		||||
            "authentik_interfaces:if",
 | 
			
		||||
            self.request.GET,
 | 
			
		||||
            if_name=interface.url_name,
 | 
			
		||||
            **kwargs,
 | 
			
		||||
        )
 | 
			
		||||
        target = reverse_interface(request, self.type, **kwargs)
 | 
			
		||||
        if self.request.GET:
 | 
			
		||||
            target += "?" + urlencode(self.request.GET.items())
 | 
			
		||||
        return redirect(target)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@method_decorator(ensure_csrf_cookie, name="dispatch")
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,8 @@ from authentik.flows.models import FlowToken
 | 
			
		||||
from authentik.flows.planner import PLAN_CONTEXT_IS_RESTORED, PLAN_CONTEXT_PENDING_USER
 | 
			
		||||
from authentik.flows.stage import ChallengeStageView
 | 
			
		||||
from authentik.flows.views.executor import QS_KEY_TOKEN
 | 
			
		||||
from authentik.interfaces.models import InterfaceType
 | 
			
		||||
from authentik.interfaces.views import reverse_interface
 | 
			
		||||
from authentik.stages.email.models import EmailStage
 | 
			
		||||
from authentik.stages.email.tasks import send_mails
 | 
			
		||||
from authentik.stages.email.utils import TemplateEmailMessage
 | 
			
		||||
@ -47,9 +49,10 @@ class EmailStageView(ChallengeStageView):
 | 
			
		||||
 | 
			
		||||
    def get_full_url(self, **kwargs) -> str:
 | 
			
		||||
        """Get full URL to be used in template"""
 | 
			
		||||
        base_url = reverse(
 | 
			
		||||
            "authentik_core:if-flow",
 | 
			
		||||
            kwargs={"flow_slug": self.executor.flow.slug},
 | 
			
		||||
        base_url = reverse_interface(
 | 
			
		||||
            self.request,
 | 
			
		||||
            InterfaceType.FLOW,
 | 
			
		||||
            flow_slug=self.executor.flow.slug,
 | 
			
		||||
        )
 | 
			
		||||
        relative_url = f"{base_url}?{urlencode(kwargs)}"
 | 
			
		||||
        return self.request.build_absolute_uri(relative_url)
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,8 @@ from authentik.core.tests.utils import create_test_admin_user, create_test_flow
 | 
			
		||||
from authentik.flows.challenge import ChallengeTypes
 | 
			
		||||
from authentik.flows.models import FlowDesignation, FlowStageBinding
 | 
			
		||||
from authentik.flows.tests import FlowTestCase
 | 
			
		||||
from authentik.interfaces.models import InterfaceType
 | 
			
		||||
from authentik.interfaces.tests import reverse_interface
 | 
			
		||||
from authentik.sources.oauth.models import OAuthSource
 | 
			
		||||
from authentik.stages.identification.models import IdentificationStage, UserFields
 | 
			
		||||
from authentik.stages.password import BACKEND_INBUILT
 | 
			
		||||
@ -166,9 +168,9 @@ class TestIdentificationStage(FlowTestCase):
 | 
			
		||||
            component="ak-stage-identification",
 | 
			
		||||
            user_fields=["email"],
 | 
			
		||||
            password_fields=False,
 | 
			
		||||
            enroll_url=reverse(
 | 
			
		||||
                "authentik_core:if-flow",
 | 
			
		||||
                kwargs={"flow_slug": flow.slug},
 | 
			
		||||
            enroll_url=reverse_interface(
 | 
			
		||||
                InterfaceType.FLOW,
 | 
			
		||||
                flow_slug=flow.slug,
 | 
			
		||||
            ),
 | 
			
		||||
            show_source_labels=False,
 | 
			
		||||
            primary_action="Log in",
 | 
			
		||||
@ -204,9 +206,9 @@ class TestIdentificationStage(FlowTestCase):
 | 
			
		||||
            component="ak-stage-identification",
 | 
			
		||||
            user_fields=["email"],
 | 
			
		||||
            password_fields=False,
 | 
			
		||||
            recovery_url=reverse(
 | 
			
		||||
                "authentik_core:if-flow",
 | 
			
		||||
                kwargs={"flow_slug": flow.slug},
 | 
			
		||||
            recovery_url=reverse_interface(
 | 
			
		||||
                InterfaceType.FLOW,
 | 
			
		||||
                flow_slug=flow.slug,
 | 
			
		||||
            ),
 | 
			
		||||
            show_source_labels=False,
 | 
			
		||||
            primary_action="Log in",
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,6 @@ from django.contrib.auth import _clean_credentials
 | 
			
		||||
from django.contrib.auth.backends import BaseBackend
 | 
			
		||||
from django.core.exceptions import PermissionDenied
 | 
			
		||||
from django.http import HttpRequest, HttpResponse
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
from django.utils.translation import gettext as _
 | 
			
		||||
from rest_framework.exceptions import ErrorDetail, ValidationError
 | 
			
		||||
from rest_framework.fields import CharField
 | 
			
		||||
@ -23,6 +22,8 @@ from authentik.flows.challenge import (
 | 
			
		||||
from authentik.flows.models import Flow, FlowDesignation, Stage
 | 
			
		||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
 | 
			
		||||
from authentik.flows.stage import ChallengeStageView
 | 
			
		||||
from authentik.interfaces.models import InterfaceType
 | 
			
		||||
from authentik.interfaces.views import reverse_interface
 | 
			
		||||
from authentik.lib.utils.reflection import path_to_class
 | 
			
		||||
from authentik.stages.password.models import PasswordStage
 | 
			
		||||
 | 
			
		||||
@ -95,11 +96,12 @@ class PasswordStageView(ChallengeStageView):
 | 
			
		||||
                "type": ChallengeTypes.NATIVE.value,
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
        recovery_flow = Flow.objects.filter(designation=FlowDesignation.RECOVERY)
 | 
			
		||||
        if recovery_flow.exists():
 | 
			
		||||
            recover_url = reverse(
 | 
			
		||||
                "authentik_core:if-flow",
 | 
			
		||||
                kwargs={"flow_slug": recovery_flow.first().slug},
 | 
			
		||||
        recovery_flow = Flow.objects.filter(designation=FlowDesignation.RECOVERY).first()
 | 
			
		||||
        if recovery_flow:
 | 
			
		||||
            recover_url = reverse_interface(
 | 
			
		||||
                self.request,
 | 
			
		||||
                InterfaceType.FLOW,
 | 
			
		||||
                flow_slug=recovery_flow.slug,
 | 
			
		||||
            )
 | 
			
		||||
            challenge.initial_data["recovery_url"] = self.request.build_absolute_uri(recover_url)
 | 
			
		||||
        return challenge
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user