Améliore code et tests gestion User

This commit is contained in:
Emmanuel Viennet 2023-11-22 17:55:15 +01:00
parent ea1a03a654
commit 457a9ddf51
7 changed files with 42 additions and 36 deletions

View File

@ -138,7 +138,7 @@ def user_create():
ok, msg = _is_allowed_user_edit(args) ok, msg = _is_allowed_user_edit(args)
if not ok: if not ok:
return json_error(403, f"user_create: {msg}") return json_error(403, f"user_create: {msg}")
user = User() user = User(user_name=user_name)
user.from_dict(args, new_user=True) user.from_dict(args, new_user=True)
db.session.add(user) db.session.add(user)
db.session.commit() db.session.commit()

View File

@ -12,7 +12,6 @@ from typing import Optional
import cracklib # pylint: disable=import-error import cracklib # pylint: disable=import-error
import flask
from flask import current_app, g from flask import current_app, g
from flask_login import UserMixin, AnonymousUserMixin from flask_login import UserMixin, AnonymousUserMixin
@ -53,7 +52,8 @@ def is_valid_password(cleartxt) -> bool:
def invalid_user_name(user_name: str) -> bool: def invalid_user_name(user_name: str) -> bool:
"Check that user_name (aka login) is invalid" "Check that user_name (aka login) is invalid"
return ( return (
(len(user_name) < 2) not user_name
or (len(user_name) < 2)
or (len(user_name) >= USERNAME_STR_LEN) or (len(user_name) >= USERNAME_STR_LEN)
or not VALID_LOGIN_EXP.match(user_name) or not VALID_LOGIN_EXP.match(user_name)
) )
@ -116,11 +116,16 @@ class User(UserMixin, db.Model, ScoDocModel):
) )
def __init__(self, **kwargs): def __init__(self, **kwargs):
"user_name:str is mandatory"
self.roles = [] self.roles = []
self.user_roles = [] self.user_roles = []
# check login: # check login:
if kwargs.get("user_name") and invalid_user_name(kwargs["user_name"]): if not "user_name" in kwargs:
raise ValueError("missing user_name argument")
if invalid_user_name(kwargs["user_name"]):
raise ValueError(f"invalid user_name: {kwargs['user_name']}") raise ValueError(f"invalid user_name: {kwargs['user_name']}")
kwargs["nom"] = kwargs.get("nom", "") or ""
kwargs["prenom"] = kwargs.get("prenom", "") or ""
super().__init__(**kwargs) super().__init__(**kwargs)
# Ajoute roles: # Ajoute roles:
if ( if (
@ -279,6 +284,7 @@ class User(UserMixin, db.Model, ScoDocModel):
Convert boolean values to bools. Convert boolean values to bools.
""" """
args_dict = args args_dict = args
# Dates
if "date_expiration" in args: if "date_expiration" in args:
date_expiration = args.get("date_expiration") date_expiration = args.get("date_expiration")
if isinstance(date_expiration, str): if isinstance(date_expiration, str):
@ -287,27 +293,33 @@ class User(UserMixin, db.Model, ScoDocModel):
if date_expiration if date_expiration
else None else None
) )
# booléens:
for field in ("active", "cas_allow_login", "cas_allow_scodoc_login"): for field in ("active", "cas_allow_login", "cas_allow_scodoc_login"):
if field in args: if field in args:
args_dict[field] = scu.to_bool(args.get(field)) args_dict[field] = scu.to_bool(args.get(field))
# chaines ne devant pas être NULLs
for field in ("nom", "prenom"):
if field in args:
args[field] = args[field] or ""
return args_dict return args_dict
def from_dict(self, data: dict, new_user=False): def from_dict(self, data: dict, new_user=False):
"""Set users' attributes from given dict values. """Set users' attributes from given dict values.
Roles must be encoded as "roles_string", like "Ens_RT, Secr_CJ" - roles_string : roles, encoded like "Ens_RT, Secr_CJ"
- date_expiration is a dateime object.
Does not check permissions here. Does not check permissions here.
""" """
super().from_dict(data, excluded=("user_name", "roles_string"))
if new_user: if new_user:
if "user_name" in data: if "user_name" in data:
# never change name of existing users # never change name of existing users
if invalid_user_name(data["user_name"]):
raise ValueError(f"invalid user_name: {data['user_name']}")
self.user_name = data["user_name"] self.user_name = data["user_name"]
if "password" in data: if "password" in data:
self.set_password(data["password"]) self.set_password(data["password"])
if invalid_user_name(self.user_name):
raise ValueError(f"invalid user_name: {self.user_name}")
# Roles: roles_string is "Ens_RT, Secr_RT, ..." # Roles: roles_string is "Ens_RT, Secr_RT, ..."
if "roles_string" in data: if "roles_string" in data:
self.user_roles = [] self.user_roles = []
@ -316,6 +328,8 @@ class User(UserMixin, db.Model, ScoDocModel):
role, dept = UserRole.role_dept_from_string(r_d) role, dept = UserRole.role_dept_from_string(r_d)
self.add_role(role, dept) self.add_role(role, dept)
super().from_dict(data, excluded={"user_name", "roles_string", "roles"})
# Set cas_id using regexp if configured: # Set cas_id using regexp if configured:
exp = ScoDocSiteConfig.get("cas_uid_from_mail_regexp") exp = ScoDocSiteConfig.get("cas_uid_from_mail_regexp")
if exp and self.email_institutionnel: if exp and self.email_institutionnel:

