stages/dummy: add unittests

stages/password: improve coverage
stages/user_login: improve coverage
This commit is contained in:
Jens Langhammer 2020-05-10 17:02:01 +02:00
parent f111604b70
commit a3a3dde1c8
8 changed files with 65 additions and 39 deletions

View File

@ -60,12 +60,12 @@ router.register("sources/ldap", LDAPSourceViewSet)
router.register("sources/oauth", OAuthSourceViewSet) router.register("sources/oauth", OAuthSourceViewSet)
router.register("policies/all", PolicyViewSet) router.register("policies/all", PolicyViewSet)
router.register("policies/passwordexpiry", PasswordExpiryPolicyViewSet) router.register("policies/expression", ExpressionPolicyViewSet)
router.register("policies/haveibeenpwned", HaveIBeenPwendPolicyViewSet) router.register("policies/haveibeenpwned", HaveIBeenPwendPolicyViewSet)
router.register("policies/password", PasswordPolicyViewSet) router.register("policies/password", PasswordPolicyViewSet)
router.register("policies/passwordexpiry", PasswordExpiryPolicyViewSet)
router.register("policies/reputation", ReputationPolicyViewSet) router.register("policies/reputation", ReputationPolicyViewSet)
router.register("policies/webhook", WebhookPolicyViewSet) router.register("policies/webhook", WebhookPolicyViewSet)
router.register("policies/expression", ExpressionPolicyViewSet)
router.register("providers/all", ProviderViewSet) router.register("providers/all", ProviderViewSet)
router.register("providers/applicationgateway", ApplicationGatewayProviderViewSet) router.register("providers/applicationgateway", ApplicationGatewayProviderViewSet)
@ -80,12 +80,12 @@ router.register("propertymappings/saml", SAMLPropertyMappingViewSet)
router.register("stages/all", StageViewSet) router.register("stages/all", StageViewSet)
router.register("stages/captcha", CaptchaStageViewSet) router.register("stages/captcha", CaptchaStageViewSet)
router.register("stages/email", EmailStageViewSet) router.register("stages/email", EmailStageViewSet)
router.register("stages/identification", IdentificationStageViewSet)
router.register("stages/otp", OTPStageViewSet) router.register("stages/otp", OTPStageViewSet)
router.register("stages/password", PasswordStageViewSet) router.register("stages/password", PasswordStageViewSet)
router.register("stages/identification", IdentificationStageViewSet)
router.register("stages/user_login", UserLoginStageViewSet)
router.register("stages/prompt", PromptStageViewSet) router.register("stages/prompt", PromptStageViewSet)
router.register("stages/prompt/prompts", PromptViewSet) router.register("stages/prompt/prompts", PromptViewSet)
router.register("stages/user_login", UserLoginStageViewSet)
router.register("flows", FlowViewSet) router.register("flows", FlowViewSet)
router.register("flows/bindings", FlowStageBindingViewSet) router.register("flows/bindings", FlowStageBindingViewSet)

View File

