Page info utilisateur
This commit is contained in:
parent
f991ffdca5
commit
d40a2b43cd
@ -32,7 +32,7 @@ class User(UserMixin, db.Model):
|
||||
nom = db.Column(db.String(64))
|
||||
prenom = db.Column(db.String(64))
|
||||
dept = db.Column(db.String(32), index=True)
|
||||
is_old = db.Column(db.Boolean, default=False, index=True)
|
||||
active = db.Column(db.Boolean, default=True, index=True)
|
||||
|
||||
password_hash = db.Column(db.String(128))
|
||||
last_seen = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
@ -48,6 +48,7 @@ class User(UserMixin, db.Model):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.roles = []
|
||||
self.user_roles = []
|
||||
super(User, self).__init__(**kwargs)
|
||||
if (
|
||||
not self.roles
|
||||
@ -78,6 +79,8 @@ class User(UserMixin, db.Model):
|
||||
"""Check given password vs current one.
|
||||
Returns `True` if the password matched, `False` otherwise.
|
||||
"""
|
||||
if not self.active: # inactived users can't login
|
||||
return False
|
||||
if not self.password_hash: # user without password can't login
|
||||
return False
|
||||
return check_password_hash(self.password_hash, password)
|
||||
@ -99,30 +102,29 @@ class User(UserMixin, db.Model):
|
||||
return
|
||||
return User.query.get(id)
|
||||
|
||||
def to_dict(self, include_email=False):
|
||||
def to_dict(self, include_email=True):
|
||||
data = {
|
||||
"date_expiration": self.date_expiration.isoformat() + "Z"
|
||||
if self.date_expiration
|
||||
else None,
|
||||
else "",
|
||||
"date_modif_passwd": self.date_modif_passwd.isoformat() + "Z"
|
||||
if self.date_modif_passwd
|
||||
else None,
|
||||
else "",
|
||||
"date_created": self.date_created.isoformat() + "Z"
|
||||
if self.date_created
|
||||
else None,
|
||||
"dept": self.dept,
|
||||
else "",
|
||||
"dept": (self.dept or "").encode("utf-8"), # sco8
|
||||
"id": self.id,
|
||||
"is_old": self.is_old,
|
||||
"status_txt": "(ancien)" if self.is_old else "",
|
||||
"active": self.active,
|
||||
"status_txt": "actif" if self.active else "fermé",
|
||||
"last_seen": self.last_seen.isoformat() + "Z",
|
||||
"nom": self.nom,
|
||||
"prenom": self.prenom,
|
||||
"roles": self.roles,
|
||||
"nom": (self.nom or "").encode("utf-8"), # sco8
|
||||
"prenom": (self.prenom or "").encode("utf-8"), # sco8
|
||||
"roles_string": self.get_roles_string(),
|
||||
"user_name": self.user_name,
|
||||
"user_name": self.user_name.encode("utf-8"), # sco8
|
||||
}
|
||||
if include_email:
|
||||
data["email"] = self.email
|
||||
data["email"] = self.email or ""
|
||||
return data
|
||||
|
||||
def from_dict(self, data, new_user=False):
|
||||
@ -154,12 +156,14 @@ class User(UserMixin, db.Model):
|
||||
# Permissions management:
|
||||
def has_permission(self, perm, dept=False):
|
||||
"""Check if user has permission `perm` in given `dept`.
|
||||
Emulate Zope `has_permission``
|
||||
Similar to Zope ScoDoc7 `has_permission``
|
||||
|
||||
Args:
|
||||
perm: integer, one of the value defined in Permission class.
|
||||
dept: dept id (eg 'RT')
|
||||
"""
|
||||
if not self.active:
|
||||
return False
|
||||
if dept is False:
|
||||
dept = g.scodoc_dept
|
||||
# les role liés à ce département, et les roles avec dept=None (super-admin)
|
||||
@ -201,7 +205,7 @@ class User(UserMixin, db.Model):
|
||||
return ", ".join("{r.role.name}{r.dept}".format(r=r) for r in self.user_roles)
|
||||
|
||||
def is_administrator(self):
|
||||
return self.has_permission(Permission.ScoSuperAdmin, None)
|
||||
return self.active and self.has_permission(Permission.ScoSuperAdmin, None)
|
||||
|
||||
|
||||
class AnonymousUser(AnonymousUserMixin):
|
||||
|
@ -142,7 +142,7 @@ def scodoc7func(context):
|
||||
return func(*args, **kwargs)
|
||||
#
|
||||
if "scodoc_dept" in kwargs:
|
||||
g.scodoc_dept = kwargs["scodoc_dept"]
|
||||
g.scodoc_dept = kwargs["scodoc_dept"].encode("utf-8") # sco8
|
||||
del kwargs["scodoc_dept"]
|
||||
elif not hasattr(g, "scodoc_dept"):
|
||||
g.scodoc_dept = None
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -334,7 +334,7 @@ def sco_header(
|
||||
# div pour affichage messages temporaires
|
||||
H.append('<div id="sco_msg" class="head_message"></div>')
|
||||
#
|
||||
return "".join(H)
|
||||
return scu.sco8_join(H, sep="")
|
||||
|
||||
|
||||
def sco_footer(context, REQUEST=None):
|
||||
|
@ -42,11 +42,12 @@ def sidebar_common(context, REQUEST=None):
|
||||
"UsersURL": scu.UsersURL(),
|
||||
"NotesURL": scu.NotesURL(),
|
||||
"AbsencesURL": scu.AbsencesURL(),
|
||||
# XXX TODO "LogoutURL" : ,
|
||||
"authuser": str(authuser),
|
||||
}
|
||||
H = [
|
||||
'<a class="scodoc_title" href="about">ScoDoc</a>',
|
||||
'<div id="authuser"><a id="authuserlink" href="%(ScoURL)s/Users/userinfo">%(authuser)s</a><br/><a id="deconnectlink" href="%(ScoURL)s/acl_users/logout">déconnexion</a></div>'
|
||||
'<div id="authuser"><a id="authuserlink" href="%(ScoURL)s/Users/user_info_page">%(authuser)s</a><br/><a id="deconnectlink" href="%(ScoURL)s/acl_users/logout">déconnexion</a></div>'
|
||||
% params,
|
||||
sidebar_dept(context, REQUEST),
|
||||
"""<h2 class="insidebar">Scolarité</h2>
|
||||
|
@ -50,8 +50,6 @@ class Permission:
|
||||
def init_permissions():
|
||||
for (perm, symbol, description) in _SCO_PERMISSIONS:
|
||||
setattr(Permission, symbol, perm)
|
||||
# Crée aussi les attributs dans le module (ScoDoc7 compat)
|
||||
globals()[symbol] = perm
|
||||
Permission.description[symbol] = description
|
||||
Permission.NBITS = len(_SCO_PERMISSIONS)
|
||||
|
||||
|
@ -30,12 +30,12 @@
|
||||
|
||||
# Anciennement ZScoUsers.py, fonctions de gestion des données réécrite avec flask/SQLAlchemy
|
||||
|
||||
import string
|
||||
import re
|
||||
import time
|
||||
import md5
|
||||
import base64
|
||||
import jaxml
|
||||
import md5
|
||||
import re
|
||||
import string
|
||||
import time
|
||||
|
||||
from flask import current_app, url_for, g
|
||||
from flask_login import current_user
|
||||
@ -94,10 +94,10 @@ def is_valid_password(cleartxt):
|
||||
# XXX TODO supprimer ndb.GetUsersDBConnexion
|
||||
|
||||
|
||||
def index_html(context, REQUEST, all_depts=False, with_olds=False, format="html"):
|
||||
def index_html(context, REQUEST, all_depts=False, with_inactives=False, format="html"):
|
||||
"gestion utilisateurs..."
|
||||
all_depts = int(all_depts)
|
||||
with_olds = int(with_olds)
|
||||
with_inactives = int(with_inactives)
|
||||
|
||||
H = [html_sco_header.html_sem_header(context, REQUEST, "Gestion des utilisateurs")]
|
||||
H.append("<h2>Gestion des utilisateurs</h2>")
|
||||
@ -121,14 +121,14 @@ def index_html(context, REQUEST, all_depts=False, with_olds=False, format="html"
|
||||
checked = "checked"
|
||||
else:
|
||||
checked = ""
|
||||
if with_olds:
|
||||
if with_inactives:
|
||||
olds_checked = "checked"
|
||||
else:
|
||||
olds_checked = ""
|
||||
H.append(
|
||||
"""<p><form name="f" action="%s" method="get">
|
||||
<input type="checkbox" name="all_depts" value="1" onchange="document.f.submit();" %s>Tous les départements</input>
|
||||
<input type="checkbox" name="with_olds" value="1" onchange="document.f.submit();" %s>Avec anciens utilisateurs</input>
|
||||
<input type="checkbox" name="with_inactives" value="1" onchange="document.f.submit();" %s>Avec anciens utilisateurs</input>
|
||||
</form></p>"""
|
||||
% (REQUEST.URL0, checked, olds_checked)
|
||||
)
|
||||
@ -137,7 +137,7 @@ def index_html(context, REQUEST, all_depts=False, with_olds=False, format="html"
|
||||
context,
|
||||
g.scodoc_dept,
|
||||
all_depts=all_depts,
|
||||
with_olds=with_olds,
|
||||
with_inactives=with_inactives,
|
||||
format=format,
|
||||
REQUEST=REQUEST,
|
||||
with_links=current_user.has_permission(Permission.ScoUsersAdmin, g.scodoc_dept),
|
||||
@ -154,19 +154,19 @@ def list_users(
|
||||
context,
|
||||
dept,
|
||||
all_depts=False, # tous les departements
|
||||
with_olds=False, # inclut les anciens utilisateurs (status "old")
|
||||
with_inactives=False, # inclut les anciens utilisateurs (status "old")
|
||||
format="html",
|
||||
with_links=True,
|
||||
REQUEST=None,
|
||||
):
|
||||
"List users, returns a table in the specified format"
|
||||
if dept and not all_depts:
|
||||
users = get_user_list(dept=dept, with_olds=with_olds)
|
||||
users = get_user_list(dept=dept, with_inactives=with_inactives)
|
||||
comm = "dept. %s" % dept.encode(scu.SCO_ENCODING) # sco8
|
||||
else:
|
||||
r = get_user_list(with_olds=with_olds)
|
||||
r = get_user_list(with_inactives=with_inactives)
|
||||
comm = "tous"
|
||||
if with_olds:
|
||||
if with_inactives:
|
||||
comm += ", avec anciens"
|
||||
comm = "(" + comm + ")"
|
||||
# -- Add some information and links:
|
||||
@ -180,7 +180,7 @@ def list_users(
|
||||
# Add links
|
||||
if with_links and can_modify:
|
||||
target = url_for(
|
||||
"users.userinfo", scodoc_dept=g.scodoc_dept, user_name=u.user_name
|
||||
"users.user_info_page", scodoc_dept=dept, user_name=u.user_name
|
||||
).encode(
|
||||
scu.SCO_ENCODING
|
||||
) # sco8
|
||||
@ -237,7 +237,7 @@ def list_users(
|
||||
)
|
||||
|
||||
|
||||
def get_user_list(dept=None, with_olds=False):
|
||||
def get_user_list(dept=None, with_inactives=False):
|
||||
"""Returns list of users.
|
||||
If dept, select users from this dept,
|
||||
else return all users.
|
||||
@ -246,10 +246,170 @@ def get_user_list(dept=None, with_olds=False):
|
||||
q = User.query
|
||||
if dept is not None:
|
||||
q = q.filter_by(dept=dept)
|
||||
if not with_olds:
|
||||
q = q.filter_by(is_old=False)
|
||||
return q.all()
|
||||
if not with_inactives:
|
||||
q = q.filter_by(active=True)
|
||||
return q.order_by(User.nom, User.user_name).all()
|
||||
|
||||
|
||||
# def get_user_infos(user_list):
|
||||
# return [ user_info(u) for u in user_list ]
|
||||
def _user_list(user_name):
|
||||
"return user as a dict"
|
||||
u = User.query.filter_by(user_name=user_name).first()
|
||||
if u:
|
||||
return u.to_dict()
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def user_info(user_name=None, user=None):
|
||||
"""Donne infos sur l'utilisateur (qui peut ne pas etre dans notre base).
|
||||
Si user_name est specifie, interroge la BD. Sinon, user doit etre un dict.
|
||||
"""
|
||||
if user_name:
|
||||
info = _user_list(user_name)
|
||||
else:
|
||||
info = [user.copy()]
|
||||
user_name = user["user_name"]
|
||||
|
||||
if not info:
|
||||
# special case: user is not in our database
|
||||
return {
|
||||
"user_name": user_name,
|
||||
"nom": user_name,
|
||||
"prenom": "",
|
||||
"email": "",
|
||||
"dept": "",
|
||||
"nomprenom": user_name,
|
||||
"prenomnom": user_name,
|
||||
"prenom_fmt": "",
|
||||
"nom_fmt": user_name,
|
||||
"nomcomplet": user_name,
|
||||
"nomplogin": user_name,
|
||||
"nomnoacc": scu.suppress_accents(user_name),
|
||||
"passwd_temp": 0,
|
||||
"status": "",
|
||||
"date_expiration": None,
|
||||
}
|
||||
else:
|
||||
# Ensure we never publish password hash
|
||||
if "password_hash" in info:
|
||||
del info["password_hash"]
|
||||
#
|
||||
if info["prenom"]:
|
||||
p = format_prenom(info["prenom"])
|
||||
else:
|
||||
p = ""
|
||||
if info["nom"]:
|
||||
n = format_nom(
|
||||
info["nom"], uppercase=False
|
||||
) # strcapitalize(strlower(info['nom']))
|
||||
else:
|
||||
n = user_name
|
||||
|
||||
prenom_abbrv = scu.abbrev_prenom(p)
|
||||
# nomprenom est le nom capitalisé suivi de l'initiale du prénom
|
||||
info["nomprenom"] = (n + " " + prenom_abbrv).strip()
|
||||
# prenomnom est l'initiale du prénom suivie du nom
|
||||
info["prenomnom"] = (prenom_abbrv + " " + n).strip()
|
||||
# nom_fmt et prenom_fmt: minuscule capitalisé
|
||||
info["nom_fmt"] = n
|
||||
info["prenom_fmt"] = sco_etud.format_prenom(p)
|
||||
# nomcomplet est le prenom et le nom complets
|
||||
info["nomcomplet"] = info["prenom_fmt"] + " " + info["nom_fmt"]
|
||||
# nomplogin est le nom en majuscules suivi du prénom et du login
|
||||
# e.g. Dupont Pierre (dupont)
|
||||
info["nomplogin"] = "%s %s (%s)" % (scu.strupper(n), p, info["user_name"])
|
||||
# nomnoacc est le nom en minuscules sans accents
|
||||
info["nomnoacc"] = scu.suppress_accents(scu.strlower(info["nom"]))
|
||||
|
||||
return info
|
||||
|
||||
|
||||
def user_info_page(context, user_name=None, REQUEST=None):
|
||||
"""Display page of info about given user.
|
||||
If user_name not specified, user current_user
|
||||
"""
|
||||
# peut on divulguer ces infos ?
|
||||
if not can_handle_passwd(current_user, allow_admindepts=True):
|
||||
raise AccessDenied("Vous n'avez pas la permission de voir cette page")
|
||||
|
||||
dept = g.scodoc_dept
|
||||
if not user_name:
|
||||
user = current_user
|
||||
else:
|
||||
user = User.query.filter_by(user_name=user_name).first()
|
||||
if not user:
|
||||
raise ScoValueError("invalid user_name")
|
||||
H = [
|
||||
html_sco_header.sco_header(
|
||||
context,
|
||||
REQUEST,
|
||||
page_title="Utilisateur %s" % user.user_name.encode("utf-8"), # sco8
|
||||
)
|
||||
]
|
||||
F = html_sco_header.sco_footer(context, REQUEST)
|
||||
H.append("<h2>Utilisateur: %s" % user.user_name)
|
||||
info = user.to_dict()
|
||||
if info:
|
||||
H.append(" (%(status_txt)s)" % info)
|
||||
H.append("</h2>")
|
||||
if not info:
|
||||
H.append(
|
||||
"<p>L' utilisateur '%s' n'est pas défini dans ce module.</p>" % user_name
|
||||
)
|
||||
if user.has_permission(Permission.ScoEditAllNotes, dept):
|
||||
H.append("<p>(il peut modifier toutes les notes de %s)</p>" % dept)
|
||||
if user.has_permission(Permission.ScoEditAllEvals, dept):
|
||||
H.append("<p>(il peut modifier toutes les évaluations de %s)</p>" % dept)
|
||||
if user.has_permission(Permission.ScoImplement, dept):
|
||||
H.append("<p>(il peut creer des formations en %s)</p>" % dept)
|
||||
else:
|
||||
H.append(
|
||||
"""<p>
|
||||
<b>Login :</b> %(user_name)s<br/>
|
||||
<b>Nom :</b> %(nom)s<br/>
|
||||
<b>Prénom :</b> %(prenom)s<br/>
|
||||
<b>Mail :</b> %(email)s<br/>
|
||||
<b>Roles :</b> %(roles_string)s<br/>
|
||||
<b>Dept :</b> %(dept)s<br/>
|
||||
<b>Dernière modif mot de passe:</b> %(date_modif_passwd)s<br/>
|
||||
<b>Date d'expiration:</b> %(date_expiration)s
|
||||
<p><ul>
|
||||
<li><a class="stdlink" href="form_change_password?user_name=%(user_name)s">changer le mot de passe</a></li>"""
|
||||
% info
|
||||
)
|
||||
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/déactiver ce compte</a></li>
|
||||
<li><a class="stdlink" href="delete_user_form?user_name=%(user_name)s">supprimer cet utilisateur</a> <em>(à n'utiliser qu'en cas d'erreur !)</em></li>
|
||||
"""
|
||||
% info
|
||||
)
|
||||
|
||||
H.append("</ul>")
|
||||
|
||||
if current_user.user_name == user_name:
|
||||
H.append(
|
||||
'<p><b>Se déconnecter: <a class="stdlink" href="%s">logout</a></b></p>'
|
||||
% url_for("auth.logout")
|
||||
)
|
||||
# Liste des permissions
|
||||
H.append(
|
||||
'<div class="permissions"><p>Permissions de cet utilisateur dans le département %s:</p><ul>'
|
||||
% dept
|
||||
)
|
||||
for p in Permission.description:
|
||||
perm = getattr(Permission, p)
|
||||
if user.has_permission(perm, dept):
|
||||
b = "oui"
|
||||
else:
|
||||
b = "non"
|
||||
H.append("<li>%s : %s</li>" % (Permission.description[p], b))
|
||||
H.append("</ul></div>")
|
||||
|
||||
if current_user.has_permission(Permission.ScoUsersAdmin, dept):
|
||||
H.append(
|
||||
'<p><a class="stdlink" href="%s">Liste de tous les utilisateurs</a></p>'
|
||||
% url_for("users.index_html", scodoc_dept=g.scodoc_dept)
|
||||
)
|
||||
return scu.sco8_join(H) + F
|
||||
|
@ -68,6 +68,12 @@ from app.scodoc.sco_codes_parcours import NOTES_TOLERANCE, CODES_EXPL
|
||||
from app.scodoc import sco_exceptions
|
||||
from app.scodoc import VERSION
|
||||
|
||||
# ----- TEMPORAIRE POUR MIGRATION SCODOC7 -> SCODOC8 avant python3
|
||||
def sco8_join(L, sep="\n"): # sco8
|
||||
return sep.join(
|
||||
[x if not isinstance(x, types.UnicodeType) else x.encode("utf-8") for x in L]
|
||||
)
|
||||
|
||||
|
||||
# ----- CALCUL ET PRESENTATION DES NOTES
|
||||
NOTES_PRECISION = 1e-4 # evite eventuelles erreurs d'arrondis
|
||||
|
@ -35,7 +35,7 @@ Emmanuel Viennet, 2021
|
||||
"""
|
||||
|
||||
from flask import g
|
||||
from flask import current_app
|
||||
from flask import current_app, request
|
||||
|
||||
from app.auth.models import Permission
|
||||
from app.auth.models import User
|
||||
@ -45,9 +45,11 @@ from app.decorators import (
|
||||
permission_required,
|
||||
admin_required,
|
||||
login_required,
|
||||
ZRequest,
|
||||
)
|
||||
|
||||
from app.scodoc import sco_users
|
||||
from app.scodoc import sco_utils as scu
|
||||
|
||||
from app.views import users_bp as bp
|
||||
|
||||
@ -55,21 +57,28 @@ from app.views import users_bp as bp
|
||||
context = ScoDoc7Context("users") # sco8
|
||||
|
||||
|
||||
# ------- Fonctions vraiment spécifiques à ScoDoc:
|
||||
@bp.route("/")
|
||||
@bp.route("/index_html")
|
||||
@permission_required(Permission.ScoUsersView)
|
||||
@scodoc7func(context)
|
||||
def index_html(context, REQUEST, all_depts=False, with_olds=False, format="html"):
|
||||
def index_html(context, REQUEST, all_depts=False, with_inactives=False, format="html"):
|
||||
return sco_users.index_html(
|
||||
context,
|
||||
REQUEST=REQUEST,
|
||||
all_depts=all_depts,
|
||||
with_olds=with_olds,
|
||||
with_inactives=with_inactives,
|
||||
format=format,
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/user_info")
|
||||
@permission_required(Permission.ScoUsersView)
|
||||
@scodoc7func(context)
|
||||
def user_info(user_name, format="json", REQUEST=None):
|
||||
info = sco_users.user_info(user_name=user_name)
|
||||
return scu.sendResult(REQUEST, info, name="user", format=format)
|
||||
|
||||
|
||||
@bp.route("/create_user_form")
|
||||
def create_user_form():
|
||||
raise NotImplementedError()
|
||||
@ -80,6 +89,7 @@ def import_users_form():
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
@bp.route("/userinfo")
|
||||
def userinfo():
|
||||
raise NotImplementedError()
|
||||
@bp.route("/user_info_page")
|
||||
@scodoc7func(context)
|
||||
def user_info_page(user_name, REQUEST=None):
|
||||
return sco_users.user_info_page(context, user_name=user_name, REQUEST=REQUEST)
|
||||
|
@ -98,5 +98,5 @@ class FakeUsers(object):
|
||||
"date_expiration": None,
|
||||
}
|
||||
|
||||
def get_user_list(self, dept=None, with_olds=False):
|
||||
def get_user_list(self, dept=None, with_inactives=False):
|
||||
return [self.user_info()]
|
||||
|
Loading…
Reference in New Issue
Block a user