forked from ScoDoc/ScoDoc
Utilisateurs:
- désactive automatiquement les comptes scodoc7 avec temp=1 - améliore table export et affichages. - améliore log (et préfixe par 'auth: ')
This commit is contained in:
parent
e65c60873f
commit
6872c20344
@ -19,7 +19,7 @@ from werkzeug.security import generate_password_hash, check_password_hash
|
||||
|
||||
import jwt
|
||||
|
||||
from app import db, log, login
|
||||
from app import db, email, log, login
|
||||
from app.models import Departement
|
||||
from app.models import SHORT_STR_LEN, USERNAME_STR_LEN
|
||||
from app.models.config import ScoDocSiteConfig
|
||||
@ -85,6 +85,8 @@ class User(UserMixin, db.Model):
|
||||
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)
|
||||
"""champ obsolete. Si connexion alors que passwd_temp est vrai,
|
||||
efface mot de passe et redirige vers accueil."""
|
||||
token = db.Column(db.Text(), index=True, unique=True)
|
||||
token_expiration = db.Column(db.DateTime)
|
||||
|
||||
@ -121,7 +123,8 @@ class User(UserMixin, db.Model):
|
||||
# current_app.logger.info("creating user with roles={}".format(self.roles))
|
||||
|
||||
def __repr__(self):
|
||||
return f"<User {self.user_name} id={self.id} dept={self.dept}{' (inactive)' if not self.active else ''}>"
|
||||
return f"""<User {self.user_name} id={self.id} dept={self.dept}{
|
||||
' (inactive)' if not self.active else ''}>"""
|
||||
|
||||
def __str__(self):
|
||||
return self.user_name
|
||||
@ -140,6 +143,19 @@ class User(UserMixin, db.Model):
|
||||
Returns `True` if the password matched, `False` otherwise.
|
||||
"""
|
||||
if not self.active: # inactived users can't login
|
||||
current_app.logger.warning(
|
||||
f"auth: login attempt from inactive account {self}"
|
||||
)
|
||||
return False
|
||||
if self.passwd_temp:
|
||||
# Anciens comptes ScoDoc 7 non migrés
|
||||
# désactive le compte par sécurité.
|
||||
current_app.logger.warning(f"auth: desactivating legacy account {self}")
|
||||
self.active = False
|
||||
self.passwd_temp = True
|
||||
db.session.add(self)
|
||||
db.session.commit()
|
||||
send_notif_desactivation_user(self)
|
||||
return False
|
||||
|
||||
# if CAS activated and forced, allow only super-user and users with cas_allow_scodoc_login
|
||||
@ -158,7 +174,9 @@ class User(UserMixin, db.Model):
|
||||
def _migrate_scodoc7_password(self, password) -> bool:
|
||||
"""After migration, rehash password."""
|
||||
if scu.check_scodoc7_password(self.password_scodoc7, password):
|
||||
current_app.logger.warning(f"migrating legacy ScoDoc7 password for {self}")
|
||||
current_app.logger.warning(
|
||||
f"auth: migrating legacy ScoDoc7 password for {self}"
|
||||
)
|
||||
self.set_password(password)
|
||||
self.password_scodoc7 = None
|
||||
db.session.add(self)
|
||||
@ -182,7 +200,7 @@ class User(UserMixin, db.Model):
|
||||
token, current_app.config["SECRET_KEY"], algorithms=["HS256"]
|
||||
)
|
||||
except jwt.exceptions.ExpiredSignatureError:
|
||||
log(f"verify_reset_password_token: token expired")
|
||||
log("verify_reset_password_token: token expired")
|
||||
except:
|
||||
return None
|
||||
try:
|
||||
@ -387,24 +405,17 @@ class User(UserMixin, db.Model):
|
||||
"""nomplogin est le nom en majuscules suivi du prénom et du login
|
||||
e.g. Dupont Pierre (dupont)
|
||||
"""
|
||||
if self.nom:
|
||||
n = sco_etud.format_nom(self.nom)
|
||||
else:
|
||||
n = self.user_name.upper()
|
||||
return "%s %s (%s)" % (
|
||||
n,
|
||||
sco_etud.format_prenom(self.prenom),
|
||||
self.user_name,
|
||||
)
|
||||
nom = sco_etud.format_nom(self.nom) if self.nom else self.user_name.upper()
|
||||
return f"{nom} {sco_etud.format_prenom(self.prenom)} ({self.user_name})"
|
||||
|
||||
@staticmethod
|
||||
def get_user_id_from_nomplogin(nomplogin: str) -> Optional[int]:
|
||||
"""Returns id from the string "Dupont Pierre (dupont)"
|
||||
or None if user does not exist
|
||||
"""
|
||||
m = re.match(r".*\((.*)\)", nomplogin.strip())
|
||||
if m:
|
||||
user_name = m.group(1)
|
||||
match = re.match(r".*\((.*)\)", nomplogin.strip())
|
||||
if match:
|
||||
user_name = match.group(1)
|
||||
u = User.query.filter_by(user_name=user_name).first()
|
||||
if u:
|
||||
return u.id
|
||||
@ -441,6 +452,8 @@ class User(UserMixin, db.Model):
|
||||
|
||||
|
||||
class AnonymousUser(AnonymousUserMixin):
|
||||
"Notre utilisateur anonyme"
|
||||
|
||||
def has_permission(self, perm, dept=None):
|
||||
return False
|
||||
|
||||
@ -569,7 +582,7 @@ class UserRole(db.Model):
|
||||
# maxsplit=1, le dept peut contenir un "_"
|
||||
if len(fields) != 2:
|
||||
current_app.logger.warning(
|
||||
f"role_dept_from_string: Invalid role_dept '{role_dept}'"
|
||||
f"auth: role_dept_from_string: Invalid role_dept '{role_dept}'"
|
||||
)
|
||||
raise ScoValueError("Invalid role_dept")
|
||||
role_name, dept = fields
|
||||
@ -596,3 +609,23 @@ def get_super_admin():
|
||||
)
|
||||
assert admin_user
|
||||
return admin_user
|
||||
|
||||
|
||||
def send_notif_desactivation_user(user: User):
|
||||
"""Envoi un message mail de notification à l'admin et à l'adresse du compte désactivé"""
|
||||
if not user.email:
|
||||
return
|
||||
txt = [
|
||||
f"""Le compte ScoDoc '{user.user_name}' associé à votre adresse <{user.email}>""",
|
||||
"""a été désactivé par le système car son mot de passe n'était pas valide.\n""",
|
||||
"""Contactez votre responsable pour le ré-activer.\n""",
|
||||
"""Ceci est un message automatique, ne pas répondre.""",
|
||||
]
|
||||
txt = "\n".join(txt)
|
||||
email.send_email(
|
||||
f"ScoDoc: désactivation automatique du compte {user.user_name}",
|
||||
email.get_from_addr(),
|
||||
[user.email, current_app.config.get("SCODOC_ADMIN_MAIL")],
|
||||
txt,
|
||||
)
|
||||
return txt
|
||||
|
@ -279,16 +279,6 @@ def sco_header(
|
||||
# Barre menu semestre:
|
||||
H.append(formsemestre_page_title(formsemestre_id))
|
||||
|
||||
# Avertissement si mot de passe à changer
|
||||
if user_check:
|
||||
if current_user.passwd_temp:
|
||||
H.append(
|
||||
f"""<div class="passwd_warn">
|
||||
Attention !<br>
|
||||
Vous avez reçu un mot de passe temporaire.<br>
|
||||
Vous devez le changer: <a href="{scu.UsersURL}/form_change_password?user_name={current_user.user_name}">cliquez ici</a>
|
||||
</div>"""
|
||||
)
|
||||
#
|
||||
if head_message:
|
||||
H.append('<div class="head_message">' + html.escape(head_message) + "</div>")
|
||||
|
@ -141,8 +141,13 @@ def list_users(
|
||||
d["_prenom_target"] = target
|
||||
|
||||
# Hide passwd modification date (depending on visitor's permission)
|
||||
if not can_modify:
|
||||
if can_modify:
|
||||
d["non_migre"] = (
|
||||
"NON MIGRÉ" if u.passwd_temp or u.password_scodoc7 else "ok"
|
||||
)
|
||||
else:
|
||||
d["date_modif_passwd"] = "(non visible)"
|
||||
d["non_migre"] = ""
|
||||
|
||||
columns_ids = [
|
||||
"user_name",
|
||||
@ -153,7 +158,7 @@ def list_users(
|
||||
"roles_string",
|
||||
"date_expiration",
|
||||
"date_modif_passwd",
|
||||
"passwd_temp",
|
||||
"non_migre",
|
||||
"status_txt",
|
||||
]
|
||||
# Seul l'admin peut voir les dates de dernière connexion
|
||||
@ -182,7 +187,7 @@ def list_users(
|
||||
"date_expiration": "Expiration",
|
||||
"date_modif_passwd": "Modif. mot de passe",
|
||||
"last_seen": "Dernière cnx.",
|
||||
"passwd_temp": "Temp.",
|
||||
"non_migre": "Non migré (!)",
|
||||
"status_txt": "Etat",
|
||||
"cas_id": "Id CAS",
|
||||
"cas_allow_login": "CAS autorisé",
|
||||
|
@ -5,7 +5,7 @@
|
||||
{% macro render_field(field, auth_name=None) %}
|
||||
<tr style="">
|
||||
{% if auth_name %}
|
||||
<td class="wtf-field"> {{ field.label }}<span style="font-weight:700;"> ({{ auth_name }}):</span></td>
|
||||
<td class="wtf-field"> {{ field.label }}<span style="font-weight:700;"> (compte {{ auth_name }}):</span></td>
|
||||
{% else %}
|
||||
<td class="wtf-field">{{ field.label }}</td>
|
||||
{% endif %}
|
||||
|
@ -17,8 +17,12 @@
|
||||
<b>Nom :</b> {{user.nom or ""}}<br>
|
||||
<b>Prénom :</b> {{user.prenom or ""}}<br>
|
||||
<b>Mail :</b> {{user.email}}<br>
|
||||
<b>Roles :</b> {{user.get_roles_string()}}<br>
|
||||
<b>Dept :</b> {{user.dept or ""}}
|
||||
<b>Rôles :</b> {{user.get_roles_string()}}<br>
|
||||
<b>Dept :</b> {{user.dept or ""}}<br>
|
||||
{% if user.passwd_temp or user.password_scodoc7 %}
|
||||
<b class="fontred">⚠️ mot de passe invalide (compte ancien non migré à réactiver ou à fermer)</b><br>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% if current_user.is_administrator() %}
|
||||
<div class="user_info_admin">
|
||||
|
@ -83,7 +83,7 @@ class ChangePasswordForm(FlaskForm):
|
||||
"""formulaire changement mot de passe et mail"""
|
||||
|
||||
user_name = HiddenField()
|
||||
old_password = PasswordField(_l("Identifiez-vous"))
|
||||
old_password = PasswordField(_l("Mot de passe actuel"))
|
||||
new_password = PasswordField(_l("Nouveau mot de passe de l'utilisateur"))
|
||||
bis_password = PasswordField(
|
||||
_l("Répéter"),
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
SCOVERSION = "9.4.59"
|
||||
SCOVERSION = "9.4.60"
|
||||
|
||||
SCONAME = "ScoDoc"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user