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