send email for put your password

This commit is contained in:
Cayo Puigdefabregas 2023-10-18 17:30:11 +02:00
parent d9352a1f9f
commit fc226bd5df
17 changed files with 456 additions and 157 deletions

View File

@ -1,4 +1,5 @@
import logging
from smtplib import SMTPException
from django.utils.translation import gettext_lazy as _
from django.views.generic.base import TemplateView
@ -9,6 +10,7 @@ from django.urls import reverse_lazy
from django.contrib import messages
from idhub.models import Membership, Rol, Service, UserRol
from idhub.mixins import AdminView
from idhub.email.views import NotifyActivateUserByEmail
from idhub.admin.forms import (
ProfileForm,
MembershipForm,
@ -121,7 +123,7 @@ class AdminPeopleEditView(AdminPeopleView, UpdateView):
success_url = reverse_lazy('idhub:admin_people_list')
class AdminPeopleRegisterView(People, CreateView):
class AdminPeopleRegisterView(NotifyActivateUserByEmail, People, CreateView):
template_name = "idhub/admin/people_register.html"
subtitle = _('People Register')
icon = 'bi bi-person'
@ -137,6 +139,16 @@ class AdminPeopleRegisterView(People, CreateView):
)
return self.success_url
def form_valid(self, form):
user = form.save()
messages.success(self.request, _('The account is created successfully'))
if user.is_active:
try:
self.send_email(user)
except SMTPException as e:
messages.error(self.request, e)
return super().form_valid(form)
class AdminPeopleMembershipRegisterView(People, CreateView):
template_name = "idhub/admin/people_membership_register.html"

0
idhub/email/__init__.py Normal file
View File

55
idhub/email/views.py Normal file
View File

@ -0,0 +1,55 @@
from django.conf import settings
from django.template import loader
from django.core.mail import EmailMultiAlternatives
from django.contrib.auth.tokens import default_token_generator
from django.contrib.sites.shortcuts import get_current_site
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
class NotifyActivateUserByEmail:
def get_email_context(self, user):
"""
Define a new context with a token for put in a email
when send a email for add a new password
"""
protocol = 'https' if self.request.is_secure() else 'http'
current_site = get_current_site(self.request)
site_name = current_site.name
domain = current_site.domain
context = {
'email': user.email,
'domain': domain,
'site_name': site_name,
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
'user': user,
'token': default_token_generator.make_token(user),
'protocol': protocol,
}
return context
def send_email(self, user):
"""
Send a email when a user is activated.
"""
context = self.get_email_context(user)
subject_template_name = 'idhub/admin/registration/activate_user_subject.txt'
email_template_name = 'idhub/admin/registration/activate_user_email.txt'
html_email_template_name = 'idhub/admin/registration/activate_user_email.html'
subject = loader.render_to_string(subject_template_name, context)
# Email subject *must not* contain newlines
subject = ''.join(subject.splitlines())
body = loader.render_to_string(email_template_name, context)
from_email = settings.DEFAULT_FROM_EMAIL
to_email = user.email
email_message = EmailMultiAlternatives(
subject, body, from_email, [to_email])
html_email = loader.render_to_string(html_email_template_name, context)
email_message.attach_alternative(html_email, 'text/html')
if settings.DEVELOPMENT:
print(to_email)
print(body)
return
email_message.send()

View File

