core: remove redundant views/forms
This commit is contained in:
parent
5b2bf7519a
commit
69120da45c
|
@ -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):
|
||||||
|
|
|
@ -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})
|
||||||
|
|
|
@ -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")
|
|
||||||
|
|
|
@ -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}"
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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"))
|
|
||||||
|
|
|
@ -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"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -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")
|
|
||||||
|
|
|
@ -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)
|
|
||||||
|
|
|
@ -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"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Reference in New Issue