feat: Add reset password (#5)

Co-authored-by: Leonardo Hakim <leohakim@gmail.com>
Reviewed-on: #5
Co-authored-by: Leo Hakim <leo@noreply.git.negromate.rocks>
Co-committed-by: Leo Hakim <leo@noreply.git.negromate.rocks>
This commit is contained in:
Leo Hakim 2023-12-14 17:53:08 +01:00 committed by Ales (Shagi) Zabala Alava
parent 9e77205fed
commit 49154ab3a5
9 changed files with 189 additions and 36 deletions

1
.gitignore vendored
View File

@ -3,3 +3,4 @@
/django_gas.egg-info /django_gas.egg-info
/.coverage /.coverage
/htmlcov /htmlcov
*.pyc

View File

@ -8,5 +8,9 @@ urlpatterns = [
path('login/', views.GASLoginView.as_view(), name='login'), path('login/', views.GASLoginView.as_view(), name='login'),
path('logout/', logout_then_login, {'login_url': 'gas:login'}, name='logout'), path('logout/', logout_then_login, {'login_url': 'gas:login'}, name='logout'),
path('change-password/', views.GASPasswordChangeView.as_view(), name='change_password'), path('change-password/', views.GASPasswordChangeView.as_view(), name='change_password'),
path('reset-password-confirm/<uidb64>/<token>/', views.GASPasswordResetConfirmView.as_view(), name='password_reset_confirm'),
path('reset-password-confirm/done/', views.GASPasswordResetCompleteView.as_view(), name='password_reset_complete'),
path('reset-password/done/', views.GASPasswordResetDoneView.as_view(), name='password_reset_done'),
path('reset-password/', views.GASPasswordResetView.as_view(), name='reset_password'),
path('', views.Index.as_view(), name='index'), path('', views.Index.as_view(), name='index'),
] ]

View File

@ -1,45 +1,72 @@
from django.contrib.auth.views import LoginView, PasswordChangeView from django.contrib.auth.views import (
LoginView,
PasswordChangeView,
PasswordResetConfirmView,
PasswordResetDoneView,
PasswordResetView,
)
from django.shortcuts import resolve_url from django.shortcuts import resolve_url
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views.generic import TemplateView from django.views.generic import TemplateView
from gas import gas_settings
from gas.views import GASMixin from gas.views import GASMixin
from gas import gas_settings
class GASPasswordChangeView(GASMixin, PasswordChangeView):
template_name = "gas/base_form.html"
success_url = reverse_lazy("gas:index")
continue_url = reverse_lazy("gas:change_password")
title = _("Change your password")
success_message = _("Password changed.")
class GASLoginView(LoginView): class Index(GASMixin, TemplateView):
main_menu = "index"
template_name = "gas/index.html"
roles = ("staff",)
class GASContextMixin:
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
css = gas_settings.MEDIA["css"]
javascript = gas_settings.MEDIA["js"]
if gas_settings.EXTRA_MEDIA:
css = css + gas_settings.EXTRA_MEDIA.get("css", [])
javascript = javascript + gas_settings.EXTRA_MEDIA.get("js", [])
ctx.update(
{
"logo_static_url": gas_settings.LOGO,
"css": css,
"js": javascript,
}
)
return ctx
class GASLoginView(GASContextMixin, LoginView):
template_name = "gas/login.html" template_name = "gas/login.html"
def get_success_url(self): def get_success_url(self):
url = self.get_redirect_url() url = self.get_redirect_url()
return url or resolve_url('gas:index') return url or resolve_url("gas:index")
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
css = gas_settings.MEDIA['css']
javascript = gas_settings.MEDIA['js']
if gas_settings.EXTRA_MEDIA:
css = css + gas_settings.EXTRA_MEDIA.get('css', [])
javascript = javascript + gas_settings.EXTRA_MEDIA.get('js', [])
ctx.update({
'logo_static_url': gas_settings.LOGO,
'css': css,
'js': javascript,
})
return ctx
class GASPasswordChangeView(GASMixin, PasswordChangeView): class GASPasswordResetView(GASContextMixin, PasswordResetView):
template_name = 'gas/base_form.html' template_name = "gas/reset.html"
success_url = reverse_lazy('gas:index') email_template_name = "email/password_reset_email.html"
continue_url = reverse_lazy('gas:change_password') success_url = reverse_lazy("gas:password_reset_done")
title = _('Change your password')
success_message = _('Password changed.')
class Index(GASMixin, TemplateView): class GASPasswordResetDoneView(GASContextMixin, PasswordResetDoneView):
main_menu = 'index' template_name = "gas/reset_done.html"
template_name = "gas/index.html"
roles = ('staff',)
class GASPasswordResetConfirmView(GASContextMixin, PasswordResetConfirmView):
template_name = "gas/reset_confirm.html"
success_url = reverse_lazy("gas:password_reset_complete")
class GASPasswordResetCompleteView(GASContextMixin, TemplateView):
template_name = "gas/reset_complete.html"

