From d40a2b43cd419bc5c6ca81e04ebdb33fecdda3eb Mon Sep 17 00:00:00 2001
From: Emmanuel Viennet
Date: Sun, 27 Jun 2021 12:11:39 +0200
Subject: [PATCH] Page info utilisateur
---
app/auth/models.py | 34 +-
app/decorators.py | 2 +-
app/scodoc/ZScoUsers.py | 1314 ---------------------------------
app/scodoc/html_sco_header.py | 2 +-
app/scodoc/html_sidebar.py | 3 +-
app/scodoc/sco_permissions.py | 2 -
app/scodoc/sco_users.py | 200 ++++-
app/scodoc/sco_utils.py | 6 +
app/views/users.py | 24 +-
scodoc_manager.py | 2 +-
10 files changed, 227 insertions(+), 1362 deletions(-)
delete mode 100644 app/scodoc/ZScoUsers.py
diff --git a/app/auth/models.py b/app/auth/models.py
index a769a592bd..e4ae2fc174 100644
--- a/app/auth/models.py
+++ b/app/auth/models.py
@@ -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):
diff --git a/app/decorators.py b/app/decorators.py
index 720bd4e755..0fe4f46c3e 100644
--- a/app/decorators.py
+++ b/app/decorators.py
@@ -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
diff --git a/app/scodoc/ZScoUsers.py b/app/scodoc/ZScoUsers.py
deleted file mode 100644
index a8418ed47d..0000000000
--- a/app/scodoc/ZScoUsers.py
+++ /dev/null
@@ -1,1314 +0,0 @@
-# -*- mode: python -*-
-# -*- coding: utf-8 -*-
-
-##############################################################################
-#
-# Gestion scolarite IUT
-#
-# Copyright (c) 1999 - 2021 Emmanuel Viennet. All rights reserved.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-#
-# Emmanuel Viennet emmanuel.viennet@viennet.net
-#
-##############################################################################
-
-""" Gestion des utilisateurs (table SQL pour Zope User Folder)
-"""
-import string
-import re
-import time
-import md5
-import base64
-import jaxml
-
-from app.scodoc.sco_zope import * # pylint: disable=unused-wildcard-import
-
-# ---------------
-import app.scodoc.sco_utils as scu
-import app.scodoc.notesdb as ndb
-from app.scodoc.notes_log import log
-from app.scodoc.scolog import logdb
-from scolars import format_prenom, format_nom
-from app.scodoc import sco_import_users
-from app.scodoc import sco_excel
-from app.scodoc.TrivialFormulator import TrivialFormulator, TF, tf_error_message
-from app.scodoc.gen_tables import GenTable
-from app.scodoc import sco_etud
-from app.scodoc import sco_cache
-from app.scodoc import sco_users
-
-from app.scodoc.sco_exceptions import (
- AccessDenied,
- ScoException,
- ScoValueError,
- ScoInvalidDateError,
- ScoLockedFormError,
- ScoGenError,
-)
-
-# ---------------
-# cache global: chaque instance, repérée par son URL, a un cache
-# qui est recréé à la demande
-# On cache ici la liste des utilisateurs, pour une duree limitee
-# (une minute).
-
-CACHE_userlist = {}
-
-# ---------------
-
-
-class ZScoUsers(
- ObjectManager, PropertyManager, RoleManager, Item, Persistent, Implicit
-):
-
- "ZScousers object"
-
- meta_type = "ZScoUsers"
- security = ClassSecurityInfo()
-
- # This is the list of the methods associated to 'tabs' in the ZMI
- # Be aware that The first in the list is the one shown by default, so if
- # the 'View' tab is the first, you will never see your tabs by cliquing
- # on the object.
- manage_options = (
- ({"label": "Contents", "action": "manage_main"},)
- + PropertyManager.manage_options # add the 'Properties' tab
- + ({"label": "View", "action": "index_html"},)
- + Item.manage_options # add the 'Undo' & 'Owner' tab
- + RoleManager.manage_options # add the 'Security' tab
- )
-
- # no permissions, only called from python
- def __init__(self, id, title):
- "initialise a new instance"
- self.id = id
- self.title = title
-
- # Connexion to SQL database of users:
-
- # Ugly but necessary during transition out of Zope:
- _db_cnx_string = "dbname=SCOUSERS port=5432"
- security.declareProtected("Change DTML Documents", "GetUsersDBConnexion")
- GetUsersDBConnexion = ndb.GetUsersDBConnexion
-
- # --------------------------------------------------------------------
- #
- # Users (top level)
- #
- # --------------------------------------------------------------------
- # used to view content of the object
- security.declareProtected(ScoUsersView, "index_html")
-
- def index_html(self, REQUEST, all=0, with_olds=0, format="html"):
- "gestion utilisateurs..."
- all = int(all)
- with_olds = int(with_olds)
- # Controle d'acces
- authuser = REQUEST.AUTHENTICATED_USER
- user_name = str(authuser)
- # log('user: %s roles: %s'%(user_name,authuser.getRolesInContext(self)))
- user = self._user_list(args={"user_name": user_name})
- if not user:
- zope_roles = authuser.getRolesInContext(self)
- if ("Manager" in zope_roles) or ("manage" in zope_roles):
- dept = "" # special case for zope admin
- else:
- raise AccessDenied("Vous n'avez pas la permission de voir cette page")
- else:
- dept = user[0]["dept"]
-
- H = [self.sco_header(REQUEST, page_title="Gestion des utilisateurs")]
- H.append("Gestion des utilisateurs
")
-
- if authuser.has_permission(Permission.ScoUsersAdmin, self):
- H.append(
- 'Ajouter un utilisateur'
- )
- H.append(
- ' Importer des utilisateurs
'
- )
- if all:
- checked = "checked"
- else:
- checked = ""
- if with_olds:
- olds_checked = "checked"
- else:
- olds_checked = ""
- H.append(
- """
"""
- % (REQUEST.URL0, checked, olds_checked)
- )
-
- L = self.list_users(
- dept,
- all=all,
- with_olds=with_olds,
- format=format,
- REQUEST=REQUEST,
- with_links=authuser.has_permission(Permission.ScoUsersAdmin, self),
- )
- if format != "html":
- return L
- H.append(L)
-
- F = self.sco_footer(REQUEST)
- return "\n".join(H) + F
-
- _userEditor = ndb.EditableTable(
- "sco_users",
- "user_id",
- (
- "user_id",
- "user_name",
- "passwd",
- "roles",
- "date_modif_passwd",
- "nom",
- "prenom",
- "email",
- "dept",
- "passwd_temp",
- "status",
- "date_expiration",
- ),
- output_formators={
- "date_modif_passwd": ndb.DateISOtoDMY,
- "date_expiration": ndb.DateISOtoDMY,
- },
- input_formators={
- "date_modif_passwd": ndb.DateDMYtoISO,
- "date_expiration": ndb.DateDMYtoISO,
- },
- sortkey="nom",
- filter_nulls=False,
- )
-
- def _user_list(self, **kw):
- # list info sur utilisateur(s)
- cnx = self.GetUsersDBConnexion()
- users = self._userEditor.list(cnx, **kw)
- for u in users:
- if u["status"] == "old":
- u["status_txt"] = "(ancien)"
- else:
- u["status_txt"] = ""
-
- return users
-
- def _user_edit(self, user_name, vals):
- # edit user
- cnx = self.GetUsersDBConnexion()
- vals["user_name"] = user_name
- self._userEditor.edit(cnx, vals)
- self.get_userlist_cache().inval_cache() # >
- self.acl_users.cache_removeUser(user_name) # exUserFolder's caches
- self.acl_users.xcache_removeUser(user_name)
- # Ensure that if status is "old", login is disabled
- # note that operation is reversible without having to re-enter a password
- # We change the roles (to avoid dealing with passwd hash, controled by exUserFolder)
- u = self._user_list(args={"user_name": user_name})[0]
- if u["status"] == "old" and u["roles"] and u["roles"][0] != "-":
- roles = ["-" + r for r in u["roles"].split(",")]
- cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
- self.acl_users.scodoc_editUser(cursor, user_name, roles=roles)
- self.get_userlist_cache().inval_cache()
- elif not u["status"] and u["roles"] and u["roles"][0] == "-":
- roles = [r[1:] for r in u["roles"].split(",") if (r and r[0] == "-")]
- cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
- self.acl_users.scodoc_editUser(cursor, user_name, roles=roles)
- self.get_userlist_cache().inval_cache()
-
- def _user_delete(self, user_name):
- # delete user
- cnx = self.GetUsersDBConnexion()
- user_id = self._user_list(args={"user_name": user_name})[0]["user_id"]
- self._userEditor.delete(cnx, user_id)
- self.get_userlist_cache().inval_cache() # >
-
- def _all_roles(self):
- "ensemble de tous les roles attribués ou attribuables"
- roles = set(self.DeptUsersRoles())
- cnx = self.GetUsersDBConnexion()
- L = self._userEditor.list(cnx, {})
- for l in L:
- roles.update([x.strip() for x in l["roles"].split(",")])
- return [r for r in roles if r and r[0] != "-"]
-
- security.declareProtected(ScoUsersAdmin, "user_info")
-
- def user_info(self, user_name=None, user=None, format=None, REQUEST=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:
- infos = self._user_list(args={"user_name": user_name})
- else:
- infos = [user.copy()]
- user_name = user["user_name"]
-
- if not infos:
- # 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:
- info = infos[0]
- # always conceal password !
- del info["passwd"] # always conceal password !
- #
- 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 scu.sendResult(REQUEST, info, name="user", format=format)
-
- def _can_handle_passwd(self, authuser, user_name, allow_admindepts=False):
- """true if authuser can see or change passwd of user_name.
- If allow_admindepts, allow Admin from all depts (so they can view users from other depts
- and add roles to them).
- authuser is a Zope user object. user_name is a string.
- """
- # Is authuser a zope admin ?
- zope_roles = authuser.getRolesInContext(self)
- if ("Manager" in zope_roles) or ("manage" in zope_roles):
- return True
- # Anyone can change its own passwd (or see its informations)
- if str(authuser) == user_name:
- return True
- # has permission ?
- if not authuser.has_permission(Permission.ScoUsersAdmin, self):
- return False
- # Ok, now check that authuser can manage users from this departement
- # Get user info
- user = self._user_list(args={"user_name": user_name})
- if not user:
- return False # we don't have infos on this user !
- # Get authuser info
- auth_name = str(authuser)
- authuser_info = self._user_list(args={"user_name": auth_name})
- if not authuser_info:
- return False # not admin, and not in out database
- auth_dept = authuser_info[0]["dept"]
- if not auth_dept:
- return True # if no dept, can access users from all depts !
- if auth_dept == user[0]["dept"] or allow_admindepts:
- return True
- else:
- return False
-
- def do_change_password(self, user_name, password):
- user = self._user_list(args={"user_name": user_name})
- assert len(user) == 1, "database inconsistency: len(user)=%d" % len(user)
- # should not occur, already tested in _can_handle_passwd
- cnx = self.GetUsersDBConnexion() # en mode autocommit
- cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
- cursor.execute(
- "update sco_users set date_modif_passwd=now(), passwd_temp=0 where user_name=%(user_name)s",
- {"user_name": user_name},
- )
-
- # Laisse le exUserFolder modifier les donnees:
- self.acl_users.scodoc_editUser(
- cursor, user_name, password=password, roles=[user[0]["roles"]]
- )
-
- log("change_password: change ok for %s" % user_name)
- self.get_userlist_cache().inval_cache() # >
-
- security.declareProtected(ScoView, "change_password")
-
- def change_password(self, user_name, password, password2, REQUEST):
- "change a password"
- # ScoUsersAdmin: modif tous les passwd de SON DEPARTEMENT
- # sauf si pas de dept (admin global)
- H = []
- F = self.sco_footer(REQUEST)
- # Check access permission
- if not self._can_handle_passwd(REQUEST.AUTHENTICATED_USER, user_name):
- # access denied
- log(
- "change_password: access denied (authuser=%s, user_name=%s, ip=%s)"
- % (REQUEST.AUTHENTICATED_USER, user_name, REQUEST.REMOTE_ADDR)
- )
- raise AccessDenied(
- "vous n'avez pas la permission de changer ce mot de passe"
- )
- # check password
- if password != password2:
- H.append(
- """Les deux mots de passes saisis sont différents !
- Recommencer
"""
- % user_name
- )
- else:
- if not sco_users.is_valid_password(password):
- H.append(
- """ce mot de passe n\'est pas assez compliqué !
(oui, il faut un mot de passe vraiment compliqué !)
- Recommencer
- """
- % user_name
- )
- else:
- # ok, strong password
- # MD5 hash (now computed by exUserFolder)
- # digest = md5.new()
- # digest.update(password)
- # digest = digest.digest()
- # md5pwd = string.strip(base64.encodestring(digest))
- #
- self.do_change_password(user_name, password)
- #
- # ici page simplifiee car on peut ne plus avoir
- # le droit d'acceder aux feuilles de style
- H.append(
- "Changement effectué !
Ne notez pas ce mot de passe, mais mémorisez le !
Rappel: il est interdit de communiquer son mot de passe à un tiers, même si c'est un collègue de confiance !
Si vous n'êtes pas administrateur, le système va vous redemander votre login et nouveau mot de passe au prochain accès.
"
- )
- return (
- """
-
-
-
-Mot de passe changé
-
-Mot de passe changé !
-"""
- % (scu.SCO_ENCODING, scu.SCO_ENCODING)
- + "\n".join(H)
- + 'Continuer'
- % self.ScoURL()
- )
- return self.sco_header(REQUEST) + "\n".join(H) + F
-
- security.declareProtected(ScoView, "form_change_password")
-
- def form_change_password(self, REQUEST, user_name=None):
- """Formulaire changement mot de passe
- Un utilisateur peut toujours changer son mot de passe"""
- authuser = REQUEST.AUTHENTICATED_USER
- if not user_name:
- user_name = str(authuser)
- H = [self.sco_header(REQUEST, user_check=False)]
- F = self.sco_footer(REQUEST)
- # check access
- if not self._can_handle_passwd(authuser, user_name):
- return (
- "\n".join(H)
- + "Vous n'avez pas la permission de changer ce mot de passe
"
- + F
- )
- #
- H.append(
- """Changement du mot de passe de %(user_name)s
-
-
"""
% (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("Utilisateur: %s" % user.user_name)
+ info = user.to_dict()
+ if info:
+ H.append(" (%(status_txt)s)" % info)
+ H.append("
")
+ if not info:
+ H.append(
+ "L' utilisateur '%s' n'est pas défini dans ce module.
" % user_name
+ )
+ if user.has_permission(Permission.ScoEditAllNotes, dept):
+ H.append("(il peut modifier toutes les notes de %s)
" % dept)
+ if user.has_permission(Permission.ScoEditAllEvals, dept):
+ H.append("(il peut modifier toutes les évaluations de %s)
" % dept)
+ if user.has_permission(Permission.ScoImplement, dept):
+ H.append("(il peut creer des formations en %s)
" % dept)
+ else:
+ H.append(
+ """
+ Login : %(user_name)s
+ Nom : %(nom)s
+ Prénom : %(prenom)s
+ Mail : %(email)s
+ Roles : %(roles_string)s
+ Dept : %(dept)s
+ Dernière modif mot de passe: %(date_modif_passwd)s
+ Date d'expiration: %(date_expiration)s
+
")
+
+ if current_user.user_name == user_name:
+ H.append(
+ 'Se déconnecter: logout
'
+ % url_for("auth.logout")
+ )
+ # Liste des permissions
+ H.append(
+ 'Permissions de cet utilisateur dans le département %s:
'
+ % dept
+ )
+ for p in Permission.description:
+ perm = getattr(Permission, p)
+ if user.has_permission(perm, dept):
+ b = "oui"
+ else:
+ b = "non"
+ H.append("- %s : %s
" % (Permission.description[p], b))
+ H.append("
")
+
+ if current_user.has_permission(Permission.ScoUsersAdmin, dept):
+ H.append(
+ 'Liste de tous les utilisateurs
'
+ % url_for("users.index_html", scodoc_dept=g.scodoc_dept)
+ )
+ return scu.sco8_join(H) + F
diff --git a/app/scodoc/sco_utils.py b/app/scodoc/sco_utils.py
index 0c9b6e4491..ee025b07e5 100644
--- a/app/scodoc/sco_utils.py
+++ b/app/scodoc/sco_utils.py
@@ -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
diff --git a/app/views/users.py b/app/views/users.py
index 456f62bd58..24a2899b2e 100644
--- a/app/views/users.py
+++ b/app/views/users.py
@@ -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)
diff --git a/scodoc_manager.py b/scodoc_manager.py
index d3ed4d839c..5cbadf34a2 100644
--- a/scodoc_manager.py
+++ b/scodoc_manager.py
@@ -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()]