flows: add test helpers to simplify and improve checking of stages, remove force_str
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
50e3d317b2
commit
90c31c2214
|
@ -1,6 +1,5 @@
|
||||||
"""Test Applications API"""
|
"""Test Applications API"""
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_str
|
|
||||||
from rest_framework.test import APITestCase
|
from rest_framework.test import APITestCase
|
||||||
|
|
||||||
from authentik.core.models import Application
|
from authentik.core.models import Application
|
||||||
|
@ -32,7 +31,7 @@ class TestApplicationsAPI(APITestCase):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(force_str(response.content), {"messages": [], "passing": True})
|
self.assertJSONEqual(response.content.decode(), {"messages": [], "passing": True})
|
||||||
response = self.client.get(
|
response = self.client.get(
|
||||||
reverse(
|
reverse(
|
||||||
"authentik_api:application-check-access",
|
"authentik_api:application-check-access",
|
||||||
|
@ -40,14 +39,14 @@ class TestApplicationsAPI(APITestCase):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(force_str(response.content), {"messages": ["dummy"], "passing": False})
|
self.assertJSONEqual(response.content.decode(), {"messages": ["dummy"], "passing": False})
|
||||||
|
|
||||||
def test_list(self):
|
def test_list(self):
|
||||||
"""Test list operation without superuser_full_list"""
|
"""Test list operation without superuser_full_list"""
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
response = self.client.get(reverse("authentik_api:application-list"))
|
response = self.client.get(reverse("authentik_api:application-list"))
|
||||||
self.assertJSONEqual(
|
self.assertJSONEqual(
|
||||||
force_str(response.content),
|
response.content.decode(),
|
||||||
{
|
{
|
||||||
"pagination": {
|
"pagination": {
|
||||||
"next": 0,
|
"next": 0,
|
||||||
|
@ -83,7 +82,7 @@ class TestApplicationsAPI(APITestCase):
|
||||||
reverse("authentik_api:application-list") + "?superuser_full_list=true"
|
reverse("authentik_api:application-list") + "?superuser_full_list=true"
|
||||||
)
|
)
|
||||||
self.assertJSONEqual(
|
self.assertJSONEqual(
|
||||||
force_str(response.content),
|
response.content.decode(),
|
||||||
{
|
{
|
||||||
"pagination": {
|
"pagination": {
|
||||||
"next": 0,
|
"next": 0,
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
from json import loads
|
from json import loads
|
||||||
|
|
||||||
from django.urls.base import reverse
|
from django.urls.base import reverse
|
||||||
from django.utils.encoding import force_str
|
|
||||||
from rest_framework.test import APITestCase
|
from rest_framework.test import APITestCase
|
||||||
|
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
|
@ -28,5 +27,5 @@ class TestAuthenticatedSessionsAPI(APITestCase):
|
||||||
self.client.force_login(self.other_user)
|
self.client.force_login(self.other_user)
|
||||||
response = self.client.get(reverse("authentik_api:authenticatedsession-list"))
|
response = self.client.get(reverse("authentik_api:authenticatedsession-list"))
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
body = loads(force_str(response.content))
|
body = loads(response.content.decode())
|
||||||
self.assertEqual(body["pagination"]["count"], 1)
|
self.assertEqual(body["pagination"]["count"], 1)
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
"""Test helpers"""
|
||||||
|
from json import loads
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
from django.http.response import HttpResponse
|
||||||
|
from django.urls.base import reverse
|
||||||
|
from rest_framework.test import APITestCase
|
||||||
|
|
||||||
|
from authentik.core.models import User
|
||||||
|
from authentik.flows.challenge import ChallengeTypes
|
||||||
|
from authentik.flows.models import Flow
|
||||||
|
|
||||||
|
|
||||||
|
class FlowTestCase(APITestCase):
|
||||||
|
"""Helpers for testing flows and stages."""
|
||||||
|
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
def assertStageResponse(
|
||||||
|
self,
|
||||||
|
response: HttpResponse,
|
||||||
|
flow: Optional[Flow] = None,
|
||||||
|
user: Optional[User] = None,
|
||||||
|
**kwargs,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
"""Assert various attributes of a stage response"""
|
||||||
|
raw_response = loads(response.content.decode())
|
||||||
|
self.assertIsNotNone(raw_response["component"])
|
||||||
|
self.assertIsNotNone(raw_response["type"])
|
||||||
|
if flow:
|
||||||
|
self.assertIn("flow_info", raw_response)
|
||||||
|
self.assertEqual(raw_response["flow_info"]["background"], flow.background_url)
|
||||||
|
self.assertEqual(
|
||||||
|
raw_response["flow_info"]["cancel_url"], reverse("authentik_flows:cancel")
|
||||||
|
)
|
||||||
|
# We don't check the flow title since it will most likely go
|
||||||
|
# through ChallengeStageView.format_title() so might not match 1:1
|
||||||
|
# self.assertEqual(raw_response["flow_info"]["title"], flow.title)
|
||||||
|
self.assertIsNotNone(raw_response["flow_info"]["title"])
|
||||||
|
if user:
|
||||||
|
self.assertEqual(raw_response["pending_user"], user.username)
|
||||||
|
self.assertEqual(raw_response["pending_user_avatar"], user.avatar)
|
||||||
|
for key, expected in kwargs.items():
|
||||||
|
self.assertEqual(raw_response[key], expected)
|
||||||
|
return raw_response
|
||||||
|
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
def assertStageRedirects(self, response: HttpResponse, to: str) -> dict[str, Any]:
|
||||||
|
"""Wrapper around assertStageResponse that checks for a redirect"""
|
||||||
|
return self.assertStageResponse(
|
||||||
|
response, component="xak-flow-redirect", to=to, type=ChallengeTypes.REDIRECT.value
|
||||||
|
)
|
|
@ -4,16 +4,14 @@ from unittest.mock import MagicMock, PropertyMock, patch
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.test.client import RequestFactory
|
from django.test.client import RequestFactory
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_str
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.flows.challenge import ChallengeTypes
|
|
||||||
from authentik.flows.exceptions import FlowNonApplicableException
|
from authentik.flows.exceptions import FlowNonApplicableException
|
||||||
from authentik.flows.markers import ReevaluateMarker, StageMarker
|
from authentik.flows.markers import ReevaluateMarker, StageMarker
|
||||||
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding, InvalidResponseAction
|
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding, InvalidResponseAction
|
||||||
from authentik.flows.planner import FlowPlan, FlowPlanner
|
from authentik.flows.planner import FlowPlan, FlowPlanner
|
||||||
from authentik.flows.stage import PLAN_CONTEXT_PENDING_USER_IDENTIFIER, StageView
|
from authentik.flows.stage import PLAN_CONTEXT_PENDING_USER_IDENTIFIER, StageView
|
||||||
|
from authentik.flows.tests import FlowTestCase
|
||||||
from authentik.flows.views.executor import NEXT_ARG_NAME, SESSION_KEY_PLAN, FlowExecutorView
|
from authentik.flows.views.executor import NEXT_ARG_NAME, SESSION_KEY_PLAN, FlowExecutorView
|
||||||
from authentik.lib.config import CONFIG
|
from authentik.lib.config import CONFIG
|
||||||
from authentik.policies.dummy.models import DummyPolicy
|
from authentik.policies.dummy.models import DummyPolicy
|
||||||
|
@ -37,7 +35,7 @@ def to_stage_response(request: HttpRequest, source: HttpResponse):
|
||||||
TO_STAGE_RESPONSE_MOCK = MagicMock(side_effect=to_stage_response)
|
TO_STAGE_RESPONSE_MOCK = MagicMock(side_effect=to_stage_response)
|
||||||
|
|
||||||
|
|
||||||
class TestFlowExecutor(APITestCase):
|
class TestFlowExecutor(FlowTestCase):
|
||||||
"""Test executor"""
|
"""Test executor"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -90,18 +88,11 @@ class TestFlowExecutor(APITestCase):
|
||||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageResponse(
|
||||||
force_str(response.content),
|
response,
|
||||||
{
|
flow=flow,
|
||||||
"component": "ak-stage-access-denied",
|
error_message=FlowNonApplicableException.__doc__,
|
||||||
"error_message": FlowNonApplicableException.__doc__,
|
component="ak-stage-access-denied",
|
||||||
"flow_info": {
|
|
||||||
"background": flow.background_url,
|
|
||||||
"cancel_url": reverse("authentik_flows:cancel"),
|
|
||||||
"title": "",
|
|
||||||
},
|
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@patch(
|
@patch(
|
||||||
|
@ -283,14 +274,7 @@ class TestFlowExecutor(APITestCase):
|
||||||
# We do this request without the patch, so the policy results in false
|
# We do this request without the patch, so the policy results in false
|
||||||
response = self.client.post(exec_url)
|
response = self.client.post(exec_url)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "xak-flow-redirect",
|
|
||||||
"to": reverse("authentik_core:root-redirect"),
|
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_reevaluate_keep(self):
|
def test_reevaluate_keep(self):
|
||||||
"""Test planner with re-evaluate (everything is kept)"""
|
"""Test planner with re-evaluate (everything is kept)"""
|
||||||
|
@ -360,14 +344,7 @@ class TestFlowExecutor(APITestCase):
|
||||||
# We do this request without the patch, so the policy results in false
|
# We do this request without the patch, so the policy results in false
|
||||||
response = self.client.post(exec_url)
|
response = self.client.post(exec_url)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "xak-flow-redirect",
|
|
||||||
"to": reverse("authentik_core:root-redirect"),
|
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_reevaluate_remove_consecutive(self):
|
def test_reevaluate_remove_consecutive(self):
|
||||||
"""Test planner with re-evaluate (consecutive stages are removed)"""
|
"""Test planner with re-evaluate (consecutive stages are removed)"""
|
||||||
|
@ -407,18 +384,7 @@ class TestFlowExecutor(APITestCase):
|
||||||
# First request, run the planner
|
# First request, run the planner
|
||||||
response = self.client.get(exec_url)
|
response = self.client.get(exec_url)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageResponse(response, flow, component="ak-stage-dummy")
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
|
||||||
"component": "ak-stage-dummy",
|
|
||||||
"flow_info": {
|
|
||||||
"background": flow.background_url,
|
|
||||||
"cancel_url": reverse("authentik_flows:cancel"),
|
|
||||||
"title": "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
plan: FlowPlan = self.client.session[SESSION_KEY_PLAN]
|
plan: FlowPlan = self.client.session[SESSION_KEY_PLAN]
|
||||||
|
|
||||||
|
@ -441,31 +407,13 @@ class TestFlowExecutor(APITestCase):
|
||||||
# but it won't save it, hence we can't check the plan
|
# but it won't save it, hence we can't check the plan
|
||||||
response = self.client.get(exec_url)
|
response = self.client.get(exec_url)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageResponse(response, flow, component="ak-stage-dummy")
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
|
||||||
"component": "ak-stage-dummy",
|
|
||||||
"flow_info": {
|
|
||||||
"background": flow.background_url,
|
|
||||||
"cancel_url": reverse("authentik_flows:cancel"),
|
|
||||||
"title": "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
# fourth request, this confirms the last stage (dummy4)
|
# fourth request, this confirms the last stage (dummy4)
|
||||||
# We do this request without the patch, so the policy results in false
|
# We do this request without the patch, so the policy results in false
|
||||||
response = self.client.post(exec_url)
|
response = self.client.post(exec_url)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "xak-flow-redirect",
|
|
||||||
"to": reverse("authentik_core:root-redirect"),
|
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_stageview_user_identifier(self):
|
def test_stageview_user_identifier(self):
|
||||||
"""Test PLAN_CONTEXT_PENDING_USER_IDENTIFIER"""
|
"""Test PLAN_CONTEXT_PENDING_USER_IDENTIFIER"""
|
||||||
|
@ -532,35 +480,16 @@ class TestFlowExecutor(APITestCase):
|
||||||
# First request, run the planner
|
# First request, run the planner
|
||||||
response = self.client.get(exec_url)
|
response = self.client.get(exec_url)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageResponse(
|
||||||
force_str(response.content),
|
response,
|
||||||
{
|
flow,
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
component="ak-stage-identification",
|
||||||
"component": "ak-stage-identification",
|
password_fields=False,
|
||||||
"flow_info": {
|
primary_action="Log in",
|
||||||
"background": flow.background_url,
|
sources=[],
|
||||||
"cancel_url": reverse("authentik_flows:cancel"),
|
show_source_labels=False,
|
||||||
"title": "",
|
user_fields=[UserFields.E_MAIL],
|
||||||
},
|
|
||||||
"password_fields": False,
|
|
||||||
"primary_action": "Log in",
|
|
||||||
"sources": [],
|
|
||||||
"show_source_labels": False,
|
|
||||||
"user_fields": [UserFields.E_MAIL],
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
response = self.client.post(exec_url, {"uid_field": "invalid-string"}, follow=True)
|
response = self.client.post(exec_url, {"uid_field": "invalid-string"}, follow=True)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageResponse(response, flow, component="ak-stage-access-denied")
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "ak-stage-access-denied",
|
|
||||||
"error_message": None,
|
|
||||||
"flow_info": {
|
|
||||||
"background": flow.background_url,
|
|
||||||
"cancel_url": reverse("authentik_flows:cancel"),
|
|
||||||
"title": "",
|
|
||||||
},
|
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
"""Password flow tests"""
|
"""Password flow tests"""
|
||||||
from django.urls.base import reverse
|
from django.urls.base import reverse
|
||||||
from django.utils.encoding import force_str
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.flows.challenge import ChallengeTypes
|
|
||||||
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
||||||
|
from authentik.flows.tests import FlowTestCase
|
||||||
from authentik.policies.password.models import PasswordPolicy
|
from authentik.policies.password.models import PasswordPolicy
|
||||||
from authentik.stages.prompt.models import FieldTypes, Prompt, PromptStage
|
from authentik.stages.prompt.models import FieldTypes, Prompt, PromptStage
|
||||||
|
|
||||||
|
|
||||||
class TestPasswordPolicyFlow(APITestCase):
|
class TestPasswordPolicyFlow(FlowTestCase):
|
||||||
"""Test Password Policy"""
|
"""Test Password Policy"""
|
||||||
|
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
|
@ -53,11 +51,11 @@ class TestPasswordPolicyFlow(APITestCase):
|
||||||
{"password": "akadmin"},
|
{"password": "akadmin"},
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageResponse(
|
||||||
force_str(response.content),
|
response,
|
||||||
{
|
self.flow,
|
||||||
"component": "ak-stage-prompt",
|
component="ak-stage-prompt",
|
||||||
"fields": [
|
fields=[
|
||||||
{
|
{
|
||||||
"field_key": "password",
|
"field_key": "password",
|
||||||
"label": "PASSWORD_LABEL",
|
"label": "PASSWORD_LABEL",
|
||||||
|
@ -68,14 +66,7 @@ class TestPasswordPolicyFlow(APITestCase):
|
||||||
"sub_text": "",
|
"sub_text": "",
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"flow_info": {
|
response_errors={
|
||||||
"background": self.flow.background_url,
|
|
||||||
"cancel_url": reverse("authentik_flows:cancel"),
|
|
||||||
"title": "",
|
|
||||||
},
|
|
||||||
"response_errors": {
|
|
||||||
"non_field_errors": [{"code": "invalid", "string": self.policy.error_message}]
|
"non_field_errors": [{"code": "invalid", "string": self.policy.error_message}]
|
||||||
},
|
},
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
"""Test authorize view"""
|
"""Test authorize view"""
|
||||||
from django.test import RequestFactory
|
from django.test import RequestFactory
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_str
|
|
||||||
|
|
||||||
from authentik.core.models import Application
|
from authentik.core.models import Application
|
||||||
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
|
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
|
||||||
|
@ -201,7 +200,7 @@ class TestAuthorize(OAuthTestCase):
|
||||||
)
|
)
|
||||||
code: AuthorizationCode = AuthorizationCode.objects.filter(user=user).first()
|
code: AuthorizationCode = AuthorizationCode.objects.filter(user=user).first()
|
||||||
self.assertJSONEqual(
|
self.assertJSONEqual(
|
||||||
force_str(response.content),
|
response.content.decode(),
|
||||||
{
|
{
|
||||||
"component": "xak-flow-redirect",
|
"component": "xak-flow-redirect",
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
"type": ChallengeTypes.REDIRECT.value,
|
||||||
|
@ -240,7 +239,7 @@ class TestAuthorize(OAuthTestCase):
|
||||||
)
|
)
|
||||||
token: RefreshToken = RefreshToken.objects.filter(user=user).first()
|
token: RefreshToken = RefreshToken.objects.filter(user=user).first()
|
||||||
self.assertJSONEqual(
|
self.assertJSONEqual(
|
||||||
force_str(response.content),
|
response.content.decode(),
|
||||||
{
|
{
|
||||||
"component": "xak-flow-redirect",
|
"component": "xak-flow-redirect",
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
"type": ChallengeTypes.REDIRECT.value,
|
||||||
|
|
|
@ -3,7 +3,6 @@ import json
|
||||||
|
|
||||||
from django.test import RequestFactory
|
from django.test import RequestFactory
|
||||||
from django.urls.base import reverse
|
from django.urls.base import reverse
|
||||||
from django.utils.encoding import force_str
|
|
||||||
|
|
||||||
from authentik.core.models import Application
|
from authentik.core.models import Application
|
||||||
from authentik.core.tests.utils import create_test_cert, create_test_flow
|
from authentik.core.tests.utils import create_test_cert, create_test_flow
|
||||||
|
@ -31,7 +30,7 @@ class TestJWKS(OAuthTestCase):
|
||||||
response = self.client.get(
|
response = self.client.get(
|
||||||
reverse("authentik_providers_oauth2:jwks", kwargs={"application_slug": app.slug})
|
reverse("authentik_providers_oauth2:jwks", kwargs={"application_slug": app.slug})
|
||||||
)
|
)
|
||||||
body = json.loads(force_str(response.content))
|
body = json.loads(response.content.decode())
|
||||||
self.assertEqual(len(body["keys"]), 1)
|
self.assertEqual(len(body["keys"]), 1)
|
||||||
|
|
||||||
def test_hs256(self):
|
def test_hs256(self):
|
||||||
|
@ -46,4 +45,4 @@ class TestJWKS(OAuthTestCase):
|
||||||
response = self.client.get(
|
response = self.client.get(
|
||||||
reverse("authentik_providers_oauth2:jwks", kwargs={"application_slug": app.slug})
|
reverse("authentik_providers_oauth2:jwks", kwargs={"application_slug": app.slug})
|
||||||
)
|
)
|
||||||
self.assertJSONEqual(force_str(response.content), {})
|
self.assertJSONEqual(response.content.decode(), {})
|
||||||
|
|
|
@ -3,7 +3,6 @@ from base64 import b64encode
|
||||||
|
|
||||||
from django.test import RequestFactory
|
from django.test import RequestFactory
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_str
|
|
||||||
|
|
||||||
from authentik.core.models import Application
|
from authentik.core.models import Application
|
||||||
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
|
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
|
||||||
|
@ -135,7 +134,7 @@ class TestToken(OAuthTestCase):
|
||||||
)
|
)
|
||||||
new_token: RefreshToken = RefreshToken.objects.filter(user=user).first()
|
new_token: RefreshToken = RefreshToken.objects.filter(user=user).first()
|
||||||
self.assertJSONEqual(
|
self.assertJSONEqual(
|
||||||
force_str(response.content),
|
response.content.decode(),
|
||||||
{
|
{
|
||||||
"access_token": new_token.access_token,
|
"access_token": new_token.access_token,
|
||||||
"refresh_token": new_token.refresh_token,
|
"refresh_token": new_token.refresh_token,
|
||||||
|
@ -184,7 +183,7 @@ class TestToken(OAuthTestCase):
|
||||||
self.assertEqual(response["Access-Control-Allow-Credentials"], "true")
|
self.assertEqual(response["Access-Control-Allow-Credentials"], "true")
|
||||||
self.assertEqual(response["Access-Control-Allow-Origin"], "http://local.invalid")
|
self.assertEqual(response["Access-Control-Allow-Origin"], "http://local.invalid")
|
||||||
self.assertJSONEqual(
|
self.assertJSONEqual(
|
||||||
force_str(response.content),
|
response.content.decode(),
|
||||||
{
|
{
|
||||||
"access_token": new_token.access_token,
|
"access_token": new_token.access_token,
|
||||||
"refresh_token": new_token.refresh_token,
|
"refresh_token": new_token.refresh_token,
|
||||||
|
@ -230,7 +229,7 @@ class TestToken(OAuthTestCase):
|
||||||
self.assertNotIn("Access-Control-Allow-Credentials", response)
|
self.assertNotIn("Access-Control-Allow-Credentials", response)
|
||||||
self.assertNotIn("Access-Control-Allow-Origin", response)
|
self.assertNotIn("Access-Control-Allow-Origin", response)
|
||||||
self.assertJSONEqual(
|
self.assertJSONEqual(
|
||||||
force_str(response.content),
|
response.content.decode(),
|
||||||
{
|
{
|
||||||
"access_token": new_token.access_token,
|
"access_token": new_token.access_token,
|
||||||
"refresh_token": new_token.refresh_token,
|
"refresh_token": new_token.refresh_token,
|
||||||
|
|
|
@ -3,7 +3,6 @@ import json
|
||||||
from dataclasses import asdict
|
from dataclasses import asdict
|
||||||
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_str
|
|
||||||
|
|
||||||
from authentik.core.models import Application
|
from authentik.core.models import Application
|
||||||
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
|
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
|
||||||
|
@ -54,7 +53,7 @@ class TestUserinfo(OAuthTestCase):
|
||||||
HTTP_AUTHORIZATION=f"Bearer {self.token.access_token}",
|
HTTP_AUTHORIZATION=f"Bearer {self.token.access_token}",
|
||||||
)
|
)
|
||||||
self.assertJSONEqual(
|
self.assertJSONEqual(
|
||||||
force_str(res.content),
|
res.content.decode(),
|
||||||
{
|
{
|
||||||
"name": self.user.name,
|
"name": self.user.name,
|
||||||
"given_name": self.user.name,
|
"given_name": self.user.name,
|
||||||
|
@ -77,7 +76,7 @@ class TestUserinfo(OAuthTestCase):
|
||||||
HTTP_AUTHORIZATION=f"Bearer {self.token.access_token}",
|
HTTP_AUTHORIZATION=f"Bearer {self.token.access_token}",
|
||||||
)
|
)
|
||||||
self.assertJSONEqual(
|
self.assertJSONEqual(
|
||||||
force_str(res.content),
|
res.content.decode(),
|
||||||
{
|
{
|
||||||
"name": self.user.name,
|
"name": self.user.name,
|
||||||
"given_name": self.user.name,
|
"given_name": self.user.name,
|
||||||
|
|
|
@ -3,15 +3,13 @@ from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from django.test.client import RequestFactory
|
from django.test.client import RequestFactory
|
||||||
from django.urls.base import reverse
|
from django.urls.base import reverse
|
||||||
from django.utils.encoding import force_str
|
|
||||||
from django_otp.plugins.otp_totp.models import TOTPDevice
|
from django_otp.plugins.otp_totp.models import TOTPDevice
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
from webauthn.helpers import bytes_to_base64url
|
from webauthn.helpers import bytes_to_base64url
|
||||||
|
|
||||||
from authentik.core.tests.utils import create_test_admin_user
|
from authentik.core.tests.utils import create_test_admin_user
|
||||||
from authentik.flows.challenge import ChallengeTypes
|
|
||||||
from authentik.flows.models import Flow, FlowStageBinding, NotConfiguredAction
|
from authentik.flows.models import Flow, FlowStageBinding, NotConfiguredAction
|
||||||
|
from authentik.flows.tests import FlowTestCase
|
||||||
from authentik.lib.generators import generate_id, generate_key
|
from authentik.lib.generators import generate_id, generate_key
|
||||||
from authentik.lib.tests.utils import get_request
|
from authentik.lib.tests.utils import get_request
|
||||||
from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice
|
from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice
|
||||||
|
@ -27,7 +25,7 @@ from authentik.stages.authenticator_webauthn.models import WebAuthnDevice
|
||||||
from authentik.stages.identification.models import IdentificationStage, UserFields
|
from authentik.stages.identification.models import IdentificationStage, UserFields
|
||||||
|
|
||||||
|
|
||||||
class AuthenticatorValidateStageTests(APITestCase):
|
class AuthenticatorValidateStageTests(FlowTestCase):
|
||||||
"""Test validator stage"""
|
"""Test validator stage"""
|
||||||
|
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
|
@ -61,22 +59,15 @@ class AuthenticatorValidateStageTests(APITestCase):
|
||||||
follow=True,
|
follow=True,
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageResponse(
|
||||||
force_str(response.content),
|
response,
|
||||||
{
|
flow,
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
component="ak-stage-identification",
|
||||||
"component": "ak-stage-identification",
|
password_fields=False,
|
||||||
"password_fields": False,
|
primary_action="Log in",
|
||||||
"primary_action": "Log in",
|
user_fields=["username"],
|
||||||
"flow_info": {
|
sources=[],
|
||||||
"background": flow.background_url,
|
show_source_labels=False,
|
||||||
"cancel_url": reverse("authentik_flows:cancel"),
|
|
||||||
"title": flow.title,
|
|
||||||
},
|
|
||||||
"user_fields": ["username"],
|
|
||||||
"sources": [],
|
|
||||||
"show_source_labels": False,
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_stage_validation(self):
|
def test_stage_validation(self):
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
"""captcha tests"""
|
"""captcha tests"""
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_str
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.flows.challenge import ChallengeTypes
|
|
||||||
from authentik.flows.markers import StageMarker
|
from authentik.flows.markers import StageMarker
|
||||||
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
||||||
from authentik.flows.planner import FlowPlan
|
from authentik.flows.planner import FlowPlan
|
||||||
|
from authentik.flows.tests import FlowTestCase
|
||||||
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
||||||
from authentik.stages.captcha.models import CaptchaStage
|
from authentik.stages.captcha.models import CaptchaStage
|
||||||
|
|
||||||
|
@ -16,7 +14,7 @@ RECAPTCHA_PUBLIC_KEY = "6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI"
|
||||||
RECAPTCHA_PRIVATE_KEY = "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe"
|
RECAPTCHA_PRIVATE_KEY = "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe"
|
||||||
|
|
||||||
|
|
||||||
class TestCaptchaStage(APITestCase):
|
class TestCaptchaStage(FlowTestCase):
|
||||||
"""Captcha tests"""
|
"""Captcha tests"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -46,11 +44,4 @@ class TestCaptchaStage(APITestCase):
|
||||||
{"token": "PASSED"},
|
{"token": "PASSED"},
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "xak-flow-redirect",
|
|
||||||
"to": reverse("authentik_core:root-redirect"),
|
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
|
@ -2,20 +2,18 @@
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_str
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from authentik.core.models import Application, User
|
from authentik.core.models import Application, User
|
||||||
from authentik.core.tasks import clean_expired_models
|
from authentik.core.tasks import clean_expired_models
|
||||||
from authentik.flows.challenge import ChallengeTypes
|
|
||||||
from authentik.flows.markers import StageMarker
|
from authentik.flows.markers import StageMarker
|
||||||
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
||||||
from authentik.flows.planner import PLAN_CONTEXT_APPLICATION, FlowPlan
|
from authentik.flows.planner import PLAN_CONTEXT_APPLICATION, FlowPlan
|
||||||
|
from authentik.flows.tests import FlowTestCase
|
||||||
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
||||||
from authentik.stages.consent.models import ConsentMode, ConsentStage, UserConsent
|
from authentik.stages.consent.models import ConsentMode, ConsentStage, UserConsent
|
||||||
|
|
||||||
|
|
||||||
class TestConsentStage(APITestCase):
|
class TestConsentStage(FlowTestCase):
|
||||||
"""Consent tests"""
|
"""Consent tests"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -46,15 +44,7 @@ class TestConsentStage(APITestCase):
|
||||||
)
|
)
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
# pylint: disable=no-member
|
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "xak-flow-redirect",
|
|
||||||
"to": reverse("authentik_core:root-redirect"),
|
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
self.assertFalse(UserConsent.objects.filter(user=self.user).exists())
|
self.assertFalse(UserConsent.objects.filter(user=self.user).exists())
|
||||||
|
|
||||||
def test_permanent(self):
|
def test_permanent(self):
|
||||||
|
@ -82,14 +72,7 @@ class TestConsentStage(APITestCase):
|
||||||
{},
|
{},
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "xak-flow-redirect",
|
|
||||||
"to": reverse("authentik_core:root-redirect"),
|
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
UserConsent.objects.filter(user=self.user, application=self.application).exists()
|
UserConsent.objects.filter(user=self.user, application=self.application).exists()
|
||||||
)
|
)
|
||||||
|
@ -121,14 +104,7 @@ class TestConsentStage(APITestCase):
|
||||||
{},
|
{},
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "xak-flow-redirect",
|
|
||||||
"to": reverse("authentik_core:root-redirect"),
|
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
UserConsent.objects.filter(user=self.user, application=self.application).exists()
|
UserConsent.objects.filter(user=self.user, application=self.application).exists()
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,18 +1,16 @@
|
||||||
"""deny tests"""
|
"""deny tests"""
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_str
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.flows.challenge import ChallengeTypes
|
|
||||||
from authentik.flows.markers import StageMarker
|
from authentik.flows.markers import StageMarker
|
||||||
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
||||||
from authentik.flows.planner import FlowPlan
|
from authentik.flows.planner import FlowPlan
|
||||||
|
from authentik.flows.tests import FlowTestCase
|
||||||
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
||||||
from authentik.stages.deny.models import DenyStage
|
from authentik.stages.deny.models import DenyStage
|
||||||
|
|
||||||
|
|
||||||
class TestUserDenyStage(APITestCase):
|
class TestUserDenyStage(FlowTestCase):
|
||||||
"""Deny tests"""
|
"""Deny tests"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -39,19 +37,7 @@ class TestUserDenyStage(APITestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageResponse(response, self.flow, component="ak-stage-access-denied")
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "ak-stage-access-denied",
|
|
||||||
"error_message": None,
|
|
||||||
"flow_info": {
|
|
||||||
"background": self.flow.background_url,
|
|
||||||
"cancel_url": reverse("authentik_flows:cancel"),
|
|
||||||
"title": "",
|
|
||||||
},
|
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_valid_post(self):
|
def test_valid_post(self):
|
||||||
"""Test with a valid pending user and backend"""
|
"""Test with a valid pending user and backend"""
|
||||||
|
@ -65,16 +51,4 @@ class TestUserDenyStage(APITestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageResponse(response, self.flow, component="ak-stage-access-denied")
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "ak-stage-access-denied",
|
|
||||||
"error_message": None,
|
|
||||||
"flow_info": {
|
|
||||||
"background": self.flow.background_url,
|
|
||||||
"cancel_url": reverse("authentik_flows:cancel"),
|
|
||||||
"title": "",
|
|
||||||
},
|
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
"""dummy tests"""
|
"""dummy tests"""
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_str
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.flows.challenge import ChallengeTypes
|
|
||||||
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
||||||
|
from authentik.flows.tests import FlowTestCase
|
||||||
from authentik.stages.dummy.models import DummyStage
|
from authentik.stages.dummy.models import DummyStage
|
||||||
|
|
||||||
|
|
||||||
class TestDummyStage(APITestCase):
|
class TestDummyStage(FlowTestCase):
|
||||||
"""Dummy tests"""
|
"""Dummy tests"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -42,11 +40,4 @@ class TestDummyStage(APITestCase):
|
||||||
url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
||||||
response = self.client.post(url, {})
|
response = self.client.post(url, {})
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "xak-flow-redirect",
|
|
||||||
"to": reverse("authentik_core:root-redirect"),
|
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
|
@ -5,21 +5,19 @@ from django.core import mail
|
||||||
from django.core.mail.backends.locmem import EmailBackend
|
from django.core.mail.backends.locmem import EmailBackend
|
||||||
from django.core.mail.backends.smtp import EmailBackend as SMTPEmailBackend
|
from django.core.mail.backends.smtp import EmailBackend as SMTPEmailBackend
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_str
|
|
||||||
from django.utils.http import urlencode
|
from django.utils.http import urlencode
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from authentik.core.models import Token, User
|
from authentik.core.models import Token, User
|
||||||
from authentik.flows.challenge import ChallengeTypes
|
|
||||||
from authentik.flows.markers import StageMarker
|
from authentik.flows.markers import StageMarker
|
||||||
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
||||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
|
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
|
||||||
|
from authentik.flows.tests import FlowTestCase
|
||||||
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
||||||
from authentik.stages.email.models import EmailStage
|
from authentik.stages.email.models import EmailStage
|
||||||
from authentik.stages.email.stage import QS_KEY_TOKEN
|
from authentik.stages.email.stage import QS_KEY_TOKEN
|
||||||
|
|
||||||
|
|
||||||
class TestEmailStage(APITestCase):
|
class TestEmailStage(FlowTestCase):
|
||||||
"""Email tests"""
|
"""Email tests"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -123,14 +121,7 @@ class TestEmailStage(APITestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "xak-flow-redirect",
|
|
||||||
"to": reverse("authentik_core:root-redirect"),
|
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
plan: FlowPlan = session[SESSION_KEY_PLAN]
|
plan: FlowPlan = session[SESSION_KEY_PLAN]
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
"""identification tests"""
|
"""identification tests"""
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_str
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.flows.challenge import ChallengeTypes
|
from authentik.flows.challenge import ChallengeTypes
|
||||||
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
||||||
|
from authentik.flows.tests import FlowTestCase
|
||||||
from authentik.lib.generators import generate_key
|
from authentik.lib.generators import generate_key
|
||||||
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
|
||||||
|
@ -13,7 +12,7 @@ from authentik.stages.password import BACKEND_INBUILT
|
||||||
from authentik.stages.password.models import PasswordStage
|
from authentik.stages.password.models import PasswordStage
|
||||||
|
|
||||||
|
|
||||||
class TestIdentificationStage(APITestCase):
|
class TestIdentificationStage(FlowTestCase):
|
||||||
"""Identification tests"""
|
"""Identification tests"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -56,14 +55,7 @@ class TestIdentificationStage(APITestCase):
|
||||||
url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
||||||
response = self.client.post(url, form_data)
|
response = self.client.post(url, form_data)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "xak-flow-redirect",
|
|
||||||
"to": reverse("authentik_core:root-redirect"),
|
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_valid_with_password(self):
|
def test_valid_with_password(self):
|
||||||
"""Test with valid email and password in single step"""
|
"""Test with valid email and password in single step"""
|
||||||
|
@ -74,14 +66,7 @@ class TestIdentificationStage(APITestCase):
|
||||||
url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
||||||
response = self.client.post(url, form_data)
|
response = self.client.post(url, form_data)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "xak-flow-redirect",
|
|
||||||
"to": reverse("authentik_core:root-redirect"),
|
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_invalid_with_password(self):
|
def test_invalid_with_password(self):
|
||||||
"""Test with valid email and invalid password in single step"""
|
"""Test with valid email and invalid password in single step"""
|
||||||
|
@ -95,24 +80,16 @@ class TestIdentificationStage(APITestCase):
|
||||||
url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
||||||
response = self.client.post(url, form_data)
|
response = self.client.post(url, form_data)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageResponse(
|
||||||
force_str(response.content),
|
response,
|
||||||
{
|
self.flow,
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
component="ak-stage-identification",
|
||||||
"component": "ak-stage-identification",
|
password_fields=True,
|
||||||
"password_fields": True,
|
primary_action="Log in",
|
||||||
"primary_action": "Log in",
|
response_errors={
|
||||||
"response_errors": {
|
"non_field_errors": [{"code": "invalid", "string": "Failed to " "authenticate."}]
|
||||||
"non_field_errors": [
|
|
||||||
{"code": "invalid", "string": "Failed to " "authenticate."}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"flow_info": {
|
sources=[
|
||||||
"background": self.flow.background_url,
|
|
||||||
"cancel_url": reverse("authentik_flows:cancel"),
|
|
||||||
"title": "",
|
|
||||||
},
|
|
||||||
"sources": [
|
|
||||||
{
|
{
|
||||||
"challenge": {
|
"challenge": {
|
||||||
"component": "xak-flow-redirect",
|
"component": "xak-flow-redirect",
|
||||||
|
@ -123,9 +100,8 @@ class TestIdentificationStage(APITestCase):
|
||||||
"name": "test",
|
"name": "test",
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"show_source_labels": False,
|
show_source_labels=False,
|
||||||
"user_fields": ["email"],
|
user_fields=["email"],
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_invalid_with_username(self):
|
def test_invalid_with_username(self):
|
||||||
|
@ -147,25 +123,17 @@ class TestIdentificationStage(APITestCase):
|
||||||
form_data,
|
form_data,
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageResponse(
|
||||||
force_str(response.content),
|
response,
|
||||||
{
|
self.flow,
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
component="ak-stage-identification",
|
||||||
"component": "ak-stage-identification",
|
password_fields=False,
|
||||||
"password_fields": False,
|
primary_action="Log in",
|
||||||
"primary_action": "Log in",
|
response_errors={
|
||||||
"response_errors": {
|
"non_field_errors": [{"code": "invalid", "string": "Failed to " "authenticate."}]
|
||||||
"non_field_errors": [
|
|
||||||
{"code": "invalid", "string": "Failed to " "authenticate."}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"show_source_labels": False,
|
show_source_labels=False,
|
||||||
"flow_info": {
|
sources=[
|
||||||
"background": self.flow.background_url,
|
|
||||||
"cancel_url": reverse("authentik_flows:cancel"),
|
|
||||||
"title": "",
|
|
||||||
},
|
|
||||||
"sources": [
|
|
||||||
{
|
{
|
||||||
"challenge": {
|
"challenge": {
|
||||||
"component": "xak-flow-redirect",
|
"component": "xak-flow-redirect",
|
||||||
|
@ -176,8 +144,7 @@ class TestIdentificationStage(APITestCase):
|
||||||
"name": "test",
|
"name": "test",
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"user_fields": [],
|
user_fields=[],
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_invalid_with_invalid_email(self):
|
def test_invalid_with_invalid_email(self):
|
||||||
|
@ -209,25 +176,19 @@ class TestIdentificationStage(APITestCase):
|
||||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageResponse(
|
||||||
force_str(response.content),
|
response,
|
||||||
{
|
self.flow,
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
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(
|
|
||||||
"authentik_core:if-flow",
|
"authentik_core:if-flow",
|
||||||
kwargs={"flow_slug": "unique-enrollment-string"},
|
kwargs={"flow_slug": "unique-enrollment-string"},
|
||||||
),
|
),
|
||||||
"show_source_labels": False,
|
show_source_labels=False,
|
||||||
"primary_action": "Log in",
|
primary_action="Log in",
|
||||||
"flow_info": {
|
sources=[
|
||||||
"background": flow.background_url,
|
|
||||||
"cancel_url": reverse("authentik_flows:cancel"),
|
|
||||||
"title": self.flow.title,
|
|
||||||
},
|
|
||||||
"sources": [
|
|
||||||
{
|
{
|
||||||
"icon_url": "/static/authentik/sources/default.svg",
|
"icon_url": "/static/authentik/sources/default.svg",
|
||||||
"name": "test",
|
"name": "test",
|
||||||
|
@ -238,7 +199,6 @@ class TestIdentificationStage(APITestCase):
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_recovery_flow(self):
|
def test_recovery_flow(self):
|
||||||
|
@ -259,25 +219,19 @@ class TestIdentificationStage(APITestCase):
|
||||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageResponse(
|
||||||
force_str(response.content),
|
response,
|
||||||
{
|
self.flow,
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
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(
|
|
||||||
"authentik_core:if-flow",
|
"authentik_core:if-flow",
|
||||||
kwargs={"flow_slug": "unique-recovery-string"},
|
kwargs={"flow_slug": "unique-recovery-string"},
|
||||||
),
|
),
|
||||||
"show_source_labels": False,
|
show_source_labels=False,
|
||||||
"primary_action": "Log in",
|
primary_action="Log in",
|
||||||
"flow_info": {
|
sources=[
|
||||||
"background": flow.background_url,
|
|
||||||
"cancel_url": reverse("authentik_flows:cancel"),
|
|
||||||
"title": self.flow.title,
|
|
||||||
},
|
|
||||||
"sources": [
|
|
||||||
{
|
{
|
||||||
"challenge": {
|
"challenge": {
|
||||||
"component": "xak-flow-redirect",
|
"component": "xak-flow-redirect",
|
||||||
|
@ -288,5 +242,4 @@ class TestIdentificationStage(APITestCase):
|
||||||
"name": "test",
|
"name": "test",
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,17 +2,16 @@
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_str
|
|
||||||
from django.utils.http import urlencode
|
from django.utils.http import urlencode
|
||||||
from guardian.shortcuts import get_anonymous_user
|
from guardian.shortcuts import get_anonymous_user
|
||||||
from rest_framework.test import APITestCase
|
from rest_framework.test import APITestCase
|
||||||
|
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.core.tests.utils import create_test_admin_user
|
from authentik.core.tests.utils import create_test_admin_user
|
||||||
from authentik.flows.challenge import ChallengeTypes
|
|
||||||
from authentik.flows.markers import StageMarker
|
from authentik.flows.markers import StageMarker
|
||||||
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
||||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
|
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
|
||||||
|
from authentik.flows.tests import FlowTestCase
|
||||||
from authentik.flows.tests.test_executor import TO_STAGE_RESPONSE_MOCK
|
from authentik.flows.tests.test_executor import TO_STAGE_RESPONSE_MOCK
|
||||||
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
||||||
from authentik.stages.invitation.models import Invitation, InvitationStage
|
from authentik.stages.invitation.models import Invitation, InvitationStage
|
||||||
|
@ -25,7 +24,7 @@ from authentik.stages.password import BACKEND_INBUILT
|
||||||
from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
|
from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
|
||||||
|
|
||||||
|
|
||||||
class TestUserLoginStage(APITestCase):
|
class TestUserLoginStage(FlowTestCase):
|
||||||
"""Login tests"""
|
"""Login tests"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -57,18 +56,10 @@ class TestUserLoginStage(APITestCase):
|
||||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageResponse(
|
||||||
force_str(response.content),
|
response,
|
||||||
{
|
flow=self.flow,
|
||||||
"component": "ak-stage-access-denied",
|
component="ak-stage-access-denied",
|
||||||
"error_message": None,
|
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
|
||||||
"flow_info": {
|
|
||||||
"background": self.flow.background_url,
|
|
||||||
"cancel_url": reverse("authentik_flows:cancel"),
|
|
||||||
"title": "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_without_invitation_continue(self):
|
def test_without_invitation_continue(self):
|
||||||
|
@ -87,14 +78,7 @@ class TestUserLoginStage(APITestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "xak-flow-redirect",
|
|
||||||
"to": reverse("authentik_core:root-redirect"),
|
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
self.stage.continue_flow_without_invitation = False
|
self.stage.continue_flow_without_invitation = False
|
||||||
self.stage.save()
|
self.stage.save()
|
||||||
|
@ -119,14 +103,7 @@ class TestUserLoginStage(APITestCase):
|
||||||
self.assertEqual(plan.context[PLAN_CONTEXT_PROMPT], data)
|
self.assertEqual(plan.context[PLAN_CONTEXT_PROMPT], data)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "xak-flow-redirect",
|
|
||||||
"to": reverse("authentik_core:root-redirect"),
|
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_with_invitation_prompt_data(self):
|
def test_with_invitation_prompt_data(self):
|
||||||
"""Test with invitation, check data in session"""
|
"""Test with invitation, check data in session"""
|
||||||
|
@ -152,14 +129,7 @@ class TestUserLoginStage(APITestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "xak-flow-redirect",
|
|
||||||
"to": reverse("authentik_core:root-redirect"),
|
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
self.assertFalse(Invitation.objects.filter(pk=invite.pk))
|
self.assertFalse(Invitation.objects.filter(pk=invite.pk))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,14 +3,12 @@ from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_str
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.flows.challenge import ChallengeTypes
|
|
||||||
from authentik.flows.markers import StageMarker
|
from authentik.flows.markers import StageMarker
|
||||||
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
||||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
|
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
|
||||||
|
from authentik.flows.tests import FlowTestCase
|
||||||
from authentik.flows.tests.test_executor import TO_STAGE_RESPONSE_MOCK
|
from authentik.flows.tests.test_executor import TO_STAGE_RESPONSE_MOCK
|
||||||
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
||||||
from authentik.lib.generators import generate_key
|
from authentik.lib.generators import generate_key
|
||||||
|
@ -20,7 +18,7 @@ from authentik.stages.password.models import PasswordStage
|
||||||
MOCK_BACKEND_AUTHENTICATE = MagicMock(side_effect=PermissionDenied("test"))
|
MOCK_BACKEND_AUTHENTICATE = MagicMock(side_effect=PermissionDenied("test"))
|
||||||
|
|
||||||
|
|
||||||
class TestPasswordStage(APITestCase):
|
class TestPasswordStage(FlowTestCase):
|
||||||
"""Password tests"""
|
"""Password tests"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -56,18 +54,11 @@ class TestPasswordStage(APITestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageResponse(
|
||||||
force_str(response.content),
|
response,
|
||||||
{
|
self.flow,
|
||||||
"component": "ak-stage-access-denied",
|
component="ak-stage-access-denied",
|
||||||
"error_message": None,
|
error_message="Unknown error",
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
|
||||||
"flow_info": {
|
|
||||||
"background": self.flow.background_url,
|
|
||||||
"cancel_url": reverse("authentik_flows:cancel"),
|
|
||||||
"title": "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_recovery_flow_link(self):
|
def test_recovery_flow_link(self):
|
||||||
|
@ -83,7 +74,7 @@ class TestPasswordStage(APITestCase):
|
||||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertIn(flow.slug, force_str(response.content))
|
self.assertIn(flow.slug, response.content.decode())
|
||||||
|
|
||||||
def test_valid_password(self):
|
def test_valid_password(self):
|
||||||
"""Test with a valid pending user and valid password"""
|
"""Test with a valid pending user and valid password"""
|
||||||
|
@ -100,14 +91,7 @@ class TestPasswordStage(APITestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "xak-flow-redirect",
|
|
||||||
"to": reverse("authentik_core:root-redirect"),
|
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_invalid_password(self):
|
def test_invalid_password(self):
|
||||||
"""Test with a valid pending user and invalid password"""
|
"""Test with a valid pending user and invalid password"""
|
||||||
|
@ -176,16 +160,9 @@ class TestPasswordStage(APITestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageResponse(
|
||||||
force_str(response.content),
|
response,
|
||||||
{
|
self.flow,
|
||||||
"component": "ak-stage-access-denied",
|
component="ak-stage-access-denied",
|
||||||
"error_message": None,
|
error_message="Unknown error",
|
||||||
"flow_info": {
|
|
||||||
"background": self.flow.background_url,
|
|
||||||
"cancel_url": reverse("authentik_flows:cancel"),
|
|
||||||
"title": "",
|
|
||||||
},
|
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,23 +2,21 @@
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_str
|
|
||||||
from rest_framework.exceptions import ErrorDetail
|
from rest_framework.exceptions import ErrorDetail
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.core.tests.utils import create_test_admin_user
|
from authentik.core.tests.utils import create_test_admin_user
|
||||||
from authentik.flows.challenge import ChallengeTypes
|
|
||||||
from authentik.flows.markers import StageMarker
|
from authentik.flows.markers import StageMarker
|
||||||
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
||||||
from authentik.flows.planner import FlowPlan
|
from authentik.flows.planner import FlowPlan
|
||||||
|
from authentik.flows.tests import FlowTestCase
|
||||||
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
||||||
from authentik.policies.expression.models import ExpressionPolicy
|
from authentik.policies.expression.models import ExpressionPolicy
|
||||||
from authentik.stages.prompt.models import FieldTypes, Prompt, PromptStage
|
from authentik.stages.prompt.models import FieldTypes, Prompt, PromptStage
|
||||||
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT, PromptChallengeResponse
|
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT, PromptChallengeResponse
|
||||||
|
|
||||||
|
|
||||||
class TestPromptStage(APITestCase):
|
class TestPromptStage(FlowTestCase):
|
||||||
"""Prompt tests"""
|
"""Prompt tests"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -123,9 +121,9 @@ class TestPromptStage(APITestCase):
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
for prompt in self.stage.fields.all():
|
for prompt in self.stage.fields.all():
|
||||||
self.assertIn(prompt.field_key, force_str(response.content))
|
self.assertIn(prompt.field_key, response.content.decode())
|
||||||
self.assertIn(prompt.label, force_str(response.content))
|
self.assertIn(prompt.label, response.content.decode())
|
||||||
self.assertIn(prompt.placeholder, force_str(response.content))
|
self.assertIn(prompt.placeholder, response.content.decode())
|
||||||
|
|
||||||
def test_valid_challenge_with_policy(self) -> PromptChallengeResponse:
|
def test_valid_challenge_with_policy(self) -> PromptChallengeResponse:
|
||||||
"""Test challenge_response validation"""
|
"""Test challenge_response validation"""
|
||||||
|
@ -171,14 +169,7 @@ class TestPromptStage(APITestCase):
|
||||||
challenge_response.validated_data,
|
challenge_response.validated_data,
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "xak-flow-redirect",
|
|
||||||
"to": reverse("authentik_core:root-redirect"),
|
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
# Check that valid data has been saved
|
# Check that valid data has been saved
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
|
|
|
@ -2,20 +2,18 @@
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_str
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.flows.challenge import ChallengeTypes
|
|
||||||
from authentik.flows.markers import StageMarker
|
from authentik.flows.markers import StageMarker
|
||||||
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
||||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
|
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
|
||||||
|
from authentik.flows.tests import FlowTestCase
|
||||||
from authentik.flows.tests.test_executor import TO_STAGE_RESPONSE_MOCK
|
from authentik.flows.tests.test_executor import TO_STAGE_RESPONSE_MOCK
|
||||||
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
||||||
from authentik.stages.user_delete.models import UserDeleteStage
|
from authentik.stages.user_delete.models import UserDeleteStage
|
||||||
|
|
||||||
|
|
||||||
class TestUserDeleteStage(APITestCase):
|
class TestUserDeleteStage(FlowTestCase):
|
||||||
"""Delete tests"""
|
"""Delete tests"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -46,19 +44,7 @@ class TestUserDeleteStage(APITestCase):
|
||||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageResponse(response, self.flow, component="ak-stage-access-denied")
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "ak-stage-access-denied",
|
|
||||||
"error_message": None,
|
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
|
||||||
"flow_info": {
|
|
||||||
"background": self.flow.background_url,
|
|
||||||
"cancel_url": reverse("authentik_flows:cancel"),
|
|
||||||
"title": "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_user_delete_get(self):
|
def test_user_delete_get(self):
|
||||||
"""Test Form render"""
|
"""Test Form render"""
|
||||||
|
@ -72,14 +58,7 @@ class TestUserDeleteStage(APITestCase):
|
||||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "xak-flow-redirect",
|
|
||||||
"to": reverse("authentik_core:root-redirect"),
|
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertFalse(User.objects.filter(username=self.username).exists())
|
self.assertFalse(User.objects.filter(username=self.username).exists())
|
||||||
|
|
||||||
|
@ -95,13 +74,6 @@ class TestUserDeleteStage(APITestCase):
|
||||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "xak-flow-redirect",
|
|
||||||
"to": reverse("authentik_core:root-redirect"),
|
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertFalse(User.objects.filter(username=self.username).exists())
|
self.assertFalse(User.objects.filter(username=self.username).exists())
|
||||||
|
|
|
@ -3,20 +3,18 @@ from time import sleep
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_str
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.flows.challenge import ChallengeTypes
|
|
||||||
from authentik.flows.markers import StageMarker
|
from authentik.flows.markers import StageMarker
|
||||||
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
||||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
|
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
|
||||||
|
from authentik.flows.tests import FlowTestCase
|
||||||
from authentik.flows.tests.test_executor import TO_STAGE_RESPONSE_MOCK
|
from authentik.flows.tests.test_executor import TO_STAGE_RESPONSE_MOCK
|
||||||
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
||||||
from authentik.stages.user_login.models import UserLoginStage
|
from authentik.stages.user_login.models import UserLoginStage
|
||||||
|
|
||||||
|
|
||||||
class TestUserLoginStage(APITestCase):
|
class TestUserLoginStage(FlowTestCase):
|
||||||
"""Login tests"""
|
"""Login tests"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -44,14 +42,7 @@ class TestUserLoginStage(APITestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "xak-flow-redirect",
|
|
||||||
"to": reverse("authentik_core:root-redirect"),
|
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_valid_post(self):
|
def test_valid_post(self):
|
||||||
"""Test with a valid pending user and backend"""
|
"""Test with a valid pending user and backend"""
|
||||||
|
@ -66,14 +57,7 @@ class TestUserLoginStage(APITestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "xak-flow-redirect",
|
|
||||||
"to": reverse("authentik_core:root-redirect"),
|
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_expiry(self):
|
def test_expiry(self):
|
||||||
"""Test with expiry"""
|
"""Test with expiry"""
|
||||||
|
@ -89,14 +73,7 @@ class TestUserLoginStage(APITestCase):
|
||||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "xak-flow-redirect",
|
|
||||||
"to": reverse("authentik_core:root-redirect"),
|
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
self.assertNotEqual(list(self.client.session.keys()), [])
|
self.assertNotEqual(list(self.client.session.keys()), [])
|
||||||
sleep(3)
|
sleep(3)
|
||||||
self.client.session.clear_expired()
|
self.client.session.clear_expired()
|
||||||
|
@ -118,18 +95,10 @@ class TestUserLoginStage(APITestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageResponse(
|
||||||
force_str(response.content),
|
response,
|
||||||
{
|
self.flow,
|
||||||
"component": "ak-stage-access-denied",
|
component="ak-stage-access-denied",
|
||||||
"error_message": None,
|
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
|
||||||
"flow_info": {
|
|
||||||
"background": self.flow.background_url,
|
|
||||||
"cancel_url": reverse("authentik_flows:cancel"),
|
|
||||||
"title": "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_inactive_account(self):
|
def test_inactive_account(self):
|
||||||
|
@ -147,13 +116,6 @@ class TestUserLoginStage(APITestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "xak-flow-redirect",
|
|
||||||
"to": reverse("authentik_core:root-redirect"),
|
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
response = self.client.get(reverse("authentik_api:application-list"))
|
response = self.client.get(reverse("authentik_api:application-list"))
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
|
@ -1,20 +1,18 @@
|
||||||
"""logout tests"""
|
"""logout tests"""
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_str
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.flows.challenge import ChallengeTypes
|
|
||||||
from authentik.flows.markers import StageMarker
|
from authentik.flows.markers import StageMarker
|
||||||
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
||||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
|
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
|
||||||
|
from authentik.flows.tests import FlowTestCase
|
||||||
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
||||||
from authentik.stages.password import BACKEND_INBUILT
|
from authentik.stages.password import BACKEND_INBUILT
|
||||||
from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
|
from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
|
||||||
from authentik.stages.user_logout.models import UserLogoutStage
|
from authentik.stages.user_logout.models import UserLogoutStage
|
||||||
|
|
||||||
|
|
||||||
class TestUserLogoutStage(APITestCase):
|
class TestUserLogoutStage(FlowTestCase):
|
||||||
"""Logout tests"""
|
"""Logout tests"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -44,15 +42,7 @@ class TestUserLogoutStage(APITestCase):
|
||||||
|
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
# pylint: disable=no-member
|
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "xak-flow-redirect",
|
|
||||||
"to": reverse("authentik_core:root-redirect"),
|
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_valid_post(self):
|
def test_valid_post(self):
|
||||||
"""Test with a valid pending user and backend"""
|
"""Test with a valid pending user and backend"""
|
||||||
|
@ -69,12 +59,4 @@ class TestUserLogoutStage(APITestCase):
|
||||||
|
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
# pylint: disable=no-member
|
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "xak-flow-redirect",
|
|
||||||
"to": reverse("authentik_core:root-redirect"),
|
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
|
@ -4,23 +4,21 @@ from random import SystemRandom
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_str
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from authentik.core.models import USER_ATTRIBUTE_SOURCES, Group, Source, User, UserSourceConnection
|
from authentik.core.models import USER_ATTRIBUTE_SOURCES, Group, Source, User, UserSourceConnection
|
||||||
from authentik.core.sources.stage import PLAN_CONTEXT_SOURCES_CONNECTION
|
from authentik.core.sources.stage import PLAN_CONTEXT_SOURCES_CONNECTION
|
||||||
from authentik.core.tests.utils import create_test_admin_user
|
from authentik.core.tests.utils import create_test_admin_user
|
||||||
from authentik.flows.challenge import ChallengeTypes
|
|
||||||
from authentik.flows.markers import StageMarker
|
from authentik.flows.markers import StageMarker
|
||||||
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
||||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
|
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
|
||||||
|
from authentik.flows.tests import FlowTestCase
|
||||||
from authentik.flows.tests.test_executor import TO_STAGE_RESPONSE_MOCK
|
from authentik.flows.tests.test_executor import TO_STAGE_RESPONSE_MOCK
|
||||||
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
||||||
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
|
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
|
||||||
from authentik.stages.user_write.models import UserWriteStage
|
from authentik.stages.user_write.models import UserWriteStage
|
||||||
|
|
||||||
|
|
||||||
class TestUserWriteStage(APITestCase):
|
class TestUserWriteStage(FlowTestCase):
|
||||||
"""Write tests"""
|
"""Write tests"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -60,14 +58,7 @@ class TestUserWriteStage(APITestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "xak-flow-redirect",
|
|
||||||
"to": reverse("authentik_core:root-redirect"),
|
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
user_qs = User.objects.filter(username=plan.context[PLAN_CONTEXT_PROMPT]["username"])
|
user_qs = User.objects.filter(username=plan.context[PLAN_CONTEXT_PROMPT]["username"])
|
||||||
self.assertTrue(user_qs.exists())
|
self.assertTrue(user_qs.exists())
|
||||||
self.assertTrue(user_qs.first().check_password(password))
|
self.assertTrue(user_qs.first().check_password(password))
|
||||||
|
@ -98,14 +89,7 @@ class TestUserWriteStage(APITestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
force_str(response.content),
|
|
||||||
{
|
|
||||||
"component": "xak-flow-redirect",
|
|
||||||
"to": reverse("authentik_core:root-redirect"),
|
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
user_qs = User.objects.filter(username=plan.context[PLAN_CONTEXT_PROMPT]["username"])
|
user_qs = User.objects.filter(username=plan.context[PLAN_CONTEXT_PROMPT]["username"])
|
||||||
self.assertTrue(user_qs.exists())
|
self.assertTrue(user_qs.exists())
|
||||||
self.assertTrue(user_qs.first().check_password(new_password))
|
self.assertTrue(user_qs.first().check_password(new_password))
|
||||||
|
@ -128,18 +112,10 @@ class TestUserWriteStage(APITestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageResponse(
|
||||||
force_str(response.content),
|
response,
|
||||||
{
|
self.flow,
|
||||||
"component": "ak-stage-access-denied",
|
component="ak-stage-access-denied",
|
||||||
"error_message": None,
|
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
|
||||||
"flow_info": {
|
|
||||||
"background": self.flow.background_url,
|
|
||||||
"cancel_url": reverse("authentik_flows:cancel"),
|
|
||||||
"title": "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@patch(
|
@patch(
|
||||||
|
@ -163,18 +139,10 @@ class TestUserWriteStage(APITestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageResponse(
|
||||||
force_str(response.content),
|
response,
|
||||||
{
|
self.flow,
|
||||||
"component": "ak-stage-access-denied",
|
component="ak-stage-access-denied",
|
||||||
"error_message": None,
|
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
|
||||||
"flow_info": {
|
|
||||||
"background": self.flow.background_url,
|
|
||||||
"cancel_url": reverse("authentik_flows:cancel"),
|
|
||||||
"title": "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@patch(
|
@patch(
|
||||||
|
@ -199,16 +167,8 @@ class TestUserWriteStage(APITestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertJSONEqual(
|
self.assertStageResponse(
|
||||||
force_str(response.content),
|
response,
|
||||||
{
|
self.flow,
|
||||||
"component": "ak-stage-access-denied",
|
component="ak-stage-access-denied",
|
||||||
"error_message": None,
|
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
|
||||||
"flow_info": {
|
|
||||||
"background": self.flow.background_url,
|
|
||||||
"cancel_url": reverse("authentik_flows:cancel"),
|
|
||||||
"title": "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.client import RequestFactory
|
from django.test.client import RequestFactory
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_str
|
|
||||||
|
|
||||||
from authentik.core.tests.utils import create_test_tenant
|
from authentik.core.tests.utils import create_test_tenant
|
||||||
from authentik.events.models import Event, EventAction
|
from authentik.events.models import Event, EventAction
|
||||||
|
@ -18,7 +17,7 @@ class TestTenants(TestCase):
|
||||||
"""Test Current tenant API"""
|
"""Test Current tenant API"""
|
||||||
tenant = create_test_tenant()
|
tenant = create_test_tenant()
|
||||||
self.assertJSONEqual(
|
self.assertJSONEqual(
|
||||||
force_str(self.client.get(reverse("authentik_api:tenant-current")).content),
|
self.client.get(reverse("authentik_api:tenant-current")).content.decode(),
|
||||||
{
|
{
|
||||||
"branding_logo": "/static/dist/assets/icons/icon_left_brand.svg",
|
"branding_logo": "/static/dist/assets/icons/icon_left_brand.svg",
|
||||||
"branding_favicon": "/static/dist/assets/icons/icon.png",
|
"branding_favicon": "/static/dist/assets/icons/icon.png",
|
||||||
|
@ -33,11 +32,9 @@ class TestTenants(TestCase):
|
||||||
Tenant.objects.all().delete()
|
Tenant.objects.all().delete()
|
||||||
Tenant.objects.create(domain="bar.baz", branding_title="custom")
|
Tenant.objects.create(domain="bar.baz", branding_title="custom")
|
||||||
self.assertJSONEqual(
|
self.assertJSONEqual(
|
||||||
force_str(
|
|
||||||
self.client.get(
|
self.client.get(
|
||||||
reverse("authentik_api:tenant-current"), HTTP_HOST="foo.bar.baz"
|
reverse("authentik_api:tenant-current"), HTTP_HOST="foo.bar.baz"
|
||||||
).content
|
).content.decode(),
|
||||||
),
|
|
||||||
{
|
{
|
||||||
"branding_logo": "/static/dist/assets/icons/icon_left_brand.svg",
|
"branding_logo": "/static/dist/assets/icons/icon_left_brand.svg",
|
||||||
"branding_favicon": "/static/dist/assets/icons/icon.png",
|
"branding_favicon": "/static/dist/assets/icons/icon.png",
|
||||||
|
@ -51,7 +48,7 @@ class TestTenants(TestCase):
|
||||||
"""Test fallback tenant"""
|
"""Test fallback tenant"""
|
||||||
Tenant.objects.all().delete()
|
Tenant.objects.all().delete()
|
||||||
self.assertJSONEqual(
|
self.assertJSONEqual(
|
||||||
force_str(self.client.get(reverse("authentik_api:tenant-current")).content),
|
self.client.get(reverse("authentik_api:tenant-current")).content.decode(),
|
||||||
{
|
{
|
||||||
"branding_logo": "/static/dist/assets/icons/icon_left_brand.svg",
|
"branding_logo": "/static/dist/assets/icons/icon_left_brand.svg",
|
||||||
"branding_favicon": "/static/dist/assets/icons/icon.png",
|
"branding_favicon": "/static/dist/assets/icons/icon.png",
|
||||||
|
|
|
@ -19036,9 +19036,15 @@ components:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/ErrorDetail'
|
$ref: '#/components/schemas/ErrorDetail'
|
||||||
|
pending_user:
|
||||||
|
type: string
|
||||||
|
pending_user_avatar:
|
||||||
|
type: string
|
||||||
error_message:
|
error_message:
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
|
- pending_user
|
||||||
|
- pending_user_avatar
|
||||||
- type
|
- type
|
||||||
App:
|
App:
|
||||||
type: object
|
type: object
|
||||||
|
|
Reference in New Issue