View File

@ -254,7 +254,7 @@ def import_users(users, force="") -> tuple[bool, list[str], int]:
if import_ok: if import_ok:
for u in created.values(): for u in created.values():
# Création de l'utilisateur (via SQLAlchemy) # Création de l'utilisateur (via SQLAlchemy)
user = User() user = User(user_name=u["user_name"])
user.from_dict(u, new_user=True) user.from_dict(u, new_user=True)
db.session.add(user) db.session.add(user)
db.session.commit() db.session.commit()

View File

@ -432,15 +432,3 @@ def check_modif_user(
) )
# Roles ? # Roles ?
return True, "" return True, ""
def user_edit(user_name, vals):
"""Edit the user specified by user_name
(ported from Zope to SQLAlchemy, hence strange !)
"""
u: User = User.query.filter_by(user_name=user_name).first()
if not u:
raise ScoValueError("Invalid user_name")
u.from_dict(vals)
db.session.add(u)
db.session.commit()

View File

@ -9,10 +9,12 @@
<div class="user_basics"> <div class="user_basics">
<b>Login :</b> {{user.user_name}}<br> <b>Login :</b> {{user.user_name}}<br>
<b>CAS id:</b> {{user.cas_id or "(aucun)"}} <b>CAS id:</b> {{user.cas_id or "(aucun)"}}
{% if ScoDocSiteConfig.is_cas_enabled() %}
(CAS {{'autorisé' if user.cas_allow_login else 'interdit'}} pour cet utilisateur) (CAS {{'autorisé' if user.cas_allow_login else 'interdit'}} pour cet utilisateur)
{% if user.cas_allow_scodoc_login %} {% if user.cas_allow_scodoc_login %}
(connexion sans CAS autorisée) (connexion sans CAS autorisée)
{% endif %} {% endif %}
{% endif %}
<br> <br>
<b>Nom :</b> {{user.nom or ""}}<br> <b>Nom :</b> {{user.nom or ""}}<br>
<b>Prénom :</b> {{user.prenom or ""}}<br> <b>Prénom :</b> {{user.prenom or ""}}<br>

View File

@ -701,10 +701,12 @@ def create_user_form(user_name=None, edit=0, all_roles=True):
log(f"sco_users: editing {user_name} by {current_user.user_name}") log(f"sco_users: editing {user_name} by {current_user.user_name}")
log(f"sco_users: previous_values={initvalues}") log(f"sco_users: previous_values={initvalues}")
log(f"sco_users: new_values={vals}") log(f"sco_users: new_values={vals}")
sco_users.user_edit(user_name, vals)
flash(f"Utilisateur {user_name} modifié")
else: else:
sco_users.user_edit(user_name, {"roles_string": vals["roles_string"]}) vals = {"roles_string": vals["roles_string"]}
the_user.from_dict(vals)
db.session.add(the_user)
db.session.commit()
flash(f"Utilisateur {user_name} modifié")
return flask.redirect( return flask.redirect(
url_for( url_for(
"users.user_info_page", "users.user_info_page",
@ -760,7 +762,7 @@ def create_user_form(user_name=None, edit=0, all_roles=True):
log( log(
f"""sco_users: new_user {vals["user_name"]} by {current_user.user_name}""" f"""sco_users: new_user {vals["user_name"]} by {current_user.user_name}"""
) )
the_user = User() the_user = User(user_name=user_name)
the_user.from_dict(vals, new_user=True) the_user.from_dict(vals, new_user=True)
db.session.add(the_user) db.session.add(the_user)
db.session.commit() db.session.commit()
@ -927,11 +929,12 @@ def user_info_page(user_name=None):
return render_template( return render_template(
"auth/user_info_page.j2", "auth/user_info_page.j2",
user=user,
title=f"Utilisateur {user.user_name}",
Permission=Permission,
dept=dept, dept=dept,
Permission=Permission,
ScoDocSiteConfig=ScoDocSiteConfig,
session_info=session_info, session_info=session_info,
title=f"Utilisateur {user.user_name}",
user=user,
) )

View File

@ -128,19 +128,18 @@ def test_create_delete(test_client):
def test_edit(test_client): def test_edit(test_client):
"test edition object utlisateur" "test edition object utlisateur"
args = { args = {
"user_name": "Tonari",
"prenom": "No Totoro", "prenom": "No Totoro",
"edt_id": "totorito", "edt_id": "totorito",
"cas_allow_login": 1, # boolean "cas_allow_login": 1, # boolean
"irrelevant": "..", # intentionnellement en dehors des attributs "irrelevant": "..", # intentionnellement en dehors des attributs
} }
u = User() u = User(user_name="Tonari")
u.from_dict(args) u.from_dict(args)
db.session.add(u) db.session.add(u)
db.session.commit() db.session.commit()
db.session.refresh(u) db.session.refresh(u)
assert u.edt_id == "totorito" assert u.edt_id == "totorito"
assert u.nom is None assert u.nom == ""
assert u.cas_allow_login is True assert u.cas_allow_login is True
d = u.to_dict() d = u.to_dict()
assert d["nom"] == "" assert d["nom"] == ""