forked from ScoDoc/ScoDoc
CAS: options cas_force et cas_allow_scodoc_login, améliorations diverses.
This commit is contained in:
parent
dc13e8bba5
commit
5ca85a9da9
@ -22,6 +22,7 @@ import jwt
|
|||||||
from app import db, log, login
|
from app import db, log, login
|
||||||
from app.models import Departement
|
from app.models import Departement
|
||||||
from app.models import SHORT_STR_LEN
|
from app.models import SHORT_STR_LEN
|
||||||
|
from app.models.config import ScoDocSiteConfig
|
||||||
from app.scodoc.sco_exceptions import ScoValueError
|
from app.scodoc.sco_exceptions import ScoValueError
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
from app.scodoc.sco_roles_default import SCO_ROLES_DEFAULTS
|
from app.scodoc.sco_roles_default import SCO_ROLES_DEFAULTS
|
||||||
@ -71,9 +72,8 @@ class User(UserMixin, db.Model):
|
|||||||
cas_allow_scodoc_login = db.Column(
|
cas_allow_scodoc_login = db.Column(
|
||||||
db.Boolean, default=False, server_default="false", nullable=False
|
db.Boolean, default=False, server_default="false", nullable=False
|
||||||
)
|
)
|
||||||
"""(not yet implemented XXX)
|
"""Si CAS forcé (cas_force), peut-on se logguer sur ScoDoc directement ?
|
||||||
si CAS activé, peut-on se logguer sur ScoDoc directement ?
|
(le rôle ScoSuperAdmin peut toujours, mettre à True pour les utilisateur API)
|
||||||
(le rôle ScoSuperAdmin peut toujours)
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
password_hash = db.Column(db.String(128))
|
password_hash = db.Column(db.String(128))
|
||||||
@ -133,28 +133,37 @@ class User(UserMixin, db.Model):
|
|||||||
self.password_hash = None
|
self.password_hash = None
|
||||||
self.passwd_temp = False
|
self.passwd_temp = False
|
||||||
|
|
||||||
def check_password(self, password):
|
def check_password(self, password: str) -> bool:
|
||||||
"""Check given password vs current one.
|
"""Check given password vs current one.
|
||||||
Returns `True` if the password matched, `False` otherwise.
|
Returns `True` if the password matched, `False` otherwise.
|
||||||
"""
|
"""
|
||||||
if not self.active: # inactived users can't login
|
if not self.active: # inactived users can't login
|
||||||
return False
|
return False
|
||||||
if (not self.password_hash) and self.password_scodoc7:
|
|
||||||
# Special case: user freshly migrated from ScoDoc7
|
# if CAS activated and forced, allow only super-user and users with cas_allow_scodoc_login
|
||||||
if scu.check_scodoc7_password(self.password_scodoc7, password):
|
if ScoDocSiteConfig.is_cas_enabled() and ScoDocSiteConfig.get("cas_force"):
|
||||||
current_app.logger.warning(
|
if (not self.is_administrator()) and not self.cas_allow_scodoc_login:
|
||||||
f"migrating legacy ScoDoc7 password for {self}"
|
return False
|
||||||
)
|
|
||||||
self.set_password(password)
|
|
||||||
self.password_scodoc7 = None
|
|
||||||
db.session.add(self)
|
|
||||||
db.session.commit()
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
if not self.password_hash: # user without password can't login
|
if not self.password_hash: # user without password can't login
|
||||||
|
if self.password_scodoc7:
|
||||||
|
# Special case: user freshly migrated from ScoDoc7
|
||||||
|
return self._migrate_scodoc7_password(password)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return check_password_hash(self.password_hash, password)
|
return check_password_hash(self.password_hash, password)
|
||||||
|
|
||||||
|
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}")
|
||||||
|
self.set_password(password)
|
||||||
|
self.password_scodoc7 = None
|
||||||
|
db.session.add(self)
|
||||||
|
db.session.commit()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def get_reset_password_token(self, expires_in=600):
|
def get_reset_password_token(self, expires_in=600):
|
||||||
"Un token pour réinitialiser son mot de passe"
|
"Un token pour réinitialiser son mot de passe"
|
||||||
return jwt.encode(
|
return jwt.encode(
|
||||||
|
@ -27,12 +27,8 @@ _ = lambda x: x # sans babel
|
|||||||
_l = _
|
_l = _
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/login", methods=["GET", "POST"])
|
def _login_form():
|
||||||
def login():
|
"""le formulaire de login, avec un lien CAS s'il est configuré."""
|
||||||
"ScoDoc Login form"
|
|
||||||
if current_user.is_authenticated:
|
|
||||||
return redirect(url_for("scodoc.index"))
|
|
||||||
|
|
||||||
form = LoginForm()
|
form = LoginForm()
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
user = User.query.filter_by(user_name=form.user_name.data).first()
|
user = User.query.filter_by(user_name=form.user_name.data).first()
|
||||||
@ -40,9 +36,12 @@ def login():
|
|||||||
current_app.logger.info("login: invalid (%s)", form.user_name.data)
|
current_app.logger.info("login: invalid (%s)", form.user_name.data)
|
||||||
flash(_("Nom ou mot de passe invalide"))
|
flash(_("Nom ou mot de passe invalide"))
|
||||||
return redirect(url_for("auth.login"))
|
return redirect(url_for("auth.login"))
|
||||||
|
|
||||||
login_user(user, remember=form.remember_me.data)
|
login_user(user, remember=form.remember_me.data)
|
||||||
|
|
||||||
current_app.logger.info("login: success (%s)", form.user_name.data)
|
current_app.logger.info("login: success (%s)", form.user_name.data)
|
||||||
return form.redirect("scodoc.index")
|
return form.redirect("scodoc.index")
|
||||||
|
|
||||||
message = request.args.get("message", "")
|
message = request.args.get("message", "")
|
||||||
return render_template(
|
return render_template(
|
||||||
"auth/login.j2",
|
"auth/login.j2",
|
||||||
@ -53,6 +52,32 @@ def login():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/login", methods=["GET", "POST"])
|
||||||
|
def login():
|
||||||
|
"""ScoDoc Login form
|
||||||
|
Si paramètre cas_force, redirige vers le CAS.
|
||||||
|
"""
|
||||||
|
if current_user.is_authenticated:
|
||||||
|
return redirect(url_for("scodoc.index"))
|
||||||
|
|
||||||
|
if ScoDocSiteConfig.get("cas_force"):
|
||||||
|
current_app.logger.info("login: forcing CAS")
|
||||||
|
return redirect(url_for("cas.login"))
|
||||||
|
|
||||||
|
return _login_form()
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/login_scodoc", methods=["GET", "POST"])
|
||||||
|
def login_scodoc():
|
||||||
|
"""ScoDoc Login form.
|
||||||
|
Formulaire login, sans redirection immédiate sur CAS si ce dernier est configuré.
|
||||||
|
Sans CAS, ce formulaire est identique à /login
|
||||||
|
"""
|
||||||
|
if current_user.is_authenticated:
|
||||||
|
return redirect(url_for("scodoc.index"))
|
||||||
|
return _login_form()
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/logout")
|
@bp.route("/logout")
|
||||||
def logout() -> flask.Response:
|
def logout() -> flask.Response:
|
||||||
"Logout a scodoc user. If CAS session, logout from CAS. Redirect."
|
"Logout a scodoc user. If CAS session, logout from CAS. Redirect."
|
||||||
|
@ -96,7 +96,7 @@ def permission_required(permission):
|
|||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
def permission_required_compat_scodoc7(permission):
|
def permission_required_compat_scodoc7(permission): # XXX TODO A SUPPRIMER
|
||||||
"""Décorateur pour les fonctions utilisées comme API dans ScoDoc 7
|
"""Décorateur pour les fonctions utilisées comme API dans ScoDoc 7
|
||||||
Comme @permission_required mais autorise de passer directement
|
Comme @permission_required mais autorise de passer directement
|
||||||
les informations d'auth en paramètres:
|
les informations d'auth en paramètres:
|
||||||
|
@ -26,17 +26,20 @@
|
|||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Formulaires configuration Exports Apogée (codes)
|
Formulaire configuration CAS
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import BooleanField, SubmitField
|
from wtforms import BooleanField, SubmitField
|
||||||
from wtforms.fields.simple import FileField, StringField, TextAreaField
|
from wtforms.fields.simple import FileField, StringField
|
||||||
|
|
||||||
|
|
||||||
class ConfigCASForm(FlaskForm):
|
class ConfigCASForm(FlaskForm):
|
||||||
"Formulaire paramétrage CAS"
|
"Formulaire paramétrage CAS"
|
||||||
cas_enable = BooleanField("activer le CAS")
|
cas_enable = BooleanField("Activer le CAS")
|
||||||
|
cas_force = BooleanField(
|
||||||
|
"Forcer l'utilisation de CAS (tous les utilisateurs seront redirigés vers le CAS)"
|
||||||
|
)
|
||||||
|
|
||||||
cas_server = StringField(
|
cas_server = StringField(
|
||||||
label="URL du serveur CAS",
|
label="URL du serveur CAS",
|
||||||
|
@ -214,20 +214,6 @@ class ScoDocSiteConfig(db.Model):
|
|||||||
cfg = ScoDocSiteConfig.query.filter_by(name="cas_enable").first()
|
cfg = ScoDocSiteConfig.query.filter_by(name="cas_enable").first()
|
||||||
return cfg is not None and cfg.value
|
return cfg is not None and cfg.value
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def cas_enable(cls, enabled=True) -> bool:
|
|
||||||
"""Active (ou déactive) le CAS. True si changement."""
|
|
||||||
if enabled != ScoDocSiteConfig.is_cas_enabled():
|
|
||||||
cfg = ScoDocSiteConfig.query.filter_by(name="cas_enable").first()
|
|
||||||
if cfg is None:
|
|
||||||
cfg = ScoDocSiteConfig(name="cas_enable", value="on" if enabled else "")
|
|
||||||
else:
|
|
||||||
cfg.value = "on" if enabled else ""
|
|
||||||
db.session.add(cfg)
|
|
||||||
db.session.commit()
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_entreprises_enabled(cls) -> bool:
|
def is_entreprises_enabled(cls) -> bool:
|
||||||
"""True si on doit activer le module entreprise"""
|
"""True si on doit activer le module entreprise"""
|
||||||
@ -259,10 +245,11 @@ class ScoDocSiteConfig(db.Model):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def set(cls, name: str, value: str) -> bool:
|
def set(cls, name: str, value: str) -> bool:
|
||||||
"Set parameter, returns True if change. Commit session."
|
"Set parameter, returns True if change. Commit session."
|
||||||
if cls.get(name) != (value or ""):
|
value_str = str(value or "")
|
||||||
|
if (cls.get(name) or "") != value_str:
|
||||||
cfg = ScoDocSiteConfig.query.filter_by(name=name).first()
|
cfg = ScoDocSiteConfig.query.filter_by(name=name).first()
|
||||||
if cfg is None:
|
if cfg is None:
|
||||||
cfg = ScoDocSiteConfig(name=name, value=str(value))
|
cfg = ScoDocSiteConfig(name=name, value=value_str)
|
||||||
else:
|
else:
|
||||||
cfg.value = str(value or "")
|
cfg.value = str(value or "")
|
||||||
current_app.logger.info(
|
current_app.logger.info(
|
||||||
|
@ -226,8 +226,6 @@ _identiteEditor = ndb.EditableTable(
|
|||||||
"nom_usuel",
|
"nom_usuel",
|
||||||
"prenom",
|
"prenom",
|
||||||
"cas_id",
|
"cas_id",
|
||||||
"cas_allow_login",
|
|
||||||
"cas_allow_scodoc_login",
|
|
||||||
"civilite", # 'M", "F", or "X"
|
"civilite", # 'M", "F", or "X"
|
||||||
"date_naissance",
|
"date_naissance",
|
||||||
"lieu_naissance",
|
"lieu_naissance",
|
||||||
|
@ -31,7 +31,7 @@ import random
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
from email.mime.multipart import MIMEMultipart
|
from email.mime.multipart import MIMEMultipart
|
||||||
from flask import g, url_for
|
from flask import url_for
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
@ -41,30 +41,34 @@ import app.scodoc.sco_utils as scu
|
|||||||
from app import log
|
from app import log
|
||||||
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
|
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
|
||||||
from app.scodoc import sco_excel
|
from app.scodoc import sco_excel
|
||||||
from app.scodoc import sco_preferences
|
|
||||||
from app.scodoc import sco_users
|
from app.scodoc import sco_users
|
||||||
|
|
||||||
|
|
||||||
TITLES = ("user_name", "nom", "prenom", "email", "roles", "dept")
|
TITLES = ("user_name", "nom", "prenom", "email", "roles", "dept", "cas_id")
|
||||||
COMMENTS = (
|
COMMENTS = (
|
||||||
"""user_name:
|
"""user_name:
|
||||||
|
L'identifiant (login).
|
||||||
Composé de lettres (minuscules ou majuscules), de chiffres ou du caractère _
|
Composé de lettres (minuscules ou majuscules), de chiffres ou du caractère _
|
||||||
""",
|
""",
|
||||||
"""nom:
|
"""nom:
|
||||||
Maximum 64 caractères""",
|
Maximum 64 caractères.""",
|
||||||
"""prenom:
|
"""prenom:
|
||||||
Maximum 64 caractères""",
|
Maximum 64 caractères.""",
|
||||||
"""email:
|
"""email:
|
||||||
Maximum 120 caractères""",
|
Maximum 120 caractères.""",
|
||||||
"""roles:
|
"""roles:
|
||||||
un plusieurs rôles séparés par ','
|
un plusieurs rôles séparés par ','
|
||||||
chaque role est fait de 2 composantes séparées par _:
|
chaque rôle est fait de 2 composantes séparées par _:
|
||||||
1. Le role (Ens, Secr ou Admin)
|
1. Le rôle (Ens, Secr ou Admin)
|
||||||
2. Le département (en majuscule)
|
2. Le département (en majuscule)
|
||||||
Exemple: "Ens_RT,Admin_INFO"
|
Exemple: "Ens_RT,Admin_INFO".
|
||||||
""",
|
""",
|
||||||
"""dept:
|
"""dept:
|
||||||
Le département d'appartenance du l'utillsateur. Laisser vide si l'utilisateur intervient dans plusieurs dépatements
|
Le département d'appartenance de l'utilisateur. Laisser vide si l'utilisateur intervient dans plusieurs départements.
|
||||||
|
""",
|
||||||
|
"""cas_id:
|
||||||
|
|
||||||
|
Identifiant de l'utilisateur sur CAS (optionnel).
|
||||||
""",
|
""",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -423,9 +423,9 @@ class BasePreferences(object):
|
|||||||
"email_chefdpt",
|
"email_chefdpt",
|
||||||
{
|
{
|
||||||
"initvalue": "",
|
"initvalue": "",
|
||||||
"title": "e-mail chef du département",
|
"title": "e-mail du chef du département",
|
||||||
"size": 40,
|
"size": 40,
|
||||||
"explanation": "utilisé pour envoi mail notification absences",
|
"explanation": "pour lui envoyer des notifications sur les absences",
|
||||||
"category": "abs",
|
"category": "abs",
|
||||||
"only_global": True,
|
"only_global": True,
|
||||||
},
|
},
|
||||||
|
@ -63,7 +63,7 @@ def index_html(all_depts=False, with_inactives=False, format="html"):
|
|||||||
)
|
)
|
||||||
if current_user.is_administrator():
|
if current_user.is_administrator():
|
||||||
H.append(
|
H.append(
|
||||||
""" <a href="{url_for("users.import_users_form",
|
f""" <a href="{url_for("users.import_users_form",
|
||||||
scodoc_dept=g.scodoc_dept)
|
scodoc_dept=g.scodoc_dept)
|
||||||
}" class="stdlink">Importer des utilisateurs</a></p>"""
|
}" class="stdlink">Importer des utilisateurs</a></p>"""
|
||||||
)
|
)
|
||||||
|
@ -4545,6 +4545,11 @@ table.formation_table_recap td.heures_tp {
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.cas_link {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
div.cas_etat_certif_ssl {
|
div.cas_etat_certif_ssl {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
|
@ -10,18 +10,20 @@
|
|||||||
|
|
||||||
<h1>Connexion</h1>
|
<h1>Connexion</h1>
|
||||||
|
|
||||||
{% if is_cas_enabled %}
|
|
||||||
<div class"cas_link">
|
|
||||||
<a href="{{ url_for('cas.login') }}" class="stdlink">Se connecter avec CAS</a>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
{{ wtf.quick_form(form) }}
|
{{ wtf.quick_form(form) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if is_cas_enabled %}
|
||||||
|
<div class="cas_link">
|
||||||
|
ou bien <a href="{{ url_for('cas.login') }}" class="stdlink">se connecter avec CAS</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
En cas d'oubli de votre mot de passe
|
En cas d'oubli de votre mot de passe ScoDoc
|
||||||
<a href="{{ url_for('auth.reset_password_request') }}">cliquez ici pour le réinitialiser</a>.
|
<a href="{{ url_for('auth.reset_password_request') }}">cliquez ici pour le réinitialiser</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -9,6 +9,9 @@
|
|||||||
<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)"}}
|
||||||
(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 %}
|
||||||
|
(connexion sans CAS autorisée)
|
||||||
|
{% 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>
|
||||||
|
@ -18,8 +18,16 @@
|
|||||||
non chargé.
|
non chargé.
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
<div style="margin-top:16px;">
|
||||||
|
ℹ️ <em>Note: si le CAS est forcé, le super-admin et les utilisateurs autorisés
|
||||||
|
à "se connecter via ScoDoc" pourront toujours se
|
||||||
|
connecter via l'adresse spéciale</em>
|
||||||
|
<tt style="color: blue;">{{url_for("auth.login_scodoc", _external=True)}}</tt>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -144,8 +144,10 @@ def config_cas():
|
|||||||
if request.method == "POST" and form.cancel.data: # cancel button
|
if request.method == "POST" and form.cancel.data: # cancel button
|
||||||
return redirect(url_for("scodoc.index"))
|
return redirect(url_for("scodoc.index"))
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
if ScoDocSiteConfig.cas_enable(enabled=form.data["cas_enable"]):
|
if ScoDocSiteConfig.set("cas_enable", form.data["cas_enable"]):
|
||||||
flash("CAS " + ("activé" if form.data["cas_enable"] else "désactivé"))
|
flash("CAS " + ("activé" if form.data["cas_enable"] else "désactivé"))
|
||||||
|
if ScoDocSiteConfig.set("cas_force", form.data["cas_force"]):
|
||||||
|
flash("CAS " + ("forcé" if form.data["cas_force"] else "non forcé"))
|
||||||
if ScoDocSiteConfig.set("cas_server", form.data["cas_server"]):
|
if ScoDocSiteConfig.set("cas_server", form.data["cas_server"]):
|
||||||
flash("Serveur CAS enregistré")
|
flash("Serveur CAS enregistré")
|
||||||
if ScoDocSiteConfig.set("cas_attribute_id", form.data["cas_attribute_id"]):
|
if ScoDocSiteConfig.set("cas_attribute_id", form.data["cas_attribute_id"]):
|
||||||
@ -165,6 +167,7 @@ def config_cas():
|
|||||||
|
|
||||||
elif request.method == "GET":
|
elif request.method == "GET":
|
||||||
form.cas_enable.data = ScoDocSiteConfig.get("cas_enable")
|
form.cas_enable.data = ScoDocSiteConfig.get("cas_enable")
|
||||||
|
form.cas_force.data = ScoDocSiteConfig.get("cas_force")
|
||||||
form.cas_server.data = ScoDocSiteConfig.get("cas_server")
|
form.cas_server.data = ScoDocSiteConfig.get("cas_server")
|
||||||
form.cas_attribute_id.data = ScoDocSiteConfig.get("cas_attribute_id")
|
form.cas_attribute_id.data = ScoDocSiteConfig.get("cas_attribute_id")
|
||||||
form.cas_ssl_verify.data = ScoDocSiteConfig.get("cas_ssl_verify")
|
form.cas_ssl_verify.data = ScoDocSiteConfig.get("cas_ssl_verify")
|
||||||
|
@ -403,7 +403,16 @@ def create_user_form(user_name=None, edit=0, all_roles=True):
|
|||||||
{
|
{
|
||||||
"title": "Autorise connexion via CAS",
|
"title": "Autorise connexion via CAS",
|
||||||
"input_type": "boolcheckbox",
|
"input_type": "boolcheckbox",
|
||||||
"explanation": "en test: seul le super-administrateur peut changer ce réglage",
|
"explanation": " seul le super-administrateur peut changer ce réglage",
|
||||||
|
"readonly": not current_user.is_administrator(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"cas_allow_scodoc_login",
|
||||||
|
{
|
||||||
|
"title": "Autorise connexion via ScoDoc",
|
||||||
|
"input_type": "boolcheckbox",
|
||||||
|
"explanation": " seul le super-administrateur peut changer ce réglage",
|
||||||
"readonly": not current_user.is_administrator(),
|
"readonly": not current_user.is_administrator(),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -764,8 +773,9 @@ def import_users_form():
|
|||||||
<li>envoi à chaque utilisateur de son <b>mot de passe initial par mail</b>.</li>
|
<li>envoi à chaque utilisateur de son <b>mot de passe initial par mail</b>.</li>
|
||||||
</ol>"""
|
</ol>"""
|
||||||
H.append(
|
H.append(
|
||||||
"""<ol><li><a class="stdlink" href="import_users_generate_excel_sample">
|
f"""<ol><li><a class="stdlink" href="{
|
||||||
Obtenir la feuille excel à remplir</a></li><li>"""
|
url_for("users.import_users_generate_excel_sample", scodoc_dept=g.scodoc_dept)
|
||||||
|
}">Obtenir la feuille excel à remplir</a></li><li>"""
|
||||||
)
|
)
|
||||||
F = html_sco_header.sco_footer()
|
F = html_sco_header.sco_footer()
|
||||||
tf = TrivialFormulator(
|
tf = TrivialFormulator(
|
||||||
|
@ -516,7 +516,6 @@ def photos_import_files(formsemestre_id: int, xlsfile: str, zipfile: str):
|
|||||||
"""Import des photos d'étudiants à partir d'une liste excel et d'un zip avec les images."""
|
"""Import des photos d'étudiants à partir d'une liste excel et d'un zip avec les images."""
|
||||||
import app as mapp
|
import app as mapp
|
||||||
from app.scodoc import sco_trombino, sco_photos
|
from app.scodoc import sco_trombino, sco_photos
|
||||||
from flask_login import login_user
|
|
||||||
from app.auth.models import get_super_admin
|
from app.auth.models import get_super_admin
|
||||||
|
|
||||||
sem = mapp.models.formsemestre.FormSemestre.query.get(formsemestre_id)
|
sem = mapp.models.formsemestre.FormSemestre.query.get(formsemestre_id)
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Ré-écriture de test_users avec pytest.
|
Ré-écriture de test_users avec pytest.
|
||||||
|
|
||||||
Usage: pytest tests/unit/test_users.py
|
Usage: pytest tests/unit/test_users.py
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@ -24,7 +24,7 @@ def test_password_hashing(test_client):
|
|||||||
db.session.add(u)
|
db.session.add(u)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
# nota: default attributes values, like active,
|
# nota: default attributes values, like active,
|
||||||
# are not set before the first commit() (?)
|
# are not set before the first commit()
|
||||||
assert u.active
|
assert u.active
|
||||||
u.set_password("cat")
|
u.set_password("cat")
|
||||||
assert not u.check_password("dog")
|
assert not u.check_password("dog")
|
||||||
@ -62,7 +62,7 @@ def test_roles_permissions(test_client):
|
|||||||
|
|
||||||
|
|
||||||
def test_users_roles(test_client):
|
def test_users_roles(test_client):
|
||||||
dept = "XX"
|
dept = DEPT
|
||||||
perm = Permission.ScoAbsChange
|
perm = Permission.ScoAbsChange
|
||||||
perm2 = Permission.ScoView
|
perm2 = Permission.ScoView
|
||||||
u = User(user_name="un_enseignant")
|
u = User(user_name="un_enseignant")
|
||||||
@ -97,14 +97,14 @@ def test_users_roles(test_client):
|
|||||||
|
|
||||||
|
|
||||||
def test_user_admin(test_client):
|
def test_user_admin(test_client):
|
||||||
dept = "XX"
|
dept = DEPT
|
||||||
perm = 0x1234 # a random perm
|
perm = 0x1234 # a random perm
|
||||||
u = User(user_name="un_admin", email=current_app.config["SCODOC_ADMIN_MAIL"])
|
u = User(user_name="un_admin", email=current_app.config["SCODOC_ADMIN_MAIL"])
|
||||||
db.session.add(u)
|
db.session.add(u)
|
||||||
assert len(u.roles) == 1
|
assert len(u.roles) == 1
|
||||||
assert u.has_permission(perm, dept)
|
assert u.has_permission(perm, dept)
|
||||||
# Le grand admin a accès à tous les départements:
|
# Le grand admin a accès à tous les départements:
|
||||||
assert u.has_permission(perm, dept + "XX")
|
assert u.has_permission(perm, dept + DEPT)
|
||||||
assert u.roles[0].name == "SuperAdmin"
|
assert u.roles[0].name == "SuperAdmin"
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user