@ -1,162 +1,54 @@
{% extends "auth/login_base.html" %}
{% load i18n static %}
<!doctype html>
<html lang="en">
<head>
{% block head %}
{% block meta %}
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="robots" content="NONE,NOARCHIVE" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="Pangea">
{% endblock %}
<title>{% block title %}{% if title %}{{ title }} {% endif %}IdHub{% endblock %}</title>
{% block login_content %}
<form action="{% url 'idhub:login' %}" role="form" method="post">
{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}" />
<!-- Bootstrap core CSS -->
{% block style %}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href= "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
<link href="{% static "/css/bootstrap.min.css" %}" rel="stylesheet">
<style>
.bd-placeholder-img {
font-size: 1.125rem;
text-anchor: middle;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
@media (min-width: 768px) {
.bd-placeholder-img-lg {
font-size: 3.5rem;
}
}
</style>
<!-- Custom styles for this template -->
<link href="{% static "/css/dashboard.css" %}" rel="stylesheet">
{% endblock %}
{% endblock %}
</head>
<body id="body-login">
<header class="navbar navbar-dark sticky-top bg-grey flex-md-nowrap p-0 shadow">
<div class="navbar-nav navbar-sub-brand">
</div>
<div class="navbar-nav">
</div>
</header>
<div class="container-fluid">
<div class="row">
<main class="col-md-12 bt-5">
{% block messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags|default:'info' }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
{% endfor %}
{% endblock messages %}
<div class="jumbotron vertical-center">
<div id="login-wrapper" class="container" style="width: 430px;">
<div id="login-content" class="rounded">
<div id="login-branding">
{% block branding %}
<h1>
<img class="img-fluid" src="{% static '/images/logo-pangea-monocrome-h.png' %}"
alt="Pangea.org - Internet etic i solidari" />
</h1>
{% endblock %}
</div><!-- /login-branding -->
<div class="mt-5">
<form action="{% url 'idhub:login' %}" role="form" method="post">
{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}" />
<div id="div_id_username"
class="clearfix control-group {% if form.username.errors %}error{% endif %}">
<div class="form-group">
<input type="text" name="username" maxlength="100" autocapitalize="off"
autocorrect="off" class="form-control textinput textInput" id="id_username" required
autofocus placeholder="{{ form.username.label }}"
{% if form.username.value %}value="{{ form.username.value }}" {% endif %}>
{% if form.username.errors %}
<p class="text-error">
{{ form.username.errors|striptags }}
</p>
{% endif %}
</div>
</div>
<div id="div_id_password"
class="clearfix control-group {% if form.password.errors %}error{% endif %}">
<div class="form-group">
<input type="password" name="password" maxlength="100" autocapitalize="off"
autocorrect="off" class="form-control textinput textInput" id="id_password"
placeholder="{{ form.password.label }}" required>
{% if form.password.errors %}
<p class="text-error">
{{ form.password.errors|striptags }}
</p>
{% endif %}
</div>
</div>
{% if form.non_field_errors %}
{% for error in form.non_field_errors %}
<div class="well well-small text-error" style="border: none">{{ error }}</div>
{% endfor %}
{% endif %}
<input name="next" type="hidden" value="{{ success_url }}">
<div class="form-actions-no-box">
<input type="submit" name="submit" value="{% trans 'Log in' %}"
class="btn btn-primary form-control" id="submit-id-submit">
</div>
</form>
</div><!-- /.row-fluid -->
</div>
<!--/#login-content-->
<div id="login-footer">
<a href="#password_reset" data-toggle="modal" data-target="#forgotPasswordModal">{% trans "Forgot your password? Click here to recover" %}</a>
</div>
</div><!-- /#login-wrapper -->
</div><!-- /.jumbotron -->
<!-- Modal -->
<div class="modal fade" id="forgotPasswordModal" tabindex="-1" role="dialog" aria-labelledby="forgotPasswordModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="forgotPasswordModalLabel">{% trans "Forgot your password?" %}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<div id="div_id_username"
class="clearfix control-group {% if form.username.errors %}error{% endif %}">
<div class="form-group">
<input type="text" name="username" maxlength="100" autocapitalize="off"
autocorrect="off" class="form-control textinput textInput" id="id_username" required
autofocus placeholder="{{ form.username.label }}"
{% if form.username.value %}value="{{ form.username.value }}" {% endif %}>
{% if form.username.errors %}
<p class="text-error">
{{ form.username.errors|striptags }}
</p>
{% endif %}
</div>
<div class="modal-body">
{% blocktrans trimmed with support_email="suport@pangea.org" %}
Send an email to <a href="mailto:{{ support_email }}">{{ support_email }}</a> including your username and we will provide instructions.
{% endblocktrans %}
</div>
</div>
</div>
</div>
</main>
</div>
</div>
<script src="/static/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons@4.28.0/dist/feather.min.js" integrity="sha384-uO3SXW5IuS1ZpFPKugNNWqTZRRglnUJK6UAZ/gxOX80nxEkN9NcGZTftn6RzhGWE" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js" integrity="sha384-zNy6FEbO50N+Cg5wap8IKA4M/ZnLJgzc6w2NqACZaK0u0FXfOWRRJOnQtpZun8ha" crossorigin="anonymous"></script>
<script src="/static/js/dashboard.js"></script>
</body>
</html>
<div id="div_id_password"
class="clearfix control-group {% if form.password.errors %}error{% endif %}">
<div class="form-group">
<input type="password" name="password" maxlength="100" autocapitalize="off"
autocorrect="off" class="form-control textinput textInput" id="id_password"
placeholder="{{ form.password.label }}" required>
{% if form.password.errors %}
<p class="text-error">
{{ form.password.errors|striptags }}
</p>
{% endif %}
</div>
</div>
{% if form.non_field_errors %}
{% for error in form.non_field_errors %}
<div class="well well-small text-error" style="border: none">{{ error }}</div>
{% endfor %}
{% endif %}
<input name="next" type="hidden" value="{{ success_url }}">
<div class="form-actions-no-box">
<input type="submit" name="submit" value="{% trans 'Log in' %}"
class="btn btn-primary form-control" id="submit-id-submit">
</div>
</form>
<div id="login-footer">
<a href="{% url 'idhub:password_reset' %}" data-toggle="modal" data-target="#forgotPasswordModal">{% trans "Forgot your password? Click here to recover" %}</a>
</div>
{% endblock %}

View File

@ -0,0 +1,114 @@
{% load i18n static %}
<!doctype html>
<html lang="en">
<head>
{% block head %}
{% block meta %}
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="robots" content="NONE,NOARCHIVE" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="Pangea">
{% endblock %}
<title>{% block title %}{% if title %}{{ title }} {% endif %}IdHub{% endblock %}</title>
<!-- Bootstrap core CSS -->
{% block style %}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href= "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
<link href="{% static "/css/bootstrap.min.css" %}" rel="stylesheet">
<style>
.bd-placeholder-img {
font-size: 1.125rem;
text-anchor: middle;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
@media (min-width: 768px) {
.bd-placeholder-img-lg {
font-size: 3.5rem;
}
}
</style>
<!-- Custom styles for this template -->
<link href="{% static "/css/dashboard.css" %}" rel="stylesheet">
{% endblock %}
{% endblock %}
</head>
<body id="body-login">
<header class="navbar navbar-dark sticky-top bg-grey flex-md-nowrap p-0 shadow">
<div class="navbar-nav navbar-sub-brand">
</div>
<div class="navbar-nav">
</div>
</header>
<div class="container-fluid">
<div class="row">
<main class="col-md-12 bt-5">
{% block messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags|default:'info' }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
{% endfor %}
{% endblock messages %}
<div class="jumbotron vertical-center">
<div id="login-wrapper" class="container" style="width: 430px;">
<div id="login-content" class="rounded">
<div id="login-branding">
{% block branding %}
<h1>
<img class="img-fluid" src="{% static '/images/logo-pangea-monocrome-h.png' %}"
alt="Pangea.org - Internet etic i solidari" />
</h1>
{% endblock %}
</div><!-- /login-branding -->
<div class="mt-5">
{% block login_content %}
{% endblock %}
</div><!-- /.row-fluid -->
</div>
<!--/#login-content-->
</div><!-- /#login-wrapper -->
</div><!-- /.jumbotron -->
<!-- Modal -->
<div class="modal fade" id="forgotPasswordModal" tabindex="-1" role="dialog" aria-labelledby="forgotPasswordModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="forgotPasswordModalLabel">{% trans "Forgot your password?" %}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
{% blocktrans trimmed with support_email="suport@pangea.org" %}
Send an email to <a href="mailto:{{ support_email }}">{{ support_email }}</a> including your username and we will provide instructions.
{% endblocktrans %}
</div>
</div>
</div>
</div>
</main>
</div>
</div>
<script src="/static/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons@4.28.0/dist/feather.min.js" integrity="sha384-uO3SXW5IuS1ZpFPKugNNWqTZRRglnUJK6UAZ/gxOX80nxEkN9NcGZTftn6RzhGWE" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js" integrity="sha384-zNy6FEbO50N+Cg5wap8IKA4M/ZnLJgzc6w2NqACZaK0u0FXfOWRRJOnQtpZun8ha" crossorigin="anonymous"></script>
<script src="/static/js/dashboard.js"></script>
</body>
</html>

View File

@ -0,0 +1,27 @@
{% extends "auth/login_base.html" %}
{% load i18n django_bootstrap5 %}
{% block login_content %}
<div class="well">
<div class="row-fluid">
<h2>{% trans 'Password reset' %}</h2>
<span>{% trans "Forgotten your password? Enter your email address below, and we'll email instructions for setting a new one." %}</span>
</div>
</div>
<div class="well">
<div class="row-fluid">
<div>
<form action="{% url 'idhub:password_reset' %}" role="form" method="post">
{% csrf_token %}
{% bootstrap_form form %}
{% bootstrap_form_errors form type='non_fields' %}
<div class="form-actions-no-box">
<input type="submit" name="submit" value="{% trans 'Reset my password' %}" class="btn btn-primary form-control" id="submit-id-submit">
</div>
</form>
</div>
</div><!-- /.row-fluid -->
</div><!--/.well-->
{% endblock %}

View File

@ -0,0 +1,16 @@
{% extends "auth/login_base.html" %}
{% load i18n %}
{% block login_content %}
<div class="container-fluid" style="margin-top: 30px">
<div class="row-fluid">
<div class="well" style="width: 800px; margin: auto auto 50px auto">
<div class="row-fluid">
<h2>{% trans 'Password reset complete' %}</h2>
<p>{% trans 'Your password has been set. You may go ahead and log in now.' %}</p>
<a href="{% url 'idhub:login' %}">{% trans 'Login' %}</a>
</div>
</div><!--/.well-->
</div><!-- /.row-fluid -->
</div><!-- /.container-fluid -->
{% endblock %}

View File

@ -0,0 +1,36 @@
{% extends "auth/login_base.html" %}
{% load i18n django_bootstrap5 %}
{% block login_content %}
<div class="container-fluid" style="margin-top: 30px">
<div class="row-fluid">
{% if validlink %}
<div class="well" style="width: 600px; margin-left: auto; margin-right: auto">
<h2>{% trans 'Enter new password' %}</h2>
<p>{% trans 'Please enter your new password twice so we can verify you typed it in correctly.' %}</p>
</div><!-- /well -->
<div class="well" style="width: 320px; margin-left: auto; margin-right: auto">
<div class="row-fluid">
<div>
<form role="form" method="post">
{% csrf_token %}
{% bootstrap_form form %}
{% bootstrap_form_errors form type='non_fields' %}
<div class="form-actions-no-box">
<input type="submit" name="submit" value="{% trans 'Change my password' %}" class="btn btn-primary form-control" id="submit-id-submit">
</div>
</form>
</div>
</div><!-- /.row-fluid -->
</div><!--/.well-->
{% else %}
<div class="well" style="width: 800px; margin-left: auto; margin-right: auto">
<h2>{% trans 'Password reset unsuccessful' %}</h2>
<p>{% trans 'The password reset link was invalid, possibly because it has already been used.' %}<br />
{% trans 'Please request a new password reset.' %}</p>
</div><!-- /well -->
{% endif %}
</div><!-- /.row-fluid -->
</div><!-- /.container-fluid -->
{% endblock %}

View File

@ -0,0 +1,15 @@
{% extends "auth/login_base.html" %}
{% load i18n %}
{% block login_content %}
<div class="well">
<div class="row-fluid">
<h2>{% trans 'Password reset sent' %}</h2>
<p>{% trans "We've emailed you instructions for setting your password, if an account exists with the email you entered. You should receive them shortly." %}</p>
<p>{% trans "If you don't receive an email, please make sure you've entered the address you registered with, and check your spam folder." %}</p>
</div><!-- /.row-fluid -->
</div><!--/.well-->
{% endblock %}

View File

@ -0,0 +1,31 @@
{% load i18n %}{% autoescape off %}
{% trans "IdHub" as site %}
<p>
{% blocktrans %}You're receiving this email because your user account at {{site}} has been activated.{% endblocktrans %}
</p>
<p>
{% trans "Your username is:" %} {{ user.username }}
</p>
<p>
{% trans "Please go to the following page and choose a password:" %}
</p>
<p>
{% block reset_link %}
<a href="{{ protocol }}://{{ domain }}{% url 'idhub:password_reset_confirm' uidb64=uid token=token %}">
{{ protocol }}://{{ domain }}{% url 'idhub:password_reset_confirm' uidb64=uid token=token %}
</a>
{% endblock %}
</p>
<p>
{% trans "Thanks for using our site!" %}
</p>
<p>
{% blocktrans %}The {{site}} team{% endblocktrans %}
</p>
{% endautoescape %}

View File

@ -0,0 +1,19 @@
{% load i18n %}{% autoescape off %}
{% trans "Idhub" as site %}
{% blocktrans %}You're receiving this email because your user account at {{site}} has been activated.{% endblocktrans %}
{% trans "Your username is:" %} {{ user.username }}
{% trans "Please go to the following page and choose a password:" %}
{% block reset_link %}
{{ protocol }}://{{ domain }}{% url 'idhub:password_reset_confirm' uidb64=uid token=token %}
{% endblock %}
{% trans "Thanks for using our site!" %}
{% blocktrans %}The {{site}} team{% endblocktrans %}
{% endautoescape %}

View File

@ -0,0 +1,4 @@
{% load i18n %}{% autoescape off %}
{% trans "IdHub" as site %}
{% blocktrans %}User activation on {{site}}{% endblocktrans %}
{% endautoescape %}

View File

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

View File

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

View File

@ -0,0 +1,3 @@
{% load i18n %}{% autoescape off %}
{% blocktrans %}Password reset on {{ site_name }}{% endblocktrans %}
{% endautoescape %}

View File

@ -28,6 +28,35 @@ urlpatterns = [
permanent=False)),
path('login/', LoginView.as_view(), name='login'),
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
path('auth/password_reset/',
auth_views.PasswordResetView.as_view(
template_name='auth/password_reset.html',
email_template_name='auth/registration/password_reset_email.txt',
html_email_template_name='auth/registration/password_reset_email.html',
subject_template_name='auth/registration/password_reset_subject.txt',
success_url=reverse_lazy('auth:password_reset_done')
),
name='password_reset'
),
path('auth/password_reset/done/',
auth_views.PasswordResetDoneView.as_view(
template_name='auth/password_reset_done.html'
),
name='password_reset_done'
),
path('auth/reset/<uidb64>/<token>/',
auth_views.PasswordResetConfirmView.as_view(
template_name='auth/password_reset_confirm.html',
success_url=reverse_lazy('idhub:password_reset_complete')
),
name='password_reset_confirm'
),
path('auth/reset/done/',
auth_views.PasswordResetCompleteView.as_view(
template_name='auth/password_reset_complete.html'
),
name='password_reset_complete'
),
# User
path('user/dashboard/', views_user.UserDashboardView.as_view(),

View File

@ -30,6 +30,7 @@ SECRET_KEY = config('SECRET_KEY')
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = config('DEBUG', default=False, cast=bool)
DEVELOPMENT = config('DEVELOPMENT', default=False, cast=bool)
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default=[], cast=Csv())
@ -173,3 +174,4 @@ MESSAGE_TAGS = {
messages.WARNING: 'alert-warning',
messages.ERROR: 'alert-danger',
}