Rajoute une page pour créer plus facilement des comptes avec mdp

This commit is contained in:
Tom Hubrecht 2021-04-26 17:54:07 +02:00
parent c73f8a5943
commit c97f57912b
11 changed files with 108 additions and 19 deletions

View file

@ -94,7 +94,7 @@ class DeleteVoteForm(forms.Form):
super().__init__(**kwargs) super().__init__(**kwargs)
if voter is not None: if voter is not None:
self.fields["delete"].label = _("Supprimer le vote de {} ({}) ?").format( self.fields["delete"].label = _("Supprimer le vote de {} ({}) ?").format(
voter.full_name, voter.get_username() voter.full_name, voter.base_username
) )
delete = forms.ChoiceField( delete = forms.ChoiceField(

View file

@ -208,7 +208,8 @@ class User(AbstractUser):
) )
full_name = models.CharField(_("Nom et Prénom"), max_length=150, blank=True) full_name = models.CharField(_("Nom et Prénom"), max_length=150, blank=True)
def get_username(self): @property
def base_username(self):
return "__".join(self.username.split("__")[1:]) return "__".join(self.username.split("__")[1:])
def can_vote(self, request, election): def can_vote(self, request, election):

View file

@ -62,7 +62,7 @@
{% if election.restricted %} {% if election.restricted %}
{% for v in election.registered_voters.all %} {% for v in election.registered_voters.all %}
<tr> <tr>
<td>{{ v.full_name }} ({{ v.get_username }})</td> <td>{{ v.full_name }} ({{ v.base_username }})</td>
<td class="has-text-centered"> <td class="has-text-centered">
<span class="icon"> <span class="icon">
{% if v in voters %} {% if v in voters %}
@ -77,7 +77,7 @@
{% else %} {% else %}
{% for v in voters %} {% for v in voters %}
<tr id="v_{{ forloop.counter }}"> <tr id="v_{{ forloop.counter }}">
<td>{{ v.full_name }} ({{ v.get_username }})</td> <td>{{ v.full_name }} ({{ v.base_username }})</td>
<td class="has-text-centered"> <td class="has-text-centered">
<span class="icon"> <span class="icon">
<i class="fas fa-check"></i> <i class="fas fa-check"></i>

View file

@ -110,7 +110,7 @@
<tbody> <tbody>
{% for v in voters %} {% for v in voters %}
<tr> <tr>
<td>{{ v.get_username }}</td> <td>{{ v.base_username }}</td>
<td>{{ v.full_name }}</td> <td>{{ v.full_name }}</td>
<td>{{ v.email }}</td> <td>{{ v.email }}</td>
</tr> </tr>

View file

@ -1,6 +1,5 @@
import csv import csv
import io import io
import random
import networkx as nx import networkx as nx
import numpy as np import numpy as np
@ -14,6 +13,8 @@ from django.template.loader import render_to_string
from django.urls import reverse from django.urls import reverse
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from shared.auth.utils import generate_password
# ############################################################################# # #############################################################################
# Fonctions universelles # Fonctions universelles
# ############################################################################# # #############################################################################
@ -389,16 +390,6 @@ def check_csv(csv_file):
return errors return errors
def generate_password():
random.seed()
alphabet = "abcdefghjkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789"
password = ""
for i in range(15):
password += random.choice(alphabet)
return password
def send_mail(election, mail_form): def send_mail(election, mail_form):
"""Envoie le mail d'annonce de l'élection avec identifiants et mot de passe """Envoie le mail d'annonce de l'élection avec identifiants et mot de passe
aux votant·e·s, le mdp est généré en même temps que le mail est envoyé. aux votant·e·s, le mdp est généré en même temps que le mail est envoyé.
@ -418,7 +409,7 @@ def send_mail(election, mail_form):
body=mail_form.cleaned_data["message"].format( body=mail_form.cleaned_data["message"].format(
full_name=v.full_name, full_name=v.full_name,
election_url=url, election_url=url,
username=v.get_username(), username=v.base_username,
password=password, password=password,
), ),
to=[v.email], to=[v.email],

View file

