diff --git a/re2o/settings.py b/re2o/settings.py index e2d7015c..989451dc 100644 --- a/re2o/settings.py +++ b/re2o/settings.py @@ -12,7 +12,7 @@ https://docs.djangoproject.com/en/1.8/ref/settings/ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) import os -from .settings_local import SECRET_KEY, DATABASES, DEBUG, ALLOWED_HOSTS, ASSO_NAME, ASSO_ADDRESS_LINE1, ASSO_ADDRESS_LINE2, ASSO_SIRET, ASSO_EMAIL, ASSO_PHONE, LOGO_PATH, services_urls +from .settings_local import SECRET_KEY, DATABASES, DEBUG, ALLOWED_HOSTS, ASSO_NAME, ASSO_ADDRESS_LINE1, ASSO_ADDRESS_LINE2, ASSO_SIRET, ASSO_EMAIL, ASSO_PHONE, LOGO_PATH, services_urls, REQ_EXPIRE_HRS, REQ_EXPIRE_STR, EMAIL_FROM, SITE_NAME BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) diff --git a/users/admin.py b/users/admin.py index 47545a86..884e7200 100644 --- a/users/admin.py +++ b/users/admin.py @@ -2,7 +2,7 @@ from django.contrib import admin from django.contrib.auth.models import Group from django.contrib.auth.admin import UserAdmin as BaseUserAdmin -from .models import User, School, Right, ListRight, Ban, Whitelist +from .models import User, School, Right, ListRight, Ban, Whitelist, Request from .forms import UserChangeForm, UserCreationForm @@ -29,6 +29,8 @@ class ListRightAdmin(admin.ModelAdmin): class RightAdmin(admin.ModelAdmin): list_display = ('user', 'right') +class RequestAdmin(admin.ModelAdmin): + list_display = ('user', 'type', 'created_at', 'expires_at') class BanAdmin(admin.ModelAdmin): list_display = ('user', 'raison', 'date_start', 'date_end') @@ -71,6 +73,7 @@ admin.site.register(Right, RightAdmin) admin.site.register(ListRight, ListRightAdmin) admin.site.register(Ban, BanAdmin) admin.site.register(Whitelist, WhitelistAdmin) +admin.site.register(Request, RequestAdmin) # Now register the new UserAdmin... admin.site.unregister(User) admin.site.register(User, UserAdmin) diff --git a/users/migrations/0020_request.py b/users/migrations/0020_request.py new file mode 100644 index 00000000..c824a117 --- /dev/null +++ b/users/migrations/0020_request.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +from django.conf import settings + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0019_auto_20160708_1633'), + ] + + operations = [ + migrations.CreateModel( + name='Request', + fields=[ + ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)), + ('type', models.CharField(choices=[('PW', 'Mot de passe'), ('EM', 'Email')], max_length=2)), + ('token', models.CharField(max_length=32)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('expires_at', models.DateTimeField()), + ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=django.db.models.deletion.PROTECT)), + ], + ), + ] diff --git a/users/models.py b/users/models.py index e0647d15..db82dc1e 100644 --- a/users/models.py +++ b/users/models.py @@ -3,8 +3,9 @@ from django.db.models import Q from django.forms import ModelForm, Form from django import forms -from re2o.settings import RIGHTS_LINK -import re +from re2o.settings import RIGHTS_LINK, REQ_EXPIRE_HRS +import re, uuid +import datetime from django.utils import timezone from django.contrib.auth.models import AbstractBaseUser, BaseUserManager @@ -265,6 +266,27 @@ class Whitelist(models.Model): def __str__(self): return str(self.user) + ' ' + str(self.raison) +class Request(models.Model): + PASSWD = 'PW' + EMAIL = 'EM' + TYPE_CHOICES = ( + (PASSWD, 'Mot de passe'), + (EMAIL, 'Email'), + ) + type = models.CharField(max_length=2, choices=TYPE_CHOICES) + token = models.CharField(max_length=32) + user = models.ForeignKey('User', on_delete=models.PROTECT) + created_at = models.DateTimeField(auto_now_add=True, editable=False) + expires_at = models.DateTimeField() + + def save(self): + if not self.expires_at: + self.expires_at = timezone.now() \ + + datetime.timedelta(hours=REQ_EXPIRE_HRS) + if not self.token: + self.token = str(uuid.uuid4()).replace('-', '') # remove hyphens + super(Request, self).save() + class BaseInfoForm(ModelForm): def __init__(self, *args, **kwargs): super(BaseInfoForm, self).__init__(*args, **kwargs) diff --git a/users/templates/users/email_passwd_request b/users/templates/users/email_passwd_request new file mode 100644 index 00000000..b698ebe1 --- /dev/null +++ b/users/templates/users/email_passwd_request @@ -0,0 +1,15 @@ +Bonjour {{ name }}, + +Vous trouverez ci-dessous une url permetant d'initialiser ou de reinitialiser votre +compte {{ site_name }}. Celui-ci vous permet de gérer l'ensemble de vos équipements +connectés, votre compte, vos factures, et tous les services proposés sur le réseau. + + {{ url }} + +Contactez les administrateurs si vous n'êtes pas à l'origine de cette requête. + +Ce lien expirera dans {{ expire_in }}. + +Cordialement, + +L'équipe de {{ asso }} {{ asso_mail }}. diff --git a/users/urls.py b/users/urls.py index 58c910fb..babdcb7e 100644 --- a/users/urls.py +++ b/users/urls.py @@ -21,6 +21,7 @@ urlpatterns = [ url(r'^index_white/$', views.index_white, name='index-white'), url(r'^index_school/$', views.index_school, name='index-school'), url(r'^mon_profil/$', views.mon_profil, name='mon-profil'), + url(r'^process/(?P[a-z0-9]{32})/$', views.process, name='process'), url(r'^$', views.index, name='index'), ] diff --git a/users/views.py b/users/views.py index 4655ca60..7b245782 100644 --- a/users/views.py +++ b/users/views.py @@ -1,16 +1,18 @@ # App de gestion des users pour re2o # Goulven Kermarec, Gabriel Détraz # Gplv2 -from django.shortcuts import render_to_response, render, redirect +from django.shortcuts import render_to_response, get_object_or_404, render, redirect from django.core.context_processors import csrf -from django.template import RequestContext +from django.template import Context, RequestContext, loader from django.contrib import messages from django.contrib.auth.decorators import login_required, permission_required from django.db.models import Max, ProtectedError from django.db import IntegrityError +from django.core.mail import send_mail from django.utils import timezone +from django.core.urlresolvers import reverse -from users.models import User, Right, Ban, Whitelist, School +from users.models import User, Right, Ban, Whitelist, School, Request from users.models import DelRightForm, BanForm, WhitelistForm, DelSchoolForm from users.models import InfoForm, BaseInfoForm, StateForm, RightForm, SchoolForm from cotisations.models import Facture @@ -19,7 +21,7 @@ from users.forms import PassForm from machines.views import unassign_ips, assign_ips from re2o.login import hashNT - +from re2o.settings import REQ_EXPIRE_STR, EMAIL_FROM, ASSO_NAME, ASSO_EMAIL, SITE_NAME def archive(user): """ Archive un utilisateur """ @@ -41,14 +43,49 @@ def form(ctx, template, request): context_instance=RequestContext(request) ) +def password_change_action(u_form, user, request, req=False): + """ Fonction qui effectue le changeemnt de mdp bdd""" + if u_form.cleaned_data['passwd1'] != u_form.cleaned_data['passwd2']: + messages.error(request, "Les 2 mots de passe différent") + return form({'userform': u_form}, 'users/user.html', request) + user.set_password(u_form.cleaned_data['passwd1']) + user.pwd_ntlm = hashNT(u_form.cleaned_data['passwd1']) + user.save() + messages.success(request, "Le mot de passe a changé") + if req: + req.delete() + return redirect("/") + return redirect("/users/profil/" + str(user.id)) + +def reset_passwd_mail(req, request): + t = loader.get_template('users/email_passwd_request') + c = Context({ + 'name': str(req.user.name) + ' ' + str(req.user.surname), + 'asso': ASSO_NAME, + 'asso_mail': ASSO_EMAIL, + 'site_name': SITE_NAME, + 'url': request.build_absolute_uri( + reverse('users:process', kwargs={'token': req.token})), + 'expire_in': REQ_EXPIRE_STR, + }) + send_mail('Changement de mot de passe', t.render(c), + EMAIL_FROM, [req.user.email], fail_silently=False) + return + @login_required @permission_required('cableur') def new_user(request): user = InfoForm(request.POST or None) if user.is_valid(): + user = user.save(commit=False) user.save() - messages.success(request, "L'utilisateur a été crée") - return redirect("/users/") + req = Request() + req.type = Request.PASSWD + req.user = user + req.save() + reset_passwd_mail(req, request) + messages.success(request, "L'utilisateur %s a été crée, un mail pour l'initialisation du mot de passe a été envoyé" % user.pseudo) + redirect("/users/profil/" + user.id) return form({'userform': user}, 'users/user.html', request) @login_required @@ -106,14 +143,7 @@ def password(request, userid): return redirect("/users/profil/" + str(request.user.id)) u_form = PassForm(request.POST or None) if u_form.is_valid(): - if u_form.cleaned_data['passwd1'] != u_form.cleaned_data['passwd2']: - messages.error(request, "Les 2 mots de passe différent") - return form({'userform': u_form}, 'users/user.html', request) - user.set_password(u_form.cleaned_data['passwd1']) - user.pwd_ntlm = hashNT(u_form.cleaned_data['passwd1']) - user.save() - messages.success(request, "Le mot de passe a changé") - return redirect("/users/profil/" + userid) + return password_change_action(u_form, user, request) return form({'userform': u_form}, 'users/user.html', request) @login_required @@ -322,3 +352,20 @@ def profil(request, userid): } ) +def process(request, token): + valid_reqs = Request.objects.filter(expires_at__gt=timezone.now()) + req = get_object_or_404(valid_reqs, token=token) + + if req.type == Request.PASSWD: + return process_passwd(request, req) + elif req.type == Request.EMAIL: + return process_email(request, req=req) + else: + return error(request, 'Entrée incorrecte, contactez un admin') + +def process_passwd(request, req): + u_form = PassForm(request.POST or None) + user = req.user + if u_form.is_valid(): + return password_change_action(u_form, user, request, req=req) + return form({'userform': u_form}, 'users/user.html', request)