start fixing tests
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
parent
1889e82309
commit
ff996f798f
|
@ -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 a new issue