forked from ScoDoc/ScoDoc
User: clarifie code vérif. login ScoDoc si CAS, et adapte messages reset passwd
This commit is contained in:
parent
60d31a5e6f
commit
2967596de2
@ -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"],
|
||||
|
@ -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"))
|
||||
|
||||
|
@ -1,4 +1,7 @@
|
||||
|
||||
<p>Bonjour {{ user.user_name }},</p>
|
||||
|
||||
{% if token %}
|
||||
<p>
|
||||
Pour réinitialiser votre mot de passe ScoDoc,
|
||||
<a href="{{ url_for('auth.reset_password', token=token, _external=True) }}">
|
||||
@ -8,6 +11,14 @@
|
||||
<p>Vous pouvez aussi copier ce lien dans votre navigateur Web:</p>
|
||||
<p>{{ url_for('auth.reset_password', token=token, _external=True) }}</p>
|
||||
|
||||
{% else %}
|
||||
<p>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).
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
|
||||
<p>Si vous n'avez pas demandé à réinitialiser votre mot de passe sur
|
||||
ScoDoc, vous pouvez simplement ignorer ce message.
|
||||
</p>
|
||||
|
@ -1,10 +1,19 @@
|
||||
Bonjour {{ user.user_name }},
|
||||
|
||||
{% if token %}
|
||||
|
||||
Pour réinitialiser votre mot de passe ScoDoc, suivre le lien:
|
||||
|
||||
{{ url_for('auth.reset_password', token=token, _external=True) }}
|
||||
|
||||
|
||||
{% else %}
|
||||
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).
|
||||
|
||||
{% endif %}
|
||||
|
||||
Si vous n'avez pas demandé à réinitialiser votre mot de passe sur
|
||||
ScoDoc, vous pouvez simplement ignorer ce message.
|
||||
|
||||
|
@ -5,20 +5,21 @@
|
||||
<p>
|
||||
Votre identifiant ScoDoc est: <b>{{ user.user_name }}</b>
|
||||
</p>
|
||||
{% if cas_force %}
|
||||
<p>
|
||||
Pour vous connecter, vous devrez utiliser votre identifiant universitaire
|
||||
sur le système d'authentification de votre établissement (CAS, ENT).
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% if token %}
|
||||
<p>Pour initialiser votre mot de passe ScoDoc,
|
||||
<a href="{{ url_for('auth.reset_password', token=token, _external=True) }}">
|
||||
cliquez sur ce lien
|
||||
</a>.
|
||||
</p>
|
||||
<p>Vous pouvez aussi copier ce lien dans votre navigateur Web:</p>
|
||||
<p>{{ url_for('auth.reset_password', token=token, _external=True) }}</p>
|
||||
<p>Pour initialiser votre mot de passe ScoDoc,
|
||||
<a href="{{ url_for('auth.reset_password', token=token, _external=True) }}">
|
||||
cliquez sur ce lien
|
||||
</a>.
|
||||
</p>
|
||||
<p>Vous pouvez aussi copier ce lien dans votre navigateur Web:</p>
|
||||
<p>{{ url_for('auth.reset_password', token=token, _external=True) }}</p>
|
||||
|
||||
<p>Ce lien expirera le {{date_expiration_token}}</p>
|
||||
{% else %}
|
||||
<p>Pour vous connecter, vous devrez utiliser votre identifiant universitaire
|
||||
sur le système d'authentification de votre établissement (CAS, ENT).
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<p>A bientôt !</p>
|
||||
|
@ -3,16 +3,16 @@ Bienvenue {{ user.prenom }} {{ user.nom }},
|
||||
Votre accès à ScoDoc vient d'être validé.
|
||||
Votre identifiant ScoDoc est: {{ user.user_name }}
|
||||
|
||||
{% if cas_force %}
|
||||
<p>
|
||||
Pour vous connecter, vous devrez utiliser votre identifiant universitaire
|
||||
sur le système d'authentification de votre établissement (CAS, ENT).
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% if token %}
|
||||
Pour initialiser votre mot de passe ScoDoc, suivre le lien:
|
||||
{{ url_for('auth.reset_password', token=token, _external=True) }}
|
||||
|
||||
Pour initialiser votre mot de passe ScoDoc, suivre le lien:
|
||||
{{ url_for('auth.reset_password', token=token, _external=True) }}
|
||||
|
||||
Ce lien expirera le {{date_expiration_token}}
|
||||
{% else %}
|
||||
|
||||
Pour vous connecter, vous devrez utiliser votre identifiant universitaire
|
||||
sur le système d'authentification de votre établissement (CAS, ENT).
|
||||
{% endif %}
|
||||
|
||||
A bientôt !
|
||||
|
@ -35,6 +35,7 @@ Emmanuel Viennet, 2021
|
||||
"""
|
||||
import datetime
|
||||
import re
|
||||
import time
|
||||
from enum import auto, IntEnum
|
||||
from xml.etree import ElementTree
|
||||
|
||||
@ -50,11 +51,14 @@ from wtforms.validators import DataRequired, Email, ValidationError, EqualTo
|
||||
from app import db
|
||||
from app import email
|
||||
from app.auth.forms import DeactivateUserForm
|
||||
from app.auth.models import Permission
|
||||
from app.auth.models import User
|
||||
from app.auth.models import Role
|
||||
from app.auth.models import UserRole
|
||||
from app.auth.models import is_valid_password
|
||||
from app.auth.models import (
|
||||
DEFAULT_RESET_TOKEN_DURATION,
|
||||
Permission,
|
||||
User,
|
||||
Role,
|
||||
UserRole,
|
||||
is_valid_password,
|
||||
)
|
||||
from app.models import Departement
|
||||
from app.models.config import ScoDocSiteConfig
|
||||
|
||||
@ -64,10 +68,13 @@ from app.decorators import (
|
||||
permission_required,
|
||||
)
|
||||
|
||||
from app.scodoc import sco_import_users, sco_roles_default
|
||||
from app.scodoc import sco_users
|
||||
from app.scodoc import sco_utils as scu
|
||||
from app.scodoc import sco_xml
|
||||
from app.scodoc import (
|
||||
sco_import_users,
|
||||
sco_roles_default,
|
||||
sco_users,
|
||||
sco_utils as scu,
|
||||
sco_xml,
|
||||
)
|
||||
from app import log
|
||||
from app.scodoc.sco_exceptions import AccessDenied, ScoPermissionDenied, ScoValueError
|
||||
from app.scodoc.sco_import_users import generate_password
|
||||
@ -779,11 +786,9 @@ def create_user_form(user_name=None, edit=0, all_roles=True):
|
||||
# B: envoi de welcome seulement (mot de passe saisie dans le formulaire)
|
||||
# C: Aucun envoi (mot de passe saisi dans le formulaire)
|
||||
if vals["welcome"]: # "Envoie un mail d'accueil" coché
|
||||
if vals["reset_password"] and (
|
||||
(not ScoDocSiteConfig.get("cas_force"))
|
||||
or vals.get("cas_allow_scodoc_login", False)
|
||||
):
|
||||
# nb: si login scodoc non autorisé car CAS seul, n'envoie pas le mot de passe.
|
||||
if vals["reset_password"]:
|
||||
# nb: le token ne sera envoyé que si le login ScoDoc est autorisé,
|
||||
# voir get_reset_password_token()
|
||||
mode = Mode.WELCOME_AND_CHANGE_PASSWORD
|
||||
else:
|
||||
mode = Mode.WELCOME_ONLY
|
||||
@ -828,13 +833,10 @@ def create_user_form(user_name=None, edit=0, all_roles=True):
|
||||
db.session.commit()
|
||||
# envoi éventuel d'un message
|
||||
if mode in (Mode.WELCOME_AND_CHANGE_PASSWORD, Mode.WELCOME_ONLY):
|
||||
token = (
|
||||
the_user.get_reset_password_token()
|
||||
if mode == Mode.WELCOME_AND_CHANGE_PASSWORD
|
||||
else None
|
||||
)
|
||||
|
||||
cas_force = ScoDocSiteConfig.get("cas_force")
|
||||
token = the_user.get_reset_password_token()
|
||||
date_expiration_token = datetime.datetime.fromtimestamp(
|
||||
time.time() + DEFAULT_RESET_TOKEN_DURATION
|
||||
).strftime(scu.DATEATIME_FMT)
|
||||
# Le from doit utiliser la préférence du département de l'utilisateur
|
||||
email.send_email(
|
||||
"[ScoDoc] Création de votre compte",
|
||||
@ -844,13 +846,13 @@ def create_user_form(user_name=None, edit=0, all_roles=True):
|
||||
"email/welcome.txt",
|
||||
user=the_user,
|
||||
token=token,
|
||||
cas_force=cas_force,
|
||||
date_expiration_token=date_expiration_token,
|
||||
),
|
||||
html_body=render_template(
|
||||
"email/welcome.j2",
|
||||
user=the_user,
|
||||
token=token,
|
||||
cas_force=cas_force,
|
||||
date_expiration_token=date_expiration_token,
|
||||
),
|
||||
)
|
||||
flash(f"Mail accueil envoyé à {the_user.email}")
|
||||
|
Loading…
x
Reference in New Issue
Block a user