forked from ScoDoc/ScoDoc
CAS: calcul du cas_id à partir du mail via regexp
This commit is contained in:
parent
d869c3d938
commit
f2549a24e7
@ -306,6 +306,13 @@ class User(UserMixin, db.Model):
|
||||
role, dept = UserRole.role_dept_from_string(r_d)
|
||||
self.add_role(role, dept)
|
||||
|
||||
# Set cas_id using regexp if configured:
|
||||
exp = ScoDocSiteConfig.get("cas_uid_from_mail_regexp")
|
||||
if exp and self.email_institutionnel:
|
||||
cas_id = ScoDocSiteConfig.extract_cas_id(self.email_institutionnel)
|
||||
if cas_id is not None:
|
||||
self.cas_id = cas_id
|
||||
|
||||
def get_token(self, expires_in=3600):
|
||||
"Un jeton pour cet user. Stocké en base, non commité."
|
||||
now = datetime.utcnow()
|
||||
|
@ -30,8 +30,17 @@ Formulaire configuration CAS
|
||||
"""
|
||||
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import BooleanField, SubmitField
|
||||
from wtforms import BooleanField, SubmitField, ValidationError
|
||||
from wtforms.fields.simple import FileField, StringField
|
||||
from wtforms.validators import Optional
|
||||
|
||||
from app.models import ScoDocSiteConfig
|
||||
|
||||
|
||||
def check_cas_uid_from_mail_regexp(form, field):
|
||||
"Vérifie la regexp fournie pur l'extraction du CAS id"
|
||||
if not ScoDocSiteConfig.cas_uid_from_mail_regexp_is_valid(field.data):
|
||||
raise ValidationError("expression régulière invalide")
|
||||
|
||||
|
||||
class ConfigCASForm(FlaskForm):
|
||||
@ -50,7 +59,8 @@ class ConfigCASForm(FlaskForm):
|
||||
)
|
||||
cas_login_route = StringField(
|
||||
label="Route du login CAS",
|
||||
description="""ajouté à l'URL du serveur: exemple <tt>/cas</tt> (si commence par <tt>/</tt>, part de la racine)""",
|
||||
description="""ajouté à l'URL du serveur: exemple <tt>/cas</tt>
|
||||
(si commence par <tt>/</tt>, part de la racine)""",
|
||||
default="/cas",
|
||||
)
|
||||
cas_logout_route = StringField(
|
||||
@ -70,6 +80,18 @@ class ConfigCASForm(FlaskForm):
|
||||
comptes utilisateurs.""",
|
||||
)
|
||||
|
||||
cas_uid_from_mail_regexp = StringField(
|
||||
label="Expression pour extraire l'identifiant utilisateur",
|
||||
description="""regexp python appliquée au mail institutionnel de l'utilisateur,
|
||||
dont le premier groupe doit donner l'identifiant CAS.
|
||||
Si non fournie, le super-admin devra saisir cet identifiant pour chaque compte.
|
||||
Par exemple, <tt>(.*)@</tt> indique que le mail sans le domaine (donc toute
|
||||
la partie avant le <tt>@</tt>) est l'identifiant.
|
||||
Pour prendre le mail complet, utiliser <tt>(.*)</tt>.
|
||||
""",
|
||||
validators=[Optional(), check_cas_uid_from_mail_regexp],
|
||||
)
|
||||
|
||||
cas_ssl_verify = BooleanField("Vérification du certificat SSL")
|
||||
cas_ssl_certificate_file = FileField(
|
||||
label="Certificat (PEM)",
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
import json
|
||||
import urllib.parse
|
||||
import re
|
||||
|
||||
from flask import flash
|
||||
from app import current_app, db, log
|
||||
@ -103,6 +104,7 @@ class ScoDocSiteConfig(db.Model):
|
||||
"cas_logout_route": str,
|
||||
"cas_validate_route": str,
|
||||
"cas_attribute_id": str,
|
||||
"cas_uid_from_mail_regexp": str,
|
||||
# Assiduité
|
||||
"morning_time": str,
|
||||
"lunch_time": str,
|
||||
@ -395,6 +397,41 @@ class ScoDocSiteConfig(db.Model):
|
||||
data_links = json.dumps(links_dict)
|
||||
cls.set("personalized_links", data_links)
|
||||
|
||||
@classmethod
|
||||
def extract_cas_id(cls, email_addr: str) -> str | None:
|
||||
"Extract cas_id from maill, using regexp in config. None if not possible."
|
||||
exp = cls.get("cas_uid_from_mail_regexp")
|
||||
if not exp or not email_addr:
|
||||
return None
|
||||
try:
|
||||
match = re.search(exp, email_addr)
|
||||
except re.error:
|
||||
log("error extracting CAS id from '{email_addr}' using regexp '{exp}'")
|
||||
return None
|
||||
if not match:
|
||||
log("no match extracting CAS id from '{email_addr}' using regexp '{exp}'")
|
||||
return None
|
||||
try:
|
||||
cas_id = match.group(1)
|
||||
except IndexError:
|
||||
log(
|
||||
"no group found extracting CAS id from '{email_addr}' using regexp '{exp}'"
|
||||
)
|
||||
return None
|
||||
return cas_id
|
||||
|
||||
@classmethod
|
||||
def cas_uid_from_mail_regexp_is_valid(cls, exp: str) -> bool:
|
||||
"True si l'expression régulière semble valide"
|
||||
# check that it compiles
|
||||
try:
|
||||
pattern = re.compile(exp)
|
||||
except re.error:
|
||||
return False
|
||||
# and returns at least one group on a simple cannonical address
|
||||
match = pattern.search("emmanuel@exemple.fr")
|
||||
return len(match.groups()) > 0
|
||||
|
||||
@classmethod
|
||||
def assi_get_rounded_time(cls, label: str, default: str) -> float:
|
||||
"Donne l'heure stockée dans la config globale sous label, en float arrondi au quart d'heure"
|
||||
|
@ -23,6 +23,7 @@
|
||||
{{ wtf.form_field(form.cas_logout_route) }}
|
||||
{{ wtf.form_field(form.cas_validate_route) }}
|
||||
{{ wtf.form_field(form.cas_attribute_id) }}
|
||||
{{ wtf.form_field(form.cas_uid_from_mail_regexp) }}
|
||||
<div class="cas_settings">
|
||||
{{ wtf.form_field(form.cas_ssl_verify) }}
|
||||
{{ wtf.form_field(form.cas_ssl_certificate_file) }}
|
||||
|
@ -173,6 +173,10 @@ def config_cas():
|
||||
flash("Route de validation CAS enregistrée")
|
||||
if ScoDocSiteConfig.set("cas_attribute_id", form.data["cas_attribute_id"]):
|
||||
flash("Attribut CAS ID enregistré")
|
||||
if ScoDocSiteConfig.set(
|
||||
"cas_uid_from_mail_regexp", form.data["cas_uid_from_mail_regexp"]
|
||||
):
|
||||
flash("Expression extraction identifiant CAS enregistrée")
|
||||
if ScoDocSiteConfig.set("cas_ssl_verify", form.data["cas_ssl_verify"]):
|
||||
flash("Vérification SSL modifiée")
|
||||
if form.cas_ssl_certificate_file.data:
|
||||
@ -197,13 +201,16 @@ def config_cas():
|
||||
form.cas_logout_route.data = ScoDocSiteConfig.get("cas_logout_route")
|
||||
form.cas_validate_route.data = ScoDocSiteConfig.get("cas_validate_route")
|
||||
form.cas_attribute_id.data = ScoDocSiteConfig.get("cas_attribute_id")
|
||||
form.cas_ssl_verify.data = ScoDocSiteConfig.get("cas_ssl_verify")
|
||||
return render_template(
|
||||
"config_cas.j2",
|
||||
form=form,
|
||||
title="Configuration du Service d'Authentification Central (CAS)",
|
||||
cas_ssl_certificate_loaded=ScoDocSiteConfig.get("cas_ssl_certificate"),
|
||||
form.cas_uid_from_mail_regexp.data = ScoDocSiteConfig.get(
|
||||
"cas_uid_from_mail_regexp"
|
||||
)
|
||||
form.cas_ssl_verify.data = ScoDocSiteConfig.get("cas_ssl_verify")
|
||||
return render_template(
|
||||
"config_cas.j2",
|
||||
form=form,
|
||||
title="Configuration du Service d'Authentification Central (CAS)",
|
||||
cas_ssl_certificate_loaded=ScoDocSiteConfig.get("cas_ssl_certificate"),
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/ScoDoc/config_assiduites", methods=["GET", "POST"])
|
||||
|
@ -400,9 +400,16 @@ def create_user_form(user_name=None, edit=0, all_roles=True):
|
||||
"title": "Identifiant CAS",
|
||||
"input_type": "text",
|
||||
"explanation": "id du compte utilisateur sur le CAS de l'établissement "
|
||||
+ "(service CAS activé)"
|
||||
if cas_enabled
|
||||
else "(service CAS non activé)",
|
||||
+ (
|
||||
"(<b>sera déduit de son e-mail institutionnel</b>) "
|
||||
if ScoDocSiteConfig.get("cas_uid_from_mail_regexp")
|
||||
else ""
|
||||
)
|
||||
+ (
|
||||
"(service CAS activé)"
|
||||
if cas_enabled
|
||||
else "(service CAS non activé)"
|
||||
),
|
||||
"size": 36,
|
||||
"allow_null": True,
|
||||
"readonly": not cas_enabled
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
SCOVERSION = "9.6.34"
|
||||
SCOVERSION = "9.6.35"
|
||||
|
||||
SCONAME = "ScoDoc"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user