core: remove redundant views/forms

This commit is contained in:
Jens Langhammer 2020-05-11 00:49:48 +02:00
parent 5b2bf7519a
commit 69120da45c
12 changed files with 43 additions and 138 deletions

View File

@ -19,7 +19,7 @@ class AdministrationOverviewView(AdminRequiredMixin, TemplateView):
"""Handle post (clear cache from modal)""" """Handle post (clear cache from modal)"""
if "clear" in self.request.POST: if "clear" in self.request.POST:
cache.clear() cache.clear()
return redirect(reverse("passbook_core:auth-login")) return redirect(reverse("passbook_flows:default-auth"))
return self.get(*args, **kwargs) return self.get(*args, **kwargs)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):

View File

@ -94,9 +94,10 @@ class UserPasswordResetView(LoginRequiredMixin, PermissionRequiredMixin, DetailV
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
"""Create nonce for user and return link""" """Create nonce for user and return link"""
super().get(request, *args, **kwargs) super().get(request, *args, **kwargs)
# TODO: create plan for user, get token
nonce = Nonce.objects.create(user=self.object) nonce = Nonce.objects.create(user=self.object)
link = request.build_absolute_uri( link = request.build_absolute_uri(
reverse("passbook_core:auth-password-reset", kwargs={"nonce": nonce.uuid}) reverse("passbook_flows:default-recovery", kwargs={"nonce": nonce.uuid})
) )
messages.success( messages.success(
request, _("Password reset link: <pre>%(link)s</pre>" % {"link": link}) request, _("Password reset link: <pre>%(link)s</pre>" % {"link": link})

View File

@ -1,8 +1,6 @@
"""passbook core user forms""" """passbook core user forms"""
from django import forms from django import forms
from django.forms import ValidationError
from django.utils.translation import gettext_lazy as _
from passbook.core.models import User from passbook.core.models import User
@ -15,28 +13,3 @@ class UserDetailForm(forms.ModelForm):
model = User model = User
fields = ["username", "name", "email"] fields = ["username", "name", "email"]
widgets = {"name": forms.TextInput} widgets = {"name": forms.TextInput}
class PasswordChangeForm(forms.Form):
"""Form to update password"""
password = forms.CharField(
label=_("Password"),
widget=forms.PasswordInput(
attrs={"placeholder": _("New Password"), "autocomplete": "new-password"}
),
)
password_repeat = forms.CharField(
label=_("Repeat Password"),
widget=forms.PasswordInput(
attrs={"placeholder": _("Repeat Password"), "autocomplete": "new-password"}
),
)
def clean_password_repeat(self):
"""Check if Password adheres to filter and if passwords matche"""
password = self.cleaned_data.get("password")
password_repeat = self.cleaned_data.get("password_repeat")
if password != password_repeat:
raise ValidationError(_("Passwords don't match"))
return self.cleaned_data.get("password_repeat")

View File

@ -208,9 +208,8 @@ class Invitation(ExportModelOperationsMixin("invitation"), UUIDModel):
@property @property
def link(self): def link(self):
"""Get link to use invitation""" """Get link to use invitation"""
return ( qs = f"?invitation={self.uuid.hex}"
reverse_lazy("passbook_core:auth-sign-up") + f"?invitation={self.uuid.hex}" return reverse_lazy("passbook_flows:default-enrollment") + qs
)
def __str__(self): def __str__(self):
return f"Invitation {self.uuid.hex} created by {self.created_by}" return f"Invitation {self.uuid.hex} created by {self.created_by}"

View File

@ -37,7 +37,7 @@
{{ user.username }} {{ user.username }}
</div> </div>
<div class="right"> <div class="right">
<a href="{% url 'passbook_core:auth-login' %}">{% trans 'Not you?' %}</a> <a href="{% url 'passbook_flows:default-auth' %}">{% trans 'Not you?' %}</a>
</div> </div>
</div> </div>
</div> </div>

View File

@ -5,7 +5,6 @@ from random import SystemRandom
from django.shortcuts import reverse from django.shortcuts import reverse
from django.test import TestCase from django.test import TestCase
from passbook.core.forms.users import PasswordChangeForm
from passbook.core.models import User from passbook.core.models import User
@ -37,21 +36,3 @@ class TestUserViews(TestCase):
) )
self.assertEqual(User.objects.filter(username="unittest user").exists(), False) self.assertEqual(User.objects.filter(username="unittest user").exists(), False)
self.setUp() self.setUp()
def test_user_change_password(self):
"""Test UserChangePasswordView"""
form_data = {"password": "test2", "password_repeat": "test2"}
form = PasswordChangeForm(data=form_data)
self.assertTrue(form.is_valid())
self.assertEqual(
self.client.get(reverse("passbook_core:user-change-password")).status_code,
200,
)
self.assertEqual(
self.client.post(
reverse("passbook_core:user-change-password"), data=form_data
).status_code,
302,
)
self.user.refresh_from_db()
self.assertTrue(self.user.check_password("test2"))

View File

@ -2,35 +2,13 @@
from django.urls import path from django.urls import path
from passbook.core.views import authentication, overview, user from passbook.core.views import authentication, overview, user
from passbook.flows.models import FlowDesignation
from passbook.flows.views import ToDefaultFlow
urlpatterns = [ urlpatterns = [
# Authentication views # Authentication views
path(
"auth/login/",
ToDefaultFlow.as_view(designation=FlowDesignation.AUTHENTICATION),
name="auth-login",
),
path("auth/logout/", authentication.LogoutView.as_view(), name="auth-logout"), path("auth/logout/", authentication.LogoutView.as_view(), name="auth-logout"),
path(
"auth/sign_up/",
ToDefaultFlow.as_view(designation=FlowDesignation.ENROLLMENT),
name="auth-sign-up",
),
path(
"auth/password/reset/<uuid:nonce_uuid>/",
authentication.PasswordResetView.as_view(),
name="auth-password-reset",
),
# User views # User views
path("-/user/", user.UserSettingsView.as_view(), name="user-settings"), path("-/user/", user.UserSettingsView.as_view(), name="user-settings"),
path("-/user/delete/", user.UserDeleteView.as_view(), name="user-delete"), path("-/user/delete/", user.UserDeleteView.as_view(), name="user-delete"),
path(
"-/user/change_password/",
user.UserChangePasswordView.as_view(),
name="user-change-password",
),
# Overview # Overview
path("", overview.OverviewView.as_view(), name="overview"), path("", overview.OverviewView.as_view(), name="overview"),
] ]

View File

@ -1,15 +1,13 @@
"""passbook core authentication views""" """passbook core authentication views"""
from django.contrib import messages from django.contrib import messages
from django.contrib.auth import login, logout from django.contrib.auth import logout
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from django.shortcuts import get_object_or_404, redirect, reverse from django.shortcuts import redirect, reverse
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views import View from django.views import View
from structlog import get_logger from structlog import get_logger
from passbook.core.models import Nonce
LOGGER = get_logger() LOGGER = get_logger()
@ -20,21 +18,4 @@ class LogoutView(LoginRequiredMixin, View):
"""Log current user out""" """Log current user out"""
logout(request) logout(request)
messages.success(request, _("You've successfully been logged out.")) messages.success(request, _("You've successfully been logged out."))
return redirect(reverse("passbook_core:auth-login")) return redirect(reverse("passbook_flows:default-auth"))
class PasswordResetView(View):
"""Temporarily authenticate User and allow them to reset their password"""
def get(self, request: HttpRequest, nonce_uuid: str) -> HttpResponse:
"""Authenticate user with nonce and redirect to password change view"""
# 3. (Optional) Trap user in password change view
nonce = get_object_or_404(Nonce, uuid=nonce_uuid)
# Workaround: hardcoded reference to ModelBackend, needs testing
nonce.user.backend = "django.contrib.auth.backends.ModelBackend"
login(request, nonce.user)
nonce.delete()
messages.success(
request, _(("Temporarily authenticated, please change your password")),
)
return redirect("passbook_core:user-change-password")

View File

@ -1,16 +1,14 @@
"""passbook core user views""" """passbook core user views"""
from django.contrib import messages from django.contrib import messages
from django.contrib.auth import logout, update_session_auth_hash from django.contrib.auth import logout
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.forms.utils import ErrorList from django.shortcuts import reverse
from django.shortcuts import redirect, reverse
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django.views.generic import DeleteView, FormView, UpdateView from django.views.generic import DeleteView, UpdateView
from passbook.core.forms.users import PasswordChangeForm, UserDetailForm from passbook.core.forms.users import UserDetailForm
from passbook.lib.config import CONFIG
class UserSettingsView(SuccessMessageMixin, LoginRequiredMixin, UpdateView): class UserSettingsView(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
@ -37,35 +35,4 @@ class UserDeleteView(LoginRequiredMixin, DeleteView):
def get_success_url(self): def get_success_url(self):
messages.success(self.request, _("Successfully deleted user.")) messages.success(self.request, _("Successfully deleted user."))
logout(self.request) logout(self.request)
return reverse("passbook_core:auth-login") return reverse("passbook_flows:default-auth")
class UserChangePasswordView(LoginRequiredMixin, FormView):
"""View for users to update their password"""
form_class = PasswordChangeForm
template_name = "login/form_with_user.html"
def form_valid(self, form: PasswordChangeForm):
# TODO: Rewrite to flow
try:
# 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.save()
update_session_auth_hash(self.request, self.request.user)
messages.success(self.request, _("Successfully changed password"))
except ValueError:
# Manually inject error into form
# pylint: disable=protected-access
errors = form._errors.setdefault("password_repeat", ErrorList(""))
# pylint: disable=protected-access
errors = form._errors.setdefault("password", ErrorList())
errors.append("foo")
return self.form_invalid(form)
return redirect("passbook_core:overview")
def get_context_data(self, **kwargs):
kwargs["config"] = CONFIG.y("passbook")
kwargs["title"] = _("Change Password")
kwargs["primary_action"] = _("Change")
return super().get_context_data(**kwargs)

View File

@ -1,9 +1,34 @@
"""flow urls""" """flow urls"""
from django.urls import path from django.urls import path
from passbook.flows.views import FlowExecutorView, FlowPermissionDeniedView from passbook.flows.models import FlowDesignation
from passbook.flows.views import (
FlowExecutorView,
FlowPermissionDeniedView,
ToDefaultFlow,
)
urlpatterns = [ urlpatterns = [
path("denied/", FlowPermissionDeniedView.as_view(), name="denied"), path("-/denied/", FlowPermissionDeniedView.as_view(), name="denied"),
path(
"-/default/auth/",
ToDefaultFlow.as_view(designation=FlowDesignation.AUTHENTICATION),
name="default-auth",
),
path(
"-/default/recovery/",
ToDefaultFlow.as_view(designation=FlowDesignation.RECOVERY),
name="default-recovery",
),
path(
"-/default/enrollment/",
ToDefaultFlow.as_view(designation=FlowDesignation.ENROLLMENT),
name="default-enrollment",
),
path(
"-/default/password_change/",
ToDefaultFlow.as_view(designation=FlowDesignation.PASSWORD_CHANGE),
name="default-password-change",
),
path("<slug:flow_slug>/", FlowExecutorView.as_view(), name="flow-executor"), path("<slug:flow_slug>/", FlowExecutorView.as_view(), name="flow-executor"),
] ]

View File

@ -44,7 +44,7 @@ INTERNAL_IPS = ["127.0.0.1"]
ALLOWED_HOSTS = ["*"] ALLOWED_HOSTS = ["*"]
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
LOGIN_URL = "passbook_core:auth-login" LOGIN_URL = "passbook_flows:default-auth"
# CSRF_FAILURE_VIEW = 'passbook.core.views.errors.CSRFErrorView.as_view' # CSRF_FAILURE_VIEW = 'passbook.core.views.errors.CSRFErrorView.as_view'
# Custom user model # Custom user model

View File

@ -11,7 +11,7 @@ from passbook.root.monitoring import MetricsView
LOGGER = get_logger() LOGGER = get_logger()
admin.autodiscover() admin.autodiscover()
admin.site.login = RedirectView.as_view(pattern_name="passbook_core:auth-login") admin.site.login = RedirectView.as_view(pattern_name="passbook_flows:default-auth")
handler400 = error.BadRequestView.as_view() handler400 = error.BadRequestView.as_view()
handler403 = error.ForbiddenView.as_view() handler403 = error.ForbiddenView.as_view()