@ -2,6 +2,7 @@ from django import forms
from django.contrib.auth import authenticate from django.contrib.auth import authenticate
from django.contrib.auth import forms as auth_forms from django.contrib.auth import forms as auth_forms
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.core.validators import validate_email
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
UserModel = get_user_model() UserModel = get_user_model()
@ -61,3 +62,23 @@ class PwdResetForm(auth_forms.PasswordResetForm):
def get_users(self, email): def get_users(self, email):
users = super().get_users(email) users = super().get_users(email)
return (u for u in users if u.username.split("__")[0] == "pwd") return (u for u in users if u.username.split("__")[0] == "pwd")
class PwdUserForm(forms.ModelForm):
"""
Allows for the creation of a Password Account given the email, base username and full name.
"""
email = forms.EmailField(
label=_("Email"), required=True, validators=[validate_email]
)
def clean(self):
# On rajoute le préfixe signifiant qu'on crée un compte avec mot de passe
cleaned_data = super().clean()
cleaned_data["username"] = "pwd__" + cleaned_data["username"]
return cleaned_data
class Meta:
model = UserModel
fields = ["username", "full_name", "email"]

View file

@ -8,4 +8,5 @@ urlpatterns = [
views.ElectionLoginView.as_view(), views.ElectionLoginView.as_view(),
name="auth.election", name="auth.election",
), ),
path("pwd-create", views.CreatePwdAccount.as_view(), name="auth.create-account"),
] ]

15
shared/auth/utils.py Normal file
View file

@ -0,0 +1,15 @@
import random
# #############################################################################
# Fonctions universelles
# #############################################################################
def generate_password(size=15):
random.seed()
alphabet = "abcdefghjkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789"
password = ""
for i in range(size):
password += random.choice(alphabet)
return password

View file

@ -1,6 +1,15 @@
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth import get_user_model
from django.contrib.auth import views as auth_views from django.contrib.auth import views as auth_views
from django.contrib.auth.hashers import make_password
from django.urls import reverse_lazy
from django.utils.decorators import method_decorator
from django.views.generic.edit import CreateView
from .forms import ElectionAuthForm from .forms import ElectionAuthForm, PwdUserForm
from .utils import generate_password
User = get_user_model()
# ############################################################################# # #############################################################################
# Election Specific Login # Election Specific Login
@ -17,3 +26,23 @@ class ElectionLoginView(auth_views.LoginView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
kwargs.update({"election_id": self.kwargs.get("election_id")}) kwargs.update({"election_id": self.kwargs.get("election_id")})
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
# #############################################################################
# Creation of Password Accounts
# #############################################################################
@method_decorator(staff_member_required, name="dispatch")
class CreatePwdAccount(CreateView):
model = User
form_class = PwdUserForm
template_name = "auth/create-user.html"
success_url = reverse_lazy("auth.create-account")
def form_valid(self, form):
# On enregistre un mot de passe aléatoire
form.instance.password = make_password(generate_password(32))
# On envoie un mail pour réinitialiser le mot de passe
return super().form_valid(form)

View file

@ -0,0 +1,31 @@
{% extends "base.html" %}
{% load i18n %}
{% block content %}
<h1 class="title">{% trans "Créer un compte avec mot de passe" %}</h1>
<hr>
<div class="columns is-centered">
<div class="column is-two-thirds">
<form action="" method="post">
{% csrf_token %}
{% include "forms/form.html" with errors=True %}
<div class="field is-grouped is-centered">
<div class="control is-expanded">
<button class="button is-fullwidth is-outlined is-primary is-light">
<span class="icon">
<i class="fas fa-check"></i>
</span>
<span>{% trans "Enregistrer" %}</span>
</button>
</div>
</div>
</form>
</div>
</div>
{% endblock %}

View file

@ -122,7 +122,7 @@
<div class="level is-mobile"> <div class="level is-mobile">
<div class="level-item"> <div class="level-item">
<div class="tag"> <div class="tag">
{% blocktrans with name=user.get_username connection=user.connection_method %}Connecté·e en tant que {{ name }} par {{ connection }}{% endblocktrans %} {% blocktrans with name=user.base_username connection=user.connection_method %}Connecté·e en tant que {{ name }} par {{ connection }}{% endblocktrans %}
</div> </div>
</div> </div>