diff --git a/app/auth/models.py b/app/auth/models.py index 1022e90b8..e6bebcb85 100644 --- a/app/auth/models.py +++ b/app/auth/models.py @@ -38,6 +38,7 @@ from app.scodoc.sco_roles_default import SCO_ROLES_DEFAULTS import app.scodoc.sco_utils as scu VALID_LOGIN_EXP = re.compile(r"^[a-zA-Z0-9@\\\-_\.]+$") +DEFAULT_RESET_TOKEN_DURATION = 24 * 60 * 60 # seconds (default 24h) def is_valid_password(cleartxt) -> bool: @@ -178,6 +179,43 @@ class User(UserMixin, ScoDocModel): raise ValueError("invalid user_id") return query.first_or_404() if not accept_none else query.first() + def can_login_using_scodoc(self) -> bool: + """True si l'utilisateur peut (essayer de) se connecter avec son compte local ScoDoc + (si par ailleurs un mot de passe valide existe et que le compte est actif) + + Toujours vrai pour le super-admin. + Si CAS activé and cas_id renseigné, il faut cas_allow_scodoc_login. + + Réglages possibles: + - Global : cas_force CAS forcé pour tous sauf super-admin + - Par utilisateur: + - cas_allow_login : Peut-on se logguer via le CAS ? + - cas_allow_scodoc_login : Si CAS activé, peut-on se logguer sur ScoDoc ? + + """ + if self.is_administrator(): + return True # super admin ou autorisation individuelle + cas_enabled = ScoDocSiteConfig.is_cas_enabled() + if not cas_enabled: + return True # CAS not enabled + + if not self.cas_allow_scodoc_login: + log( + f"""auth: {self.user_name + }: cas enabled, scodoc login not allowed""" + ) + return False + + if ScoDocSiteConfig.is_cas_forced() and self.cas_id and self.cas_allow_login: + log( + f"""auth: {self.user_name + } (cas_id='{ + self.cas_id}'): cas forced and cas_id set: scodoc login not allowed""" + ) + return False + + return True + def set_password(self, password: str): "Set password" log(f"set_password({self})") @@ -197,6 +235,7 @@ class User(UserMixin, ScoDocModel): def check_password(self, password: str) -> bool: """Check given password vs current one. Returns `True` if the password matched, `False` otherwise. + Also checks for temporary passwords and if CAS disables scodoc login. """ if not self.active: # inactived users can't login current_app.logger.warning( @@ -214,28 +253,8 @@ class User(UserMixin, ScoDocModel): send_notif_desactivation_user(self) return False - # if CAS activated and cas_id, allow only super-user and users with cas_allow_scodoc_login - cas_enabled = ScoDocSiteConfig.is_cas_enabled() - if cas_enabled and not self.is_administrator(): - if not self.cas_allow_scodoc_login: - # CAS activé et compte non autorisé à se logguer sur ScoDoc - log( - f"""auth: login attempt for user {self.user_name}: scodoc login not allowed - """ - ) - return False - # si CAS activé et forcé et cas_id renseigné, on ne peut pas se logguer - if ( - self.cas_id - and self.cas_allow_login - and ScoDocSiteConfig.get("cas_force") - ): - log( - f"""auth: login attempt for user {self.user_name - } (cas_id='{ - self.cas_id}'): cas forced and cas_id set: scodoc login not allowed""" - ) - return False + if not self.can_login_using_scodoc(): + return False if not self.password_hash: # user without password can't login if self.password_scodoc7: @@ -258,10 +277,16 @@ class User(UserMixin, ScoDocModel): return True return False - def get_reset_password_token(self, expires_in=24 * 60 * 60): + def get_reset_password_token( + self, expires_in=DEFAULT_RESET_TOKEN_DURATION + ) -> str | None: """Un token pour réinitialiser son mot de passe. Par défaut valide durant 24 heures. + Note: si le CAS est obligatoire pour l'utilisateur, renvoie None """ + # si la config CAS interdit le login ScoDoc, pas de token + if not self.can_login_using_scodoc(): + return None token = jwt.encode( {"reset_password": self.id, "exp": time() + expires_in}, current_app.config["SECRET_KEY"], diff --git a/app/auth/routes.py b/app/auth/routes.py index 6adb9a1e8..51c2a939c 100644 --- a/app/auth/routes.py +++ b/app/auth/routes.py @@ -87,7 +87,7 @@ def login(): if current_user.is_authenticated: return redirect(url_for("scodoc.index")) - if ScoDocSiteConfig.get("cas_force"): + if ScoDocSiteConfig.is_cas_forced(): current_app.logger.info("login: forcing CAS") return redirect(url_for("cas.login")) diff --git a/app/templates/email/reset_password.j2 b/app/templates/email/reset_password.j2 index 9e92a785e..44fcfbf33 100644 --- a/app/templates/email/reset_password.j2 +++ b/app/templates/email/reset_password.j2 @@ -1,4 +1,7 @@ +
Bonjour {{ user.user_name }},
+ +{% if token %}
Pour réinitialiser votre mot de passe ScoDoc,
@@ -8,6 +11,14 @@
Vous pouvez aussi copier ce lien dans votre navigateur Web: {{ url_for('auth.reset_password', token=token, _external=True) }} Vous ne pouvez pas changer votre mot de passe sur ScoDoc:
+en effet, pour vous connecter, vous devez utiliser votre identifiant universitaire
+sur le système d'authentification de votre établissement (CAS, ENT).
+ Si vous n'avez pas demandé à réinitialiser votre mot de passe sur
ScoDoc, vous pouvez simplement ignorer ce message.
Votre identifiant ScoDoc est: {{ user.user_name }}
- Pour vous connecter, vous devrez utiliser votre identifiant universitaire
- sur le système d'authentification de votre établissement (CAS, ENT).
- Pour initialiser votre mot de passe ScoDoc,
-
- cliquez sur ce lien
- .
- Vous pouvez aussi copier ce lien dans votre navigateur Web: {{ url_for('auth.reset_password', token=token, _external=True) }} Pour initialiser votre mot de passe ScoDoc,
+
+ cliquez sur ce lien
+ .
+ Vous pouvez aussi copier ce lien dans votre navigateur Web: {{ url_for('auth.reset_password', token=token, _external=True) }} Ce lien expirera le {{date_expiration_token}} Pour vous connecter, vous devrez utiliser votre identifiant universitaire
+ sur le système d'authentification de votre établissement (CAS, ENT).
+ A bientôt !
- Pour vous connecter, vous devrez utiliser votre identifiant universitaire
- sur le système d'authentification de votre établissement (CAS, ENT).
-