View File

@ -0,0 +1,14 @@
{% load i18n %}{% autoescape off %}
{% blocktranslate %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktranslate %}
{% translate "Please go to the following page and choose a new password:" %}
{% block reset_link %}
{{ protocol }}://{{ domain }}{% url 'gas:password_reset_confirm' uidb64=uid token=token %}
{% endblock %}
{% translate 'Your username, in case youve forgotten:' %} {{ user.get_username }}
{% translate "Thanks for using our site!" %}
{% blocktranslate %}The {{ site_name }} team{% endblocktranslate %}
{% endautoescape %}

View File

@ -0,0 +1,20 @@
{% load i18n static form_tags %}<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<title>{{ gas_title }} {% trans "Login" %}</title>
{% for cssfile in css %}
<link href="{% static cssfile %}" rel="stylesheet" type="text/css" />
{% endfor %}
</head>
<body id="login">
<p>{% translate 'Forgotten your password? Enter your email address below, and well email instructions for setting a new one.' %}</p>
<form action="." method="POST">{% csrf_token %}
{% form_errors form %}
{% form_field form.email %}
<input type="hidden" name="next" value="{{ next }}">
<button type="submit">{% trans "Reset my password" %}</button>
</form>
</body>
</html>

View File

@ -0,0 +1,14 @@
{% load i18n static form_tags %}<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<title>{{ gas_title }} {% trans "Login" %}</title>
{% for cssfile in css %}
<link href="{% static cssfile %}" rel="stylesheet" type="text/css" />
{% endfor %}
</head>
<body id="login">
<p>{% translate 'Your password was changed.' %}</p>
</body>
</html>

View File

@ -0,0 +1,26 @@
{% load i18n static form_tags %}<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<title>{{ gas_title }} {% trans "Login" %}</title>
{% for cssfile in css %}
<link href="{% static cssfile %}" rel="stylesheet" type="text/css" />
{% endfor %}
</head>
<body id="login">
{% if validlink %}
<p>{% translate "Please enter your new password twice so we can verify you typed it in correctly." %}</p>
<form action="." method="POST">{% csrf_token %}
<input class="hidden" autocomplete="username" value="{{ form.user.get_username }}">
{% form_errors form %}
{% form_field form.new_password1 %}
{% form_field form.new_password2 %}
<input type="hidden" name="next" value="{{ next }}">
<button type="submit">{% trans "Change my password" %}</button>
</form>
{% else %}
<p>{% translate "The password reset link was invalid, possibly because it has already been used. Please request a new password reset." %}</p>
{% endif %}
</body>
</html>

View File

@ -0,0 +1,17 @@
{% load i18n static form_tags %}<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<title>{{ gas_title }} {% trans "Login" %}</title>
{% for cssfile in css %}
<link href="{% static cssfile %}" rel="stylesheet" type="text/css" />
{% endfor %}
</head>
<body id="login">
<p>{% translate 'Weve emailed you instructions for setting your password, if an account exists with the email you entered. You should receive them shortly.' %}</p>
<p>{% translate 'If you dont receive an email, please make sure youve entered the address you registered with, and check your spam folder.' %}</p>
</body>
</html>

View File

@ -3,28 +3,58 @@ from django.urls import reverse
from model_bakery import baker from model_bakery import baker
from gas.gas.core.views import GASLoginView
class GASLoginTestCase(TestCase): class GASLoginTestCase(TestCase):
def test_load(self): def test_load(self):
client = Client() client = Client()
response = client.get(reverse('gas:login')) response = client.get(reverse("gas:login"))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
class IndexTestCase(TestCase): class IndexTestCase(TestCase):
def test_load(self): def test_load(self):
admin_user = baker.make( admin_user = baker.make(
'auth.User', "auth.User",
username='admin', username="admin",
is_superuser=True, is_superuser=True,
) )
client = Client() client = Client()
response = client.get(reverse('gas:index')) response = client.get(reverse("gas:index"))
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
client.force_login(admin_user) client.force_login(admin_user)
response = client.get(reverse('gas:index')) response = client.get(reverse("gas:index"))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
class GASPasswordResetViewTestCase(TestCase):
def test_load(self):
client = Client()
response = client.get(reverse("gas:reset_password"))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "gas/reset.html")
class GASPasswordResetDoneViewTestCase(TestCase):
def test_load(self):
client = Client()
response = client.get(reverse("gas:password_reset_done"))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "gas/reset_done.html")
class GASPasswordResetConfirmViewTestCase(TestCase):
def test_load(self):
client = Client()
response = client.get(reverse("gas:password_reset_confirm", kwargs={"uidb64": "uidb64", "token": "token"}))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "gas/reset_confirm.html")
class GASPasswordResetCompleteViewTestCase(TestCase):
def test_load(self):
client = Client()
response = client.get(reverse("gas:password_reset_complete"))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "gas/reset_complete.html")