Création/édition d'utilisateurs: renforce warnings (mail inst., cas_id).

This commit is contained in:
Emmanuel Viennet 2024-11-26 15:17:32 +01:00
parent ea1e1a1ad1
commit 9205c17b7e
2 changed files with 44 additions and 9 deletions

View File

@ -12,7 +12,7 @@ from typing import Optional
import cracklib # pylint: disable=import-error import cracklib # pylint: disable=import-error
from flask import current_app, g from flask import current_app, flash, g
from flask_login import UserMixin, AnonymousUserMixin from flask_login import UserMixin, AnonymousUserMixin
from sqlalchemy.exc import ( from sqlalchemy.exc import (
IntegrityError, IntegrityError,
@ -67,6 +67,14 @@ def is_valid_user_name(user_name: str) -> bool:
) )
def is_new_cas_id(cas_id: str) -> bool:
"Check that cas_id is a valid new id (uniqueness, allow nulls)"
if not cas_id:
return True
nb_with_this_id = db.session.query(User).filter_by(cas_id=cas_id).count()
return nb_with_this_id == 0
class User(UserMixin, ScoDocModel): class User(UserMixin, ScoDocModel):
"""ScoDoc users, handled by Flask / SQLAlchemy""" """ScoDoc users, handled by Flask / SQLAlchemy"""
@ -178,13 +186,16 @@ class User(UserMixin, ScoDocModel):
raise ValueError("invalid user_id") raise ValueError("invalid user_id")
return query.first_or_404() if not accept_none else query.first() return query.first_or_404() if not accept_none else query.first()
def set_password(self, password): def set_password(self, password: str):
"Set password" "Set password"
log(f"set_password({self})") log(f"set_password({self})")
previous_hash = self.password_hash
if password: if password:
self.password_hash = generate_password_hash(password) self.password_hash = generate_password_hash(password)
else: else:
self.password_hash = None self.password_hash = None
if self.password_hash != previous_hash:
self.date_modif_passwd = datetime.now()
# La création d'un mot de passe efface l'éventuel mot de passe historique # La création d'un mot de passe efface l'éventuel mot de passe historique
self.password_scodoc7 = None self.password_scodoc7 = None
self.passwd_temp = False self.passwd_temp = False
@ -350,9 +361,7 @@ class User(UserMixin, ScoDocModel):
date_expiration = args.get("date_expiration") date_expiration = args.get("date_expiration")
if isinstance(date_expiration, str): if isinstance(date_expiration, str):
args["date_expiration"] = ( args["date_expiration"] = (
datetime.fromisoformat(date_expiration) datetime.fromisoformat(date_expiration) if date_expiration else None
if date_expiration
else None
) )
# booléens: # booléens:
for field in ("active", "cas_allow_login", "cas_allow_scodoc_login"): for field in ("active", "cas_allow_login", "cas_allow_scodoc_login"):
@ -394,17 +403,43 @@ class User(UserMixin, 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(args, excluded={"user_name", "roles_string", "roles"}) # email_institutionnel may not be unique, but check and warns user
email_institutionnel = args.get("email_institutionnel")
if email_institutionnel and email_institutionnel != self.email_institutionnel:
nb_with_this_mail = (
db.session.query(User)
.filter_by(email_institutionnel=email_institutionnel)
.count()
)
if nb_with_this_mail > 0:
log(
"User.from_dict: plusieurs utilisateurs avec ce mail institutionnel"
)
flash(
"Attention: plusieurs utilisateurs avec ce mail institutionnel",
"warning",
)
super().from_dict(
args, excluded={"cas_id", "user_name", "roles_string", "roles"}
)
new_cas_id = args.get("cas_id")
if ScoDocSiteConfig.cas_uid_use_scodoc(): if ScoDocSiteConfig.cas_uid_use_scodoc():
self.cas_id = self.user_name new_cas_id = self.user_name
else: else:
# 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:
cas_id = ScoDocSiteConfig.extract_cas_id(self.email_institutionnel) cas_id = ScoDocSiteConfig.extract_cas_id(self.email_institutionnel)
if cas_id: if cas_id:
self.cas_id = cas_id new_cas_id = cas_id
if new_cas_id != self.cas_id:
if is_new_cas_id(new_cas_id):
self.cas_id = new_cas_id
else:
log(f"User.from_dict: CAS id invalide pour {self.user_name}")
raise ScoValueError(f"CAS id invalide pour {self.user_name}")
def get_token(self, expires_in=3600): def get_token(self, expires_in=3600):
"Un jeton pour cet user. Stocké en base, non commité." "Un jeton pour cet user. Stocké en base, non commité."

View File

@ -39,7 +39,7 @@
{% endif %} {% endif %}
<div class="user_basics"> <div class="user_basics">
<b>Dernière modif mot de passe:</b> <b>Dernière modif mot de passe:</b>
{{user.date_modif_passwd.strftime(scu.DATE_FMT) if user.date_modif_passwd else ""}}<br> {{user.date_modif_passwd.strftime(scu.DATEATIME_FMT) if user.date_modif_passwd else ""}}<br>
<b>Date d'expiration:</b> <b>Date d'expiration:</b>
{{user.date_expiration.strftime(scu.DATE_FMT) if user.date_expiration else "(sans limite)"}} {{user.date_expiration.strftime(scu.DATE_FMT) if user.date_expiration else "(sans limite)"}}
</div> </div>