From f991ffdca5f1e9b3f021bd0c31e4f365574948d7 Mon Sep 17 00:00:00 2001
From: Emmanuel Viennet
Date: Sat, 26 Jun 2021 21:57:54 +0200
Subject: [PATCH] =?UTF-8?q?d=C3=A9but=20impl=C3=A9mentation=20gestion=20ut?=
=?UTF-8?q?ilisateurs?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 39 +++++
app/__init__.py | 3 +
app/auth/forms.py | 10 +-
app/auth/models.py | 45 +++++-
app/auth/routes.py | 12 +-
app/decorators.py | 2 +-
app/scodoc/sco_dept.py | 6 +-
app/scodoc/sco_formsemestre_edit.py | 5 +-
app/scodoc/sco_permissions_check.py | 25 +++
app/scodoc/sco_users.py | 205 +++++++++++++++++++++++-
app/templates/base.html | 2 +-
app/templates/email/reset_password.html | 2 +-
app/templates/email/reset_password.txt | 2 +-
app/views/__init__.py | 3 +-
app/views/notes.py | 12 +-
app/views/users.py | 85 ++++++++++
misc/check_zope_usage.py | 2 +-
requirements.txt | 12 ++
scodoc.py | 16 +-
scodoc_manager.py | 2 +-
tests/test_users.py | 10 +-
21 files changed, 455 insertions(+), 45 deletions(-)
create mode 100644 app/views/users.py
diff --git a/README.md b/README.md
index b05219bc8..c86756b66 100644
--- a/README.md
+++ b/README.md
@@ -51,9 +51,11 @@ Puis d'installer Flask comme suit:
Installation:
+ apt-get install libcrack2-dev
pip install flask
# et pas mal d'autres paquets
+
donc utiliser:
pip install -r requirements.txt
@@ -62,6 +64,43 @@ pour régénerer ce fichier:
pip freeze > requirements.txt
+## Bases de données
+ScoDoc8 utilise les bases de département de ScoDoc7, mais une nouvelle base
+utilisateurs nommée `SCO8USERS` (au lieu de `SCOUSERS`), qui est manipulée
+via `sqlalchemy`.
+
+### Configuration de la base utilisateurs
+
+On va créer une base nommée `SCO8USERS`, appartenant à l'utilisateur (aka role) postgres `scodoc`.
+Cet utilisateur sera créé ainsi sur le serveur linux::
+
+ su - postgres
+ createuser --no-createdb --no-createrole --pwprompt scodoc
+
+Puis création de la base (en tant qu'utilisateur `postgres`):
+
+ createdb -O scodoc SCO8USERS
+
+### Initialisation base par flask
+
+En tant qu'utilisateur `www-data` (compat. avec ScoDoc7):
+
+ flask db init
+ flask db migrate -m "users and roles tables"
+ flask db upgrade
+
+Puis initialisation de l'appli:
+
+ flask user-db-init
+
+On peut ensuite créer des utilisateurs tests avec:
+
+ flask user-create toto Ens RT
+ flask user-create tata Ens RT
+ flask user-create tutu Ens Info
+ flask user-create titi Secr RT
+
+
### Bidouilles temporaires
Installer le bon vieux `pyExcelerator` dans l'environnement:
diff --git a/app/__init__.py b/app/__init__.py
index 2c0d603d3..cb030ad51 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -68,12 +68,15 @@ def create_app(config_class=Config):
from app.views import scolar_bp
from app.views import notes_bp
+ from app.views import users_bp
from app.views import absences_bp
# https://scodoc.fr/ScoDoc/RT/Scolarite/...
app.register_blueprint(scolar_bp, url_prefix="/ScoDoc//Scolarite")
# https://scodoc.fr/ScoDoc/RT/Scolarite/Notes/...
app.register_blueprint(notes_bp, url_prefix="/ScoDoc//Scolarite/Notes")
+ # https://scodoc.fr/ScoDoc/RT/Scolarite/Users/...
+ app.register_blueprint(users_bp, url_prefix="/ScoDoc//Scolarite/Users")
# https://scodoc.fr/ScoDoc/RT/Scolarite/Absences/...
app.register_blueprint(
absences_bp, url_prefix="/ScoDoc//Scolarite/Absences"
diff --git a/app/auth/forms.py b/app/auth/forms.py
index 4dcb11831..bb201e26d 100644
--- a/app/auth/forms.py
+++ b/app/auth/forms.py
@@ -16,14 +16,14 @@ _l = _
class LoginForm(FlaskForm):
- username = StringField(_l("Username"), validators=[DataRequired()])
+ user_name = StringField(_l("Username"), validators=[DataRequired()])
password = PasswordField(_l("Password"), validators=[DataRequired()])
remember_me = BooleanField(_l("Remember Me"))
submit = SubmitField(_l("Sign In"))
class UserCreationForm(FlaskForm):
- username = StringField(_l("Username"), validators=[DataRequired()])
+ user_name = StringField(_l("Username"), validators=[DataRequired()])
email = StringField(_l("Email"), validators=[DataRequired(), Email()])
password = PasswordField(_l("Password"), validators=[DataRequired()])
password2 = PasswordField(
@@ -31,10 +31,10 @@ class UserCreationForm(FlaskForm):
)
submit = SubmitField(_l("Register"))
- def validate_username(self, username):
- user = User.query.filter_by(username=username.data).first()
+ def validate_user_name(self, user_name):
+ user = User.query.filter_by(user_name=user_name.data).first()
if user is not None:
- raise ValidationError(_("Please use a different username."))
+ raise ValidationError(_("Please use a different user_name."))
def validate_email(self, email):
user = User.query.filter_by(email=email.data).first()
diff --git a/app/auth/models.py b/app/auth/models.py
index a1db43e62..a769a592b 100644
--- a/app/auth/models.py
+++ b/app/auth/models.py
@@ -26,13 +26,23 @@ class User(UserMixin, db.Model):
"""ScoDoc users, handled by Flask / SQLAlchemy"""
id = db.Column(db.Integer, primary_key=True)
- username = db.Column(db.String(64), index=True, unique=True)
+ user_name = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
+
+ 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)
+
password_hash = db.Column(db.String(128))
- about_me = db.Column(db.String(140))
last_seen = db.Column(db.DateTime, default=datetime.utcnow)
+ date_modif_passwd = db.Column(db.DateTime, default=datetime.utcnow)
+ date_created = db.Column(db.DateTime, default=datetime.utcnow)
+ date_expiration = db.Column(db.DateTime, default=None)
+ passwd_temp = db.Column(db.Boolean, default=False)
token = db.Column(db.String(32), index=True, unique=True)
token_expiration = db.Column(db.DateTime)
+
roles = db.relationship("Role", secondary="user_role", viewonly=True)
Permission = Permission
@@ -52,10 +62,10 @@ class User(UserMixin, db.Model):
current_app.logger.info("creating user with roles={}".format(self.roles))
def __repr__(self):
- return "".format(self.username)
+ return "".format(u=self)
def __str__(self):
- return self.username
+ return self.user_name
def set_password(self, password):
"Set password"
@@ -91,17 +101,32 @@ class User(UserMixin, db.Model):
def to_dict(self, include_email=False):
data = {
+ "date_expiration": self.date_expiration.isoformat() + "Z"
+ if self.date_expiration
+ else None,
+ "date_modif_passwd": self.date_modif_passwd.isoformat() + "Z"
+ if self.date_modif_passwd
+ else None,
+ "date_created": self.date_created.isoformat() + "Z"
+ if self.date_created
+ else None,
+ "dept": self.dept,
"id": self.id,
- "username": self.username,
+ "is_old": self.is_old,
+ "status_txt": "(ancien)" if self.is_old else "",
"last_seen": self.last_seen.isoformat() + "Z",
- "about_me": self.about_me,
+ "nom": self.nom,
+ "prenom": self.prenom,
+ "roles": self.roles,
+ "roles_string": self.get_roles_string(),
+ "user_name": self.user_name,
}
if include_email:
data["email"] = self.email
return data
def from_dict(self, data, new_user=False):
- for field in ["username", "email", "about_me"]:
+ for field in ["user_name", "non", "prenom", "dept", "status", "email"]:
if field in data:
setattr(self, field, data[field])
if new_user and "password" in data:
@@ -169,6 +194,12 @@ class User(UserMixin, db.Model):
for role in self.roles:
yield role
+ def get_roles_string(self):
+ """string repr. of user's roles (with depts)
+ e.g. "EnsRT, EnsInfo, SecrCJ"
+ """
+ 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)
diff --git a/app/auth/routes.py b/app/auth/routes.py
index 96cc40c6f..b45e05c2f 100644
--- a/app/auth/routes.py
+++ b/app/auth/routes.py
@@ -15,9 +15,11 @@ from app.auth.forms import (
ResetPasswordRequestForm,
ResetPasswordForm,
)
+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 scodoc7func, admin_required
+from app.decorators import admin_required
+
_ = lambda x: x # sans babel
_l = _
@@ -29,9 +31,9 @@ def login():
return redirect(url_for("main.index"))
form = LoginForm()
if form.validate_on_submit():
- user = User.query.filter_by(username=form.username.data).first()
+ user = User.query.filter_by(user_name=form.user_name.data).first()
if user is None or not user.check_password(form.password.data):
- flash(_("Invalid username or password"))
+ flash(_("Invalid user_name or password"))
return redirect(url_for("auth.login"))
login_user(user, remember=form.remember_me.data)
next_page = request.args.get("next")
@@ -53,11 +55,11 @@ def create_user():
"Form creating new user"
form = UserCreationForm()
if form.validate_on_submit():
- user = User(username=form.username.data, email=form.email.data)
+ user = User(user_name=form.user_name.data, email=form.email.data)
user.set_password(form.password.data)
db.session.add(user)
db.session.commit()
- flash("User {} created".format(user.username))
+ flash("User {} created".format(user.user_name))
return redirect(url_for("main.index"))
return render_template(
"auth/register.html", title=u"Création utilisateur", form=form
diff --git a/app/decorators.py b/app/decorators.py
index cab531a80..720bd4e75 100644
--- a/app/decorators.py
+++ b/app/decorators.py
@@ -26,7 +26,7 @@ class ZUser(object):
def __init__(self):
"create, based on `flask_login.current_user`"
- self.username = current_user.username
+ self.username = current_user.user_name
def __str__(self):
return self.username
diff --git a/app/scodoc/sco_dept.py b/app/scodoc/sco_dept.py
index 8461b890b..6d5471f25 100644
--- a/app/scodoc/sco_dept.py
+++ b/app/scodoc/sco_dept.py
@@ -27,6 +27,9 @@
"""Page accueil département (liste des semestres, etc)
"""
+
+from flask import g
+
import app.scodoc.sco_utils as scu
from app.scodoc.gen_tables import GenTable
from app.scodoc.sco_permissions import Permission
@@ -37,6 +40,7 @@ from app.scodoc import sco_modalites
from app.scodoc import sco_news
from app.scodoc import sco_preferences
from app.scodoc import sco_up_to_date
+from app.scodoc import sco_users
def index_html(context, REQUEST=None, showcodes=0, showsemtable=0):
@@ -90,7 +94,7 @@ def index_html(context, REQUEST=None, showcodes=0, showsemtable=0):
sem["groupicon"] = emptygroupicon
# S'il n'y a pas d'utilisateurs dans la base, affiche message
- if not context.Users.get_userlist():
+ if not sco_users.get_user_list(dept=g.scodoc_dept):
H.append(
"""Aucun utilisateur défini !
Pour définir des utilisateurs
passez par la page Utilisateurs.
diff --git a/app/scodoc/sco_formsemestre_edit.py b/app/scodoc/sco_formsemestre_edit.py
index a199c6fbb..1a95d8977 100644
--- a/app/scodoc/sco_formsemestre_edit.py
+++ b/app/scodoc/sco_formsemestre_edit.py
@@ -43,6 +43,7 @@ from app.scodoc import sco_compute_moy
from app.scodoc import sco_edit_matiere
from app.scodoc import sco_edit_module
from app.scodoc import sco_edit_ue
+from app.scodoc import sco_etud
from app.scodoc import sco_evaluations
from app.scodoc import sco_formations
from app.scodoc import sco_formsemestre
@@ -52,7 +53,7 @@ from app.scodoc import sco_parcours_dut
from app.scodoc import sco_permissions_check
from app.scodoc import sco_portal_apogee
from app.scodoc import sco_preferences
-from app.scodoc import sco_etud
+from app.scodoc import sco_users
def _default_sem_title(F):
@@ -139,7 +140,7 @@ def do_formsemestre_createwithmodules(context, REQUEST=None, edit=False):
)
# Liste des enseignants avec forme pour affichage / saisie avec suggestion
- userlist = context.Users.get_userlist()
+ userlist = [sco_users.user_info(u) for u in sco_users.get_user_list()]
login2display = {} # user_name : forme pour affichage = "NOM Prenom (login)"
for u in userlist:
login2display[u["user_name"]] = u["nomplogin"]
diff --git a/app/scodoc/sco_permissions_check.py b/app/scodoc/sco_permissions_check.py
index a780a4690..60b9a9a4b 100644
--- a/app/scodoc/sco_permissions_check.py
+++ b/app/scodoc/sco_permissions_check.py
@@ -3,6 +3,8 @@
"""Functions checking permissions for some common operations
"""
+from flask import g
+from flask_login import current_user
import app.scodoc.notesdb as ndb
from app.scodoc.sco_permissions import Permission
@@ -188,3 +190,26 @@ def can_change_groups(context, REQUEST, formsemestre_id):
if uid in sem["responsables"]:
return True
return False
+
+
+def can_handle_passwd(user, allow_admindepts=False):
+ """True if the current user can see or change passwd info of user.
+ If allow_admindepts, allow Admin from all depts (so they can view users from other depts
+ and add roles to them).
+ user is a User instance.
+ """
+ if current_user.is_administrator():
+ return True # super admin
+ # Anyone can change his own passwd (or see his informations)
+ if user.user_name == current_user.user_name:
+ return True
+ # If don't have permission in the current dept, abort
+ if not current_user.has_permission(Permission.ScoUsersAdmin, g.scodoc_dept):
+ return False
+ # Now check that current_user can manage users from this departement
+ if not current_user.dept:
+ return True # if no dept, can access users from all depts !
+ if (current_user.dept == user.dept) or allow_admindepts:
+ return True
+ else:
+ return False
\ No newline at end of file
diff --git a/app/scodoc/sco_users.py b/app/scodoc/sco_users.py
index 2390fa67e..ac422791a 100644
--- a/app/scodoc/sco_users.py
+++ b/app/scodoc/sco_users.py
@@ -28,12 +28,48 @@
"""Fonctions sur les utilisateurs
"""
-# Anciennement dans ZScoUsers.py, séparé pour migration
+# 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
+
+from flask import current_app, url_for, g
+from flask_login import current_user
import cracklib # pylint: disable=import-error
+
+from app.auth.models import Permission
+from app.auth.models import User
+
+from app.scodoc import html_sco_header
+from app.scodoc import sco_cache
+from app.scodoc import sco_etud
+from app.scodoc import sco_excel
+from app.scodoc import sco_import_users
+from app.scodoc import sco_preferences
+from app.scodoc.gen_tables import GenTable
+from app.scodoc.notes_log import log
+from app.scodoc.sco_permissions_check import can_handle_passwd
+from app.scodoc.scolog import logdb
+from app.scodoc.sco_etud import format_prenom, format_nom
+from app.scodoc.TrivialFormulator import TrivialFormulator, tf_error_message
+import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu
+from app.scodoc.sco_exceptions import (
+ AccessDenied,
+ ScoException,
+ ScoValueError,
+ ScoInvalidDateError,
+ ScoLockedFormError,
+ ScoGenError,
+)
+
def is_valid_password(cleartxt):
"""Check password.
@@ -50,3 +86,170 @@ def is_valid_password(cleartxt):
return True
except ValueError:
return False
+
+
+# ---------------
+
+# ---------------
+# XXX TODO supprimer ndb.GetUsersDBConnexion
+
+
+def index_html(context, REQUEST, all_depts=False, with_olds=False, format="html"):
+ "gestion utilisateurs..."
+ all_depts = int(all_depts)
+ with_olds = int(with_olds)
+
+ H = [html_sco_header.html_sem_header(context, REQUEST, "Gestion des utilisateurs")]
+ H.append("
Gestion des utilisateurs
")
+
+ if current_user.has_permission(Permission.ScoUsersAdmin, g.scodoc_dept):
+ H.append(
+ 'Ajouter un utilisateur'.format(
+ url_for("users.create_user_form", scodoc_dept=g.scodoc_dept).encode(
+ scu.SCO_ENCODING
+ ) # sco8
+ )
+ )
+ H.append(
+ ' Importer des utilisateurs
'.format(
+ url_for("users.import_users_form", scodoc_dept=g.scodoc_dept).encode(
+ scu.SCO_ENCODING
+ ) # sco8
+ )
+ )
+ if all_depts:
+ checked = "checked"
+ else:
+ checked = ""
+ if with_olds:
+ olds_checked = "checked"
+ else:
+ olds_checked = ""
+ H.append(
+ """
"""
+ % (REQUEST.URL0, checked, olds_checked)
+ )
+
+ L = list_users(
+ context,
+ g.scodoc_dept,
+ all_depts=all_depts,
+ with_olds=with_olds,
+ format=format,
+ REQUEST=REQUEST,
+ with_links=current_user.has_permission(Permission.ScoUsersAdmin, g.scodoc_dept),
+ )
+ if format != "html":
+ return L
+ H.append(L)
+
+ F = html_sco_header.sco_footer(context, REQUEST)
+ return "\n".join(H) + F
+
+
+def list_users(
+ context,
+ dept,
+ all_depts=False, # tous les departements
+ with_olds=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)
+ comm = "dept. %s" % dept.encode(scu.SCO_ENCODING) # sco8
+ else:
+ r = get_user_list(with_olds=with_olds)
+ comm = "tous"
+ if with_olds:
+ comm += ", avec anciens"
+ comm = "(" + comm + ")"
+ # -- Add some information and links:
+ r = []
+ for u in users:
+ # Can current user modify this user ?
+ can_modify = can_handle_passwd(u, allow_admindepts=True)
+
+ d = u.to_dict()
+ r.append(d)
+ # Add links
+ if with_links and can_modify:
+ target = url_for(
+ "users.userinfo", scodoc_dept=g.scodoc_dept, user_name=u.user_name
+ ).encode(
+ scu.SCO_ENCODING
+ ) # sco8
+ d["_user_name_target"] = target
+ d["_nom_target"] = target
+ d["_prenom_target"] = target
+
+ # Hide passwd modification date (depending on visitor's permission)
+ if not can_modify:
+ d["date_modif_passwd"] = "(non visible)"
+
+ title = "Utilisateurs définis dans ScoDoc"
+ tab = GenTable(
+ rows=r,
+ columns_ids=(
+ "user_name",
+ "nom_fmt",
+ "prenom_fmt",
+ "email",
+ "dept",
+ "roles_string",
+ "date_expiration",
+ "date_modif_passwd",
+ "passwd_temp",
+ "status_txt",
+ ),
+ titles={
+ "user_name": "Login",
+ "nom_fmt": "Nom",
+ "prenom_fmt": "Prénom",
+ "email": "Mail",
+ "dept": "Dept.",
+ "roles": "Rôles",
+ "date_expiration": "Expiration",
+ "date_modif_passwd": "Modif. mot de passe",
+ "passwd_temp": "Temp.",
+ "status_txt": "Etat",
+ },
+ caption=title,
+ page_title="title",
+ html_title="""%d utilisateurs %s
+ Cliquer sur un nom pour changer son mot de passe
"""
+ % (len(r), comm),
+ html_class="table_leftalign list_users",
+ html_with_td_classes=True,
+ html_sortable=True,
+ base_url="%s?all=%s" % (REQUEST.URL0, all),
+ pdf_link=False, # table is too wide to fit in a paper page => disable pdf
+ preferences=sco_preferences.SemPreferences(context),
+ )
+
+ return tab.make_page(
+ context, format=format, with_html_headers=False, REQUEST=REQUEST
+ )
+
+
+def get_user_list(dept=None, with_olds=False):
+ """Returns list of users.
+ If dept, select users from this dept,
+ else return all users.
+ """
+ # was get_userlist
+ 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()
+
+
+# def get_user_infos(user_list):
+# return [ user_info(u) for u in user_list ]
diff --git a/app/templates/base.html b/app/templates/base.html
index 15a9ee46b..33c6df8bb 100644
--- a/app/templates/base.html
+++ b/app/templates/base.html
@@ -26,7 +26,7 @@
{% if current_user.is_anonymous %}
Login
{% else %}
- {{current_user.username}}
+ {{current_user.user_name}}
Logout
{% endif %}
diff --git a/app/templates/email/reset_password.html b/app/templates/email/reset_password.html
index e928c6c9f..430d53ded 100644
--- a/app/templates/email/reset_password.html
+++ b/app/templates/email/reset_password.html
@@ -1,4 +1,4 @@
-Bonjour {{ user.username }},
+Bonjour {{ user.user_name }},
Pour réinitialiser votre mot de passe ScoDoc,
diff --git a/app/templates/email/reset_password.txt b/app/templates/email/reset_password.txt
index ea5f14fda..2136a52d6 100644
--- a/app/templates/email/reset_password.txt
+++ b/app/templates/email/reset_password.txt
@@ -1,4 +1,4 @@
-Bonjour {{ user.username }},
+Bonjour {{ user.user_name }},
Pour réinitialiser votre mot de passe ScoDoc, suivre le lien:
diff --git a/app/views/__init__.py b/app/views/__init__.py
index 08815db7f..d390b2fd8 100644
--- a/app/views/__init__.py
+++ b/app/views/__init__.py
@@ -5,9 +5,10 @@ from flask import Blueprint
scolar_bp = Blueprint("scolar", __name__)
notes_bp = Blueprint("notes", __name__)
+users_bp = Blueprint("users", __name__)
absences_bp = Blueprint("absences", __name__)
essais_bp = Blueprint("essais", __name__)
-from app.views import notes, scolar, absences, essais
+from app.views import notes, scolar, absences, users, essais
scolar.context.Notes = notes.context # XXX transitoire #sco8
diff --git a/app/views/notes.py b/app/views/notes.py
index e62a3cce7..a02976fac 100644
--- a/app/views/notes.py
+++ b/app/views/notes.py
@@ -123,6 +123,7 @@ from app.scodoc import sco_synchro_etuds
from app.scodoc import sco_tag_module
from app.scodoc import sco_ue_external
from app.scodoc import sco_undo_notes
+from app.scodoc import sco_users
from app.scodoc.gen_tables import GenTable
from app.scodoc.sco_pdf import PDFLOCK
from app.scodoc.sco_permissions import Permission
@@ -712,7 +713,7 @@ def edit_enseignants_form(context, REQUEST, moduleimpl_id):
footer = html_sco_header.sco_footer(context, REQUEST)
# Liste des enseignants avec forme pour affichage / saisie avec suggestion
- userlist = context.Users.get_userlist()
+ userlist = sco_users.get_user_list()
login2display = {} # user_name : forme pour affichage = "NOM Prenom (login)"
for u in userlist:
login2display[u["user_name"]] = u["nomplogin"]
@@ -821,9 +822,9 @@ def edit_moduleimpl_resp(context, REQUEST, moduleimpl_id):
bodyOnLoad="init_tf_form('')",
)
]
- help = """Taper le début du nom de l'enseignant.
"""
+ help_str = """Taper le début du nom de l'enseignant.
"""
# Liste des enseignants avec forme pour affichage / saisie avec suggestion
- userlist = context.Users.get_userlist()
+ userlist = [sco_users.user_info(u) for u in sco_users.get_user_list()]
login2display = {} # user_name : forme pour affichage = "NOM Prenom (login)"
for u in userlist:
login2display[u["user_name"]] = u["nomplogin"]
@@ -863,7 +864,10 @@ def edit_moduleimpl_resp(context, REQUEST, moduleimpl_id):
)
if tf[0] == 0:
return (
- "\n".join(H) + tf[1] + help + html_sco_header.sco_footer(context, REQUEST)
+ "\n".join(H)
+ + tf[1]
+ + help_str
+ + html_sco_header.sco_footer(context, REQUEST)
)
elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(
diff --git a/app/views/users.py b/app/views/users.py
new file mode 100644
index 000000000..456f62bd5
--- /dev/null
+++ b/app/views/users.py
@@ -0,0 +1,85 @@
+# -*- mode: python -*-
+# -*- coding: utf-8 -*-
+
+##############################################################################
+#
+# ScoDoc
+#
+# 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
+#
+##############################################################################
+
+"""
+Module users: interface gestion utilisateurs
+ré-écriture pour Flask ScoDoc7 / ZScoUsers.py
+
+Vues s'appuyant sur auth et sco_users
+
+Emmanuel Viennet, 2021
+"""
+
+from flask import g
+from flask import current_app
+
+from app.auth.models import Permission
+from app.auth.models import User
+from app.decorators import (
+ scodoc7func,
+ ScoDoc7Context,
+ permission_required,
+ admin_required,
+ login_required,
+)
+
+from app.scodoc import sco_users
+
+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"):
+ return sco_users.index_html(
+ context,
+ REQUEST=REQUEST,
+ all_depts=all_depts,
+ with_olds=with_olds,
+ format=format,
+ )
+
+
+@bp.route("/create_user_form")
+def create_user_form():
+ raise NotImplementedError()
+
+
+@bp.route("/import_users_form")
+def import_users_form():
+ raise NotImplementedError()
+
+
+@bp.route("/userinfo")
+def userinfo():
+ raise NotImplementedError()
diff --git a/misc/check_zope_usage.py b/misc/check_zope_usage.py
index d0b8fa4ab..ea6d723b3 100755
--- a/misc/check_zope_usage.py
+++ b/misc/check_zope_usage.py
@@ -8,7 +8,7 @@ Usage:
check_zope_usage.py publishedmethods.csv string-constants.txt
publishedmethods.csv : fichier texte, module et un nom de méthode / ligne
- ZScoUsers get_userlist
+ ZScoUsers get_user_list
comme extrait par zopelistmethods.py
string-constants.txt : les constantes chaines, extraites par extract_code_strings.py
diff --git a/requirements.txt b/requirements.txt
index addb19ba9..ae1156626 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,13 +1,18 @@
alembic==1.5.5
+astroid==1.6.6
attrdict==2.0.1
Babel==2.9.0
+backports.functools-lru-cache==1.6.4
blinker==1.4
certifi==2021.5.30
chardet==4.0.0
click==7.1.2
+configparser==4.0.2
+cracklib==2.9.3
dnspython==1.16.0
dominate==2.6.0
email-validator==1.1.2
+enum34==1.1.10
Flask==1.1.4
Flask-Babel==2.0.0
Flask-Bootstrap==3.3.7.1
@@ -17,18 +22,23 @@ Flask-Migrate==2.7.0
Flask-Moment==0.11.0
Flask-SQLAlchemy==2.4.4
Flask-WTF==0.14.3
+futures==3.3.0
icalendar==4.0.7
idna==2.10
+isort==4.3.21
itsdangerous==1.1.0
jaxml==3.2
Jinja2==2.11.2
+lazy-object-proxy==1.6.0
Mako==1.1.4
MarkupSafe==1.1.1
+mccabe==0.6.1
Pillow==6.2.2
pkg-resources==0.0.0
psycopg2==2.8.6
pyExcelerator==0.6.3a0
PyJWT==1.7.1
+pylint==1.9.5
PyRSS2Gen==1.1
python-dateutil==2.8.1
python-dotenv==0.15.0
@@ -36,6 +46,7 @@ python-editor==1.0.4
pytz==2021.1
reportlab==3.5.59
requests==2.25.1
+singledispatch==3.6.2
six==1.15.0
SQLAlchemy==1.3.23
stripogram==1.5
@@ -43,4 +54,5 @@ typing==3.7.4.3
urllib3==1.26.5
visitor==0.1.3
Werkzeug==1.0.1
+wrapt==1.12.1
WTForms==2.3.3
diff --git a/scodoc.py b/scodoc.py
index 25559d07a..65e168687 100755
--- a/scodoc.py
+++ b/scodoc.py
@@ -52,14 +52,14 @@ def user_db_init():
click.echo("created initial roles")
# Ensure that admin exists
if Config.SCODOC_ADMIN_MAIL:
- admin_username = Config.SCODOC_ADMIN_LOGIN
- user = User.query.filter_by(username=admin_username).first()
+ admin_user_name = Config.SCODOC_ADMIN_LOGIN
+ user = User.query.filter_by(user_name=admin_user_name).first()
if not user:
- user = User(username=admin_username, email=Config.SCODOC_ADMIN_MAIL)
+ user = User(user_name=admin_user_name, email=Config.SCODOC_ADMIN_MAIL)
db.session.add(user)
db.session.commit()
click.echo(
- "created initial admin user, login: {u.username}, email: {u.email}".format(
+ "created initial admin user, login: {u.user_name}, email: {u.email}".format(
u=user
)
)
@@ -90,16 +90,16 @@ def user_create(username, role, dept):
if not r:
sys.stderr.write("user_create: role {r} does not exists".format(r=r))
return 1
- u = User.query.filter_by(username=username).first()
+ u = User.query.filter_by(user_name=username).first()
if u:
sys.stderr.write("user_create: user {u} already exists".format(u=u))
return 2
- u = User(username=username)
+ u = User(user_name=username, dept=dept)
u.add_role(r, dept)
db.session.add(u)
db.session.commit()
click.echo(
- "created user, login: {u.username}, with role {r} in dept. {dept}".format(
+ "created user, login: {u.user_name}, with role {r} in dept. {dept}".format(
u=u, r=r, dept=dept
)
)
@@ -113,7 +113,7 @@ def user_password(username, password=None):
if not password:
sys.stderr.write("user_password: missing password")
return 1
- u = User.query.filter_by(username=username).first()
+ u = User.query.filter_by(user_name=username).first()
if not u:
sys.stderr.write("user_password: user {} does not exists".format(username))
return 1
diff --git a/scodoc_manager.py b/scodoc_manager.py
index 9fb8ddf8f..d3ed4d839 100644
--- a/scodoc_manager.py
+++ b/scodoc_manager.py
@@ -98,5 +98,5 @@ class FakeUsers(object):
"date_expiration": None,
}
- def get_userlist(self, dept=None, with_olds=False):
+ def get_user_list(self, dept=None, with_olds=False):
return [self.user_info()]
diff --git a/tests/test_users.py b/tests/test_users.py
index e0a2f1159..2012131b3 100644
--- a/tests/test_users.py
+++ b/tests/test_users.py
@@ -30,7 +30,7 @@ class UserModelCase(unittest.TestCase):
db.drop_all()
def test_password_hashing(self):
- u = User(username="susan")
+ u = User(user_name="susan")
u.set_password("cat")
self.assertFalse(u.check_password("dog"))
self.assertTrue(u.check_password("cat"))
@@ -68,7 +68,7 @@ class UserModelCase(unittest.TestCase):
dept = "XX"
perm = Permission.ScoAbsChange
perm2 = Permission.ScoView
- u = User(username="un enseignant")
+ u = User(user_name="un enseignant")
db.session.add(u)
self.assertFalse(u.has_permission(perm, dept))
r = Role.get_named_role("Ens")
@@ -76,7 +76,7 @@ class UserModelCase(unittest.TestCase):
r = Role(name="Ens", permissions=perm)
u.add_role(r, dept)
self.assertTrue(u.has_permission(perm, dept))
- u = User(username="un autre")
+ u = User(user_name="un autre")
u.add_role(r, dept)
db.session.add(u)
db.session.commit()
@@ -86,7 +86,7 @@ class UserModelCase(unittest.TestCase):
r2 = Role(name="Secr", dept=dept, permissions=perm2)
u.add_roles([r, r2], dept)
self.assertTrue(len(u.roles) == 2)
- u = User(username="encore un")
+ u = User(user_name="encore un")
db.session.add(u)
db.session.commit()
u.set_roles([r, r2], dept)
@@ -101,7 +101,7 @@ class UserModelCase(unittest.TestCase):
def test_user_admin(self):
dept = "XX"
perm = 0x1234 # a random perm
- u = User(username="un admin", email=current_app.config["SCODOC_ADMIN_MAIL"])
+ u = User(user_name="un admin", email=current_app.config["SCODOC_ADMIN_MAIL"])
db.session.add(u)
self.assertTrue(len(u.roles) == 1)
self.assertTrue(u.has_permission(perm, dept))