stages/identification: load uid_fields from stage in form, add more unit tests
This commit is contained in:
parent
fd5b2298e5
commit
8e488670ad
|
@ -4,6 +4,6 @@ from django.urls import path
|
|||
from passbook.flows.views import FlowExecutorView, FlowPermissionDeniedView
|
||||
|
||||
urlpatterns = [
|
||||
path("<slug:flow_slug>/", FlowExecutorView.as_view(), name="flow-executor"),
|
||||
path("denied/", FlowPermissionDeniedView.as_view(), name="denied"),
|
||||
path("<slug:flow_slug>/", FlowExecutorView.as_view(), name="flow-executor"),
|
||||
]
|
||||
|
|
|
@ -151,7 +151,7 @@ class FlowExecutorView(View):
|
|||
def stage_invalid(self) -> HttpResponse:
|
||||
"""Callback used stage when data is correct but a policy denies access
|
||||
or the user account is disabled."""
|
||||
LOGGER.debug("User invalid", flow_slug=self.flow.slug)
|
||||
LOGGER.debug("Stage invalid", flow_slug=self.flow.slug)
|
||||
self.cancel()
|
||||
return redirect_with_qs("passbook_flows:denied", self.request.GET)
|
||||
|
||||
|
|
|
@ -4,9 +4,8 @@ from django.core.validators import validate_email
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from structlog import get_logger
|
||||
|
||||
from passbook.lib.config import CONFIG
|
||||
from passbook.lib.utils.ui import human_list
|
||||
from passbook.stages.identification.models import IdentificationStage
|
||||
from passbook.stages.identification.models import IdentificationStage, UserFields
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
@ -26,20 +25,22 @@ class IdentificationStageForm(forms.ModelForm):
|
|||
class IdentificationForm(forms.Form):
|
||||
"""Allow users to login"""
|
||||
|
||||
stage: IdentificationStage
|
||||
|
||||
title = _("Log in to your account")
|
||||
uid_field = forms.CharField(label=_(""))
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.stage = kwargs.pop("stage")
|
||||
super().__init__(*args, **kwargs)
|
||||
# TODO: Get UID Fields from stage config
|
||||
if CONFIG.y("passbook.uid_fields") == ["e-mail"]:
|
||||
if self.stage.user_fields == [UserFields.E_MAIL]:
|
||||
self.fields["uid_field"] = forms.EmailField()
|
||||
self.fields["uid_field"].label = human_list(
|
||||
[x.title() for x in CONFIG.y("passbook.uid_fields")]
|
||||
[y.title() for x, y in UserFields.choices]
|
||||
)
|
||||
|
||||
def clean_uid_field(self):
|
||||
"""Validate uid_field after EmailValidator if 'email' is the only selected uid_fields"""
|
||||
if CONFIG.y("passbook.uid_fields") == ["email"]:
|
||||
if self.stage.user_fields == [UserFields.E_MAIL]:
|
||||
validate_email(self.cleaned_data.get("uid_field"))
|
||||
return self.cleaned_data.get("uid_field")
|
||||
|
|
|
@ -7,7 +7,7 @@ from passbook.flows.models import Stage
|
|||
|
||||
|
||||
class UserFields(models.TextChoices):
|
||||
"""Fields which the user can identifiy themselves with"""
|
||||
"""Fields which the user can identify themselves with"""
|
||||
|
||||
E_MAIL = "email"
|
||||
USERNAME = "username"
|
||||
|
|
|
@ -23,6 +23,11 @@ class IdentificationStageView(FormView, AuthenticationStage):
|
|||
|
||||
form_class = IdentificationForm
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs["stage"] = self.executor.current_stage
|
||||
return kwargs
|
||||
|
||||
def get_template_names(self) -> List[str]:
|
||||
current_stage: IdentificationStage = self.executor.current_stage
|
||||
return [current_stage.template]
|
||||
|
@ -61,6 +66,6 @@ class IdentificationStageView(FormView, AuthenticationStage):
|
|||
if not pre_user:
|
||||
LOGGER.debug("invalid_login")
|
||||
messages.error(self.request, _("Failed to authenticate."))
|
||||
return self.executor.stage_invalid()
|
||||
return self.form_invalid(form)
|
||||
self.executor.plan.context[PLAN_CONTEXT_PENDING_USER] = pre_user
|
||||
return self.executor.stage_ok()
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
"""identification tests"""
|
||||
from django.shortcuts import reverse
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from passbook.core.models import User
|
||||
from passbook.flows.models import Flow, FlowDesignation, FlowStageBinding
|
||||
from passbook.sources.oauth.models import OAuthSource
|
||||
from passbook.stages.identification.models import (
|
||||
IdentificationStage,
|
||||
Templates,
|
||||
UserFields,
|
||||
)
|
||||
from passbook.stages.login.models import LoginStage
|
||||
|
||||
|
||||
class TestIdentificationStage(TestCase):
|
||||
"""Identification tests"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.user = User.objects.create(username="unittest", email="test@beryju.org")
|
||||
self.client = Client()
|
||||
|
||||
self.flow = Flow.objects.create(
|
||||
name="test-identification",
|
||||
slug="test-identification",
|
||||
designation=FlowDesignation.AUTHENTICATION,
|
||||
)
|
||||
FlowStageBinding.objects.create(
|
||||
flow=self.flow,
|
||||
stage=IdentificationStage.objects.create(
|
||||
name="identification",
|
||||
user_fields=[UserFields.E_MAIL],
|
||||
template=Templates.DEFAULT_LOGIN,
|
||||
),
|
||||
order=0,
|
||||
)
|
||||
FlowStageBinding.objects.create(
|
||||
flow=self.flow, stage=LoginStage.objects.create(name="login",), order=1
|
||||
)
|
||||
|
||||
# OAuthSource for the login view
|
||||
OAuthSource.objects.create(name="test", slug="test")
|
||||
|
||||
def test_valid_render(self):
|
||||
"""Test that View renders correctly"""
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"passbook_flows:flow-executor", kwargs={"flow_slug": self.flow.slug}
|
||||
)
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_valid_with_email(self):
|
||||
"""Test with valid email, check that URL redirects back to itself"""
|
||||
form_data = {"uid_field": self.user.email}
|
||||
url = reverse(
|
||||
"passbook_flows:flow-executor", kwargs={"flow_slug": self.flow.slug}
|
||||
)
|
||||
response = self.client.post(url, form_data,)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, url)
|
||||
|
||||
def test_invalid_with_username(self):
|
||||
"""Test invalid with username (user exists but stage only allows e-mail)"""
|
||||
form_data = {"uid_field": self.user.username}
|
||||
response = self.client.post(
|
||||
reverse(
|
||||
"passbook_flows:flow-executor", kwargs={"flow_slug": self.flow.slug}
|
||||
),
|
||||
form_data,
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_invalid_with_invalid_email(self):
|
||||
"""Test with invalid e-mail (user doesn't exist) -> Will return to login form"""
|
||||
form_data = {"uid_field": self.user.email + "test"}
|
||||
response = self.client.post(
|
||||
reverse(
|
||||
"passbook_flows:flow-executor", kwargs={"flow_slug": self.flow.slug}
|
||||
),
|
||||
form_data,
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
Reference in New Issue