@ -11,7 +11,6 @@ from django.views.generic import DeleteView, FormView, UpdateView
from passbook.core.forms.users import PasswordChangeForm, UserDetailForm from passbook.core.forms.users import PasswordChangeForm, UserDetailForm
from passbook.lib.config import CONFIG from passbook.lib.config import CONFIG
from passbook.stages.password.exceptions import PasswordPolicyInvalid
class UserSettingsView(SuccessMessageMixin, LoginRequiredMixin, UpdateView): class UserSettingsView(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
@ -48,20 +47,20 @@ class UserChangePasswordView(LoginRequiredMixin, FormView):
template_name = "login/form_with_user.html" template_name = "login/form_with_user.html"
def form_valid(self, form: PasswordChangeForm): def form_valid(self, form: PasswordChangeForm):
# TODO: Rewrite to flow
try: try:
# user.set_password checks against Policies so we don't need to manually do it here # user.set_password checks against Policies so we don't need to manually do it here
self.request.user.set_password(form.cleaned_data.get("password")) self.request.user.set_password(form.cleaned_data.get("password"))
self.request.user.save() self.request.user.save()
update_session_auth_hash(self.request, self.request.user) update_session_auth_hash(self.request, self.request.user)
messages.success(self.request, _("Successfully changed password")) messages.success(self.request, _("Successfully changed password"))
except PasswordPolicyInvalid as exc: except ValueError:
# Manually inject error into form # Manually inject error into form
# pylint: disable=protected-access # pylint: disable=protected-access
errors = form._errors.setdefault("password_repeat", ErrorList("")) errors = form._errors.setdefault("password_repeat", ErrorList(""))
# pylint: disable=protected-access # pylint: disable=protected-access
errors = form._errors.setdefault("password", ErrorList()) errors = form._errors.setdefault("password", ErrorList())
for error in exc.messages: errors.append("foo")
errors.append(error)
return self.form_invalid(form) return self.form_invalid(form)
return redirect("passbook_core:overview") return redirect("passbook_core:overview")

View File

@ -352,6 +352,7 @@ for handler_name, level in _LOGGING_HANDLER_MAP.items():
TEST = False TEST = False
TEST_RUNNER = "xmlrunner.extra.djangotestrunner.XMLTestRunner" TEST_RUNNER = "xmlrunner.extra.djangotestrunner.XMLTestRunner"
TEST_OUTPUT_VERBOSE = 2
TEST_OUTPUT_FILE_NAME = "unittest.xml" TEST_OUTPUT_FILE_NAME = "unittest.xml"

View File

@ -0,0 +1,50 @@
"""dummy 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.stages.dummy.forms import DummyStageForm
from passbook.stages.dummy.models import DummyStage
class TestDummyStage(TestCase):
"""Dummy 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-dummy",
slug="test-dummy",
designation=FlowDesignation.AUTHENTICATION,
)
self.stage = DummyStage.objects.create(name="dummy",)
FlowStageBinding.objects.create(
flow=self.flow, stage=self.stage, order=0,
)
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_post(self):
"""Test with valid email, check that URL redirects back to itself"""
url = reverse(
"passbook_flows:flow-executor", kwargs={"flow_slug": self.flow.slug}
)
response = self.client.post(url, {})
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, reverse("passbook_core:overview"))
def test_form(self):
"""Test Form"""
data = {"name": "test"}
self.assertEqual(DummyStageForm(data).is_valid(), True)

View File

@ -1,12 +0,0 @@
"""passbook password policy exceptions"""
from passbook.lib.sentry import SentryIgnoredException
class PasswordPolicyInvalid(SentryIgnoredException):
"""Exception raised when a Password Policy fails"""
messages = []
def __init__(self, *messages):
super().__init__()
self.messages = messages

View File

@ -3,8 +3,6 @@ from django.contrib.postgres.fields import ArrayField
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from passbook.core.models import Policy, User
from passbook.core.types import UIUserSettings
from passbook.flows.models import Stage from passbook.flows.models import Stage
@ -15,26 +13,10 @@ class PasswordStage(Stage):
models.TextField(), models.TextField(),
help_text=_("Selection of backends to test the password against."), help_text=_("Selection of backends to test the password against."),
) )
password_policies = models.ManyToManyField(Policy, blank=True)
type = "passbook.stages.password.stage.PasswordStage" type = "passbook.stages.password.stage.PasswordStage"
form = "passbook.stages.password.forms.PasswordStageForm" form = "passbook.stages.password.forms.PasswordStageForm"
@property
def ui_user_settings(self) -> UIUserSettings:
return UIUserSettings(
name="Change Password",
icon="pficon-key",
view_name="passbook_core:user-change-password",
)
def password_passes(self, user: User) -> bool:
"""Return true if user's password passes, otherwise False or raise Exception"""
for policy in self.policies.all():
if not policy.passes(user):
return False
return True
def __str__(self): def __str__(self):
return f"Password Stage {self.name}" return f"Password Stage {self.name}"

View File

@ -53,7 +53,7 @@ class Prompt(UUIDModel):
return forms.IntegerField( return forms.IntegerField(
label=_(self.label), label=_(self.label),
widget=forms.NumberInput(attrs=attrs), widget=forms.NumberInput(attrs=attrs),
requred=self.required, required=self.required,
) )
raise ValueError raise ValueError

View File

@ -7,6 +7,7 @@ from passbook.flows.models import Flow, FlowDesignation, FlowStageBinding
from passbook.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan from passbook.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
from passbook.flows.views import SESSION_KEY_PLAN from passbook.flows.views import SESSION_KEY_PLAN
from passbook.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND from passbook.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
from passbook.stages.user_login.forms import UserLoginStageForm
from passbook.stages.user_login.models import UserLoginStage from passbook.stages.user_login.models import UserLoginStage
@ -75,3 +76,8 @@ class TestUserLoginStage(TestCase):
) )
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, reverse("passbook_flows:denied")) self.assertEqual(response.url, reverse("passbook_flows:denied"))
def test_form(self):
"""Test Form"""
data = {"name": "test"}
self.assertEqual(UserLoginStageForm(data).is_valid(), True)