forked from ScoDoc/ScoDoc
Améliore formulaires gestion utilisateurs
This commit is contained in:
parent
dcd4d3bcbd
commit
c48c52f7aa
@ -7,7 +7,7 @@ from app.email import send_email
|
||||
def send_password_reset_email(user):
|
||||
token = user.get_reset_password_token()
|
||||
send_email(
|
||||
"[ScoDoc] Reset Your Password",
|
||||
"[ScoDoc] Réinitialisation de votre mot de passe",
|
||||
sender=current_app.config["ADMINS"][0],
|
||||
recipients=[user.email],
|
||||
text_body=render_template("email/reset_password.txt", user=user, token=token),
|
||||
|
@ -53,3 +53,8 @@ class ResetPasswordForm(FlaskForm):
|
||||
_l("Repeat Password"), validators=[DataRequired(), EqualTo("password")]
|
||||
)
|
||||
submit = SubmitField(_l("Request Password Reset"))
|
||||
|
||||
|
||||
class DeactivateUserForm(FlaskForm):
|
||||
submit = SubmitField("Modifier l'utilisateur")
|
||||
cancel = SubmitField(label="Annuler", render_kw={"formnovalidate": True})
|
||||
|
@ -5,7 +5,6 @@
|
||||
|
||||
import base64
|
||||
from datetime import datetime, timedelta
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from time import time
|
||||
@ -115,7 +114,7 @@ class User(UserMixin, db.Model):
|
||||
{"reset_password": self.id, "exp": time() + expires_in},
|
||||
current_app.config["SECRET_KEY"],
|
||||
algorithm="HS256",
|
||||
).decode("utf-8")
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def verify_reset_password_token(token):
|
||||
|
@ -3,7 +3,9 @@
|
||||
auth.routes.py
|
||||
"""
|
||||
|
||||
from flask import render_template, redirect, url_for, current_app, flash, request
|
||||
from app.scodoc.sco_exceptions import ScoValueError
|
||||
from flask import current_app, g, flash, render_template
|
||||
from flask import redirect, url_for, request
|
||||
from flask_login.utils import login_required
|
||||
from werkzeug.urls import url_parse
|
||||
from flask_login import login_user, logout_user, current_user
|
||||
@ -15,12 +17,13 @@ from app.auth.forms import (
|
||||
UserCreationForm,
|
||||
ResetPasswordRequestForm,
|
||||
ResetPasswordForm,
|
||||
DeactivateUserForm,
|
||||
)
|
||||
from app.auth.models import Permission
|
||||
from app.auth.models import User
|
||||
from app.auth.email import send_password_reset_email
|
||||
from app.decorators import admin_required
|
||||
|
||||
from app.decorators import permission_required
|
||||
|
||||
_ = lambda x: x # sans babel
|
||||
_l = _
|
||||
@ -69,13 +72,23 @@ def create_user():
|
||||
|
||||
@bp.route("/reset_password_request", methods=["GET", "POST"])
|
||||
def reset_password_request():
|
||||
"""Form demande renvoi de mot de passe par mail
|
||||
Si l'utilisateur est déjà authentifié, le renvoie simplement sur
|
||||
la page d'accueil.
|
||||
"""
|
||||
if current_user.is_authenticated:
|
||||
return redirect(url_for("scodoc.index"))
|
||||
form = ResetPasswordRequestForm()
|
||||
if form.validate_on_submit():
|
||||
user = User.query.filter_by(email=form.email.data).first()
|
||||
if user:
|
||||
send_password_reset_email(user)
|
||||
users = User.query.filter_by(email=form.email.data).all()
|
||||
if len(users) == 1:
|
||||
send_password_reset_email(users[0])
|
||||
elif len(users) > 1:
|
||||
current_app.logger.info(
|
||||
"reset_password_request: multiple users with email '{}' (ignoring)".format(
|
||||
form.email.data
|
||||
)
|
||||
)
|
||||
else:
|
||||
current_app.logger.info(
|
||||
"reset_password_request: for unkown user '{}'".format(form.email.data)
|
||||
|
@ -255,7 +255,7 @@ def formsemestre_synchro_etuds(
|
||||
url_for("scolar.affectGroups",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
partition_id=partitions[0]["partition_id"]
|
||||
)}">Répartir les groupes de partitions[0]["partition_name"]</a></li>
|
||||
)}">Répartir les groupes de {partitions[0]["partition_name"]}</a></li>
|
||||
"""
|
||||
)
|
||||
|
||||
|
@ -343,11 +343,14 @@ def user_info_page(user_name=None):
|
||||
)
|
||||
if current_user.has_permission(Permission.ScoUsersAdmin, dept):
|
||||
H.append(
|
||||
"""
|
||||
<li><a class="stdlink" href="create_user_form?user_name=%(user_name)s&edit=1">modifier ou désactiver ce compte</a><br/>
|
||||
<em>(pour "supprimer" un utilisateur, le rendre inactif via le formulaire)</em>
|
||||
f"""
|
||||
<li><a class="stdlink" href="{url_for('users.create_user_form', scodoc_dept=g.scodoc_dept,
|
||||
user_name=user.user_name, edit=1)}">modifier ce compte</a>
|
||||
</li>
|
||||
<li><a class="stdlink" href="{url_for('users.toggle_active_user', scodoc_dept=g.scodoc_dept,
|
||||
user_name=user.user_name)
|
||||
}">{"désactiver" if user.active else "activer"} ce compte</a>
|
||||
</li>
|
||||
|
||||
"""
|
||||
% info
|
||||
)
|
||||
|
17
app/templates/auth/toogle_active_user.html
Normal file
17
app/templates/auth/toogle_active_user.html
Normal file
@ -0,0 +1,17 @@
|
||||
{% extends "base.html" %}
|
||||
{% import 'bootstrap/wtf.html' as wtf %}
|
||||
|
||||
{% block app_content %}
|
||||
<h1>{{ "Désactiver" if u.active else "Activer" }} l'utilisateur {{ u.get_nomplogin() }} ?</h1>
|
||||
<div class="help">
|
||||
Dans ScoDoc on ne supprime pas les utilisateurs mais on les rend inactifs:
|
||||
ils n'apparaissent plus dans les listes et ne peuvent plus se connecter.
|
||||
<br />
|
||||
Ces utilisateurs peuvent être réactivés à tout moment.
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
{{ wtf.quick_form(form) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
@ -38,11 +38,13 @@ import re
|
||||
from xml.etree import ElementTree
|
||||
|
||||
import flask
|
||||
from flask import g, url_for
|
||||
from flask import g, url_for, request
|
||||
from flask import redirect, render_template
|
||||
|
||||
from flask_login import current_user
|
||||
|
||||
from app import db
|
||||
from app.auth.forms import DeactivateUserForm
|
||||
from app.auth.models import Permission
|
||||
from app.auth.models import User
|
||||
from app.auth.models import Role
|
||||
@ -210,7 +212,8 @@ def create_user_form(REQUEST, user_name=None, edit=0):
|
||||
"title": "Mot de passe",
|
||||
"input_type": "password",
|
||||
"size": 14,
|
||||
"allow_null": False,
|
||||
"allow_null": True,
|
||||
"explanation": "optionnel, l'utilisateur pourra le saisir avec son mail",
|
||||
},
|
||||
),
|
||||
(
|
||||
@ -219,7 +222,7 @@ def create_user_form(REQUEST, user_name=None, edit=0):
|
||||
"title": "Confirmer mot de passe",
|
||||
"input_type": "password",
|
||||
"size": 14,
|
||||
"allow_null": False,
|
||||
"allow_null": True,
|
||||
},
|
||||
),
|
||||
]
|
||||
@ -237,9 +240,9 @@ def create_user_form(REQUEST, user_name=None, edit=0):
|
||||
{
|
||||
"title": "e-mail",
|
||||
"input_type": "text",
|
||||
"explanation": "vivement recommandé: utilisé pour contacter l'utilisateur",
|
||||
"explanation": "requis, doit fonctionner",
|
||||
"size": 20,
|
||||
"allow_null": True,
|
||||
"allow_null": False,
|
||||
},
|
||||
)
|
||||
]
|
||||
@ -437,14 +440,17 @@ def create_user_form(REQUEST, user_name=None, edit=0):
|
||||
)
|
||||
return "\n".join(H) + msg + "\n" + tf[1] + F
|
||||
# check passwords
|
||||
if vals["passwd"] != vals["passwd2"]:
|
||||
msg = tf_error_message(
|
||||
"""Les deux mots de passes ne correspondent pas !"""
|
||||
)
|
||||
return "\n".join(H) + msg + "\n" + tf[1] + F
|
||||
if not sco_users.is_valid_password(vals["passwd"]):
|
||||
msg = tf_error_message("""Mot de passe trop simple, recommencez !""")
|
||||
return "\n".join(H) + msg + "\n" + tf[1] + F
|
||||
if vals["passwd"]:
|
||||
if vals["passwd"] != vals["passwd2"]:
|
||||
msg = tf_error_message(
|
||||
"""Les deux mots de passes ne correspondent pas !"""
|
||||
)
|
||||
return "\n".join(H) + msg + "\n" + tf[1] + F
|
||||
if not sco_users.is_valid_password(vals["passwd"]):
|
||||
msg = tf_error_message(
|
||||
"""Mot de passe trop simple, recommencez !"""
|
||||
)
|
||||
return "\n".join(H) + msg + "\n" + tf[1] + F
|
||||
if not can_choose_dept:
|
||||
vals["dept"] = auth_dept
|
||||
# ok, go
|
||||
@ -457,8 +463,12 @@ def create_user_form(REQUEST, user_name=None, edit=0):
|
||||
db.session.add(u)
|
||||
db.session.commit()
|
||||
return flask.redirect(
|
||||
"user_info_page?user_name=%s&head_message=Nouvel utilisateur créé"
|
||||
% (user_name)
|
||||
url_for(
|
||||
"users.user_info_page",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
user_name=user_name,
|
||||
head_message="Nouvel utilisateur créé",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@ -611,7 +621,9 @@ def form_change_password(REQUEST, user_name=None):
|
||||
<input type="hidden" value="%(user_name)s" name="user_name">
|
||||
<input type="submit" value="Changer">
|
||||
</p>
|
||||
<p>Vous pouvez aussi: <a class="stdlink" href="reset_password_form?user_name=%(user_name)s">renvoyer un mot de passe aléatoire temporaire par mail à l'utilisateur</a>
|
||||
<p class="help">Note: en ScoDoc 9, les utilisateurs peuvent changer eux-même leur mot de passe
|
||||
en indiquant l'adresse mail associée à leur compte.
|
||||
</p>
|
||||
"""
|
||||
% {"nomplogin": u.get_nomplogin(), "user_name": user_name}
|
||||
)
|
||||
@ -676,3 +688,25 @@ def change_password(user_name, password, password2, REQUEST):
|
||||
% scu.ScoURL()
|
||||
)
|
||||
return html_sco_header.sco_header() + "\n".join(H) + F
|
||||
|
||||
|
||||
@bp.route("/toggle_active_user/<user_name>", methods=["GET", "POST"])
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoUsersAdmin)
|
||||
def toggle_active_user(user_name: str = None):
|
||||
"""Change active status of a user account"""
|
||||
u = User.query.filter_by(user_name=user_name).first()
|
||||
if not u:
|
||||
raise ScoValueError("invalid user_name")
|
||||
form = DeactivateUserForm()
|
||||
if (
|
||||
request.method == "POST" and form.cancel.data
|
||||
): # if cancel button is clicked, the form.cancel.data will be True
|
||||
# flash
|
||||
return redirect(url_for("users.index_html", scodoc_dept=g.scodoc_dept))
|
||||
if form.validate_on_submit():
|
||||
u.active = not u.active
|
||||
db.session.add(u)
|
||||
db.session.commit()
|
||||
return redirect(url_for("users.index_html", scodoc_dept=g.scodoc_dept))
|
||||
return render_template("auth/toogle_active_user.html", form=form, u=u)
|
||||
|
@ -32,7 +32,6 @@ iniconfig==1.1.1
|
||||
isort==5.9.3
|
||||
itsdangerous==2.0.1
|
||||
Jinja2==3.0.1
|
||||
jwt==1.2.0
|
||||
lazy-object-proxy==1.6.0
|
||||
Mako==1.1.4
|
||||
MarkupSafe==2.0.1
|
||||
@ -45,6 +44,7 @@ psycopg2==2.9.1
|
||||
py==1.10.0
|
||||
pycparser==2.20
|
||||
pydot==1.4.2
|
||||
PyJWT==2.1.0
|
||||
pylint==2.9.6
|
||||
pyOpenSSL==20.0.1
|
||||
pyparsing==2.4.7
|
||||
|
Loading…
Reference in New Issue
Block a user