CAS: ajout option pour utiliser par défaut le même uid ScoDoc et CAS + cosmetic formulaires

This commit is contained in:
Emmanuel Viennet 2024-09-15 16:50:25 +02:00
parent c38b8aa297
commit 9ae2181904
10 changed files with 327 additions and 250 deletions

View File

@ -355,12 +355,15 @@ class User(UserMixin, ScoDocModel):
super().from_dict(data, excluded={"user_name", "roles_string", "roles"})
# 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:
self.cas_id = cas_id
if ScoDocSiteConfig.cas_uid_use_scodoc():
self.cas_id = self.user_name
else:
# 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:
self.cas_id = cas_id
def get_token(self, expires_in=3600):
"Un jeton pour cet user. Stocké en base, non commité."

View File

@ -98,6 +98,15 @@ class ConfigCASForm(FlaskForm):
validators=[Optional(), check_cas_uid_from_mail_regexp],
)
cas_uid_use_scodoc = BooleanField(
"Utiliser l'identifiant ScoDoc comme identifiant CAS",
description="""Si coché, l'identifiant ScoDoc sera utilisé comme identifiant CAS,
sans transformation. Cette option est utile si les identifiants ScoDoc sont déjà
des identifiants CAS.
Dans ce cas, l'adresse mail (réglage ci-dessus) n'est pas utilisée.
""",
)
cas_edt_id_from_xml_regexp = StringField(
label="Optionnel: expression pour extraire l'identifiant edt",
description="""regexp python appliquée à la réponse XML du serveur CAS pour

View File

@ -105,6 +105,7 @@ class ScoDocSiteConfig(db.Model):
"cas_validate_route": str,
"cas_attribute_id": str,
"cas_uid_from_mail_regexp": str,
"cas_uid_use_scodoc": bool,
"cas_edt_id_from_xml_regexp": str,
# Assiduité
"morning_time": str,
@ -239,6 +240,12 @@ class ScoDocSiteConfig(db.Model):
cfg = ScoDocSiteConfig.query.filter_by(name="cas_force").first()
return cfg is not None and cfg.value
@classmethod
def cas_uid_use_scodoc(cls) -> bool:
"""True si cas_uid_use_scodoc"""
cfg = ScoDocSiteConfig.query.filter_by(name="cas_uid_use_scodoc").first()
return cfg is not None and cfg.value
@classmethod
def is_entreprises_enabled(cls) -> bool:
"""True si on doit activer le module entreprise"""
@ -404,7 +411,7 @@ class ScoDocSiteConfig(db.Model):
@classmethod
def extract_cas_id(cls, email_addr: str) -> str | None:
"Extract cas_id from maill, using regexp in config. None if not possible."
"Extract cas_id from mail, 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

View File

@ -1246,7 +1246,8 @@ a.discretelink:hover {
text-align: center;
}
.help {
.help,
.help-block {
max-width: var(--sco-content-max-width);
font-style: italic;
}
@ -1260,6 +1261,10 @@ a.discretelink:hover {
color: red;
}
div.help-block {
margin-bottom: 16px;
}
div.sco_box,
div.sco_help {
margin-top: 12px;
@ -4919,7 +4924,6 @@ table.formation_table_recap td.heures_tp {
}
div.cas_settings {
margin-left: -15px;
margin-bottom: 8px;
border: 1px dashed rgb(191, 34, 191);
background-color: #feb4e54f;

View File

@ -430,11 +430,21 @@ textarea {
color: #333;
}
form.form div.checkbox {
margin-top: 6px;
margin-bottom: 6px;
}
div.form-group {
margin-top: 16px;
margin-bottom: 6px;
}
.form-group input,
.form-control {
width: 100%;
padding: 10px;
margin-bottom: 16px;
margin-bottom: 4px;
border: 1px solid #ced4da;
border-radius: 4px;
font-size: 16px;

View File

@ -4,7 +4,7 @@
{% block app_content %}
<h1>Chargement des configurations CAS des utilisateurs</h1>
<div style="max-width: 800px;">
<div class="scobox help explanation">
<p style="color: red">A utiliser pour modifier le paramétrage CAS de
<b>comptes utilisateurs existants</b>
</p>
@ -32,21 +32,26 @@
<li style="margin-bottom:32px;">Revenez sur cette page et chargez le fichier dans ScoDoc.
</li>
</ol>
</div>
<div class="scobox">
<div class="scobox-title">Étape 1: exporter fichier Excel à charger</div>
<ul>
<li><a class="stdlink" href="{{
url_for('auth.cas_users_generate_excel_sample')
}}">Obtenir la feuille excel à remplir</a>,
avec la liste complète des utilisateurs.
</li>
</ul>
</div>
<div class="scobox">
<div class="scobox-title">Étape 2: charger le fichier Excel modifié</div>
<ul>
<li><b>Étape 1: </b><a class="stdlink" href="{{
url_for('auth.cas_users_generate_excel_sample')
}}">Obtenir la feuille excel à remplir</a>,
avec la liste complète des utilisateurs.
</li>
<li style="margin-top: 16px;"><b>Étape 2:</b>
<div class="row">
<div class="col-md-8">
{{ wtf.quick_form(form) }}
</div>
</div>
</li>
</ul>
</div>
{% endblock %}

View File

@ -1,6 +1,14 @@
{% extends "base.j2" %}
{% import 'wtf.j2' as wtf %}
{% block styles %}
{{super()}}
<style>
div.checkbox label { font-weight: normal; }
</style>
{% endblock %}
{% block app_content %}
<h1>Configuration du Service d'Authentification Central (CAS)</h1>
@ -18,14 +26,33 @@
{{ wtf.form_field(form.cas_enable) }}
{{ wtf.form_field(form.cas_force) }}
{{ wtf.form_field(form.cas_allow_for_new_users) }}
{{ wtf.form_field(form.cas_server) }}
{{ wtf.form_field(form.cas_login_route) }}
{{ 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) }}
{{ wtf.form_field(form.cas_edt_id_from_xml_regexp) }}
<div class="cas_settings">
<div class="scobox">
<div class="scobox-title">Routes CAS</div>
{{ wtf.form_field(form.cas_server) }}
{{ wtf.form_field(form.cas_login_route) }}
{{ wtf.form_field(form.cas_logout_route) }}
{{ wtf.form_field(form.cas_validate_route) }}
</div>
<div class="scobox">
{{ wtf.form_field(form.cas_attribute_id) }}
</div>
<div class="scobox">
<div class="scobox-title">Identifiant utilisateur CAS</div>
<div class="help explanation">
Ces paramètres sont utilisés pour déduire
l'identifiant CAS des utilisateurs ScoDoc au moment de la création ou modification
de comptes utilisateurs. Pour modifier les comptes existants en masse, il peut être
pratique de passer par un
<a class="stdlink" href="{{ url_for('auth.cas_users_import_config') }}">export/import excel</a>.
</div>
{{ wtf.form_field(form.cas_uid_from_mail_regexp) }}
{{ wtf.form_field(form.cas_uid_use_scodoc) }}
</div>
<div class="scobox">
{{ wtf.form_field(form.cas_edt_id_from_xml_regexp) }}
</div>
<div class="scobox cas_settings">
<div class="scobox-title">Certificat serveur CAS</div>
{{ wtf.form_field(form.cas_ssl_verify) }}
{{ wtf.form_field(form.cas_ssl_certificate_file) }}
<div class="cas_etat_certif_ssl">Certificat SSL

View File

@ -43,6 +43,9 @@ the necessary fix for required=False attributes, but will also not set the requi
<div class="checkbox">
<label>
{{field()|safe}} {{field.label.text|safe}}
{%- if field.description %}
<div class="help-block">{{field.description|safe}}</div>
{%- endif %}
</label>
</div>
{% endcall %}
@ -108,12 +111,12 @@ the necessary fix for required=False attributes, but will also not set the requi
{%- if field.errors %}
{%- for error in field.errors %}
{% call _hz_form_wrap(horizontal_columns, form_type, required=required) %}
<p class="help-block">{{error}}</p>
<div class="help-block">{{error}}</div>
{% endcall %}
{%- endfor %}
{%- elif field.description -%}
{% call _hz_form_wrap(horizontal_columns, form_type, required=required) %}
<p class="help-block">{{field.description|safe}}</p>
<div class="help-block">{{field.description|safe}}</div>
{% endcall %}
{%- endif %}
{%- else -%}
@ -126,10 +129,10 @@ the necessary fix for required=False attributes, but will also not set the requi
{%- if field.errors %}
{%- for error in field.errors %}
<p class="help-block">{{error}}</p>
<div class="help-block">{{error}}</div>
{%- endfor %}
{%- elif field.description -%}
<p class="help-block">{{field.description|safe}}</p>
<div class="help-block">{{field.description|safe}}</div>
{%- endif %}
{%- endif %}
</div>

View File

@ -296,6 +296,11 @@ def config_cas():
"cas_uid_from_mail_regexp", form.data["cas_uid_from_mail_regexp"]
):
flash("Expression extraction identifiant CAS enregistrée")
if ScoDocSiteConfig.set("cas_uid_use_scodoc", form.data["cas_uid_use_scodoc"]):
if form.data["cas_uid_use_scodoc"]:
flash("Utilisation de l'identifiant ScoDoc comme identifiant CAS")
else:
flash("N'utilise PAS l'identifiant ScoDoc pour le CAS")
if ScoDocSiteConfig.set(
"cas_edt_id_from_xml_regexp", form.data["cas_edt_id_from_xml_regexp"]
):
@ -313,7 +318,7 @@ def config_cas():
set_cas_configuration()
return redirect(url_for("scodoc.configuration"))
elif request.method == "GET":
if request.method == "GET":
form.cas_enable.data = ScoDocSiteConfig.get("cas_enable")
form.cas_force.data = ScoDocSiteConfig.get("cas_force")
form.cas_allow_for_new_users.data = ScoDocSiteConfig.get(
@ -327,6 +332,7 @@ def config_cas():
form.cas_uid_from_mail_regexp.data = ScoDocSiteConfig.get(
"cas_uid_from_mail_regexp"
)
form.cas_uid_use_scodoc.data = ScoDocSiteConfig.get("cas_uid_use_scodoc")
form.cas_edt_id_from_xml_regexp.data = ScoDocSiteConfig.get(
"cas_edt_id_from_xml_regexp"
)

View File

@ -404,9 +404,13 @@ def create_user_form(user_name=None, edit=0, all_roles=True):
"input_type": "text",
"explanation": "id du compte utilisateur sur le CAS de l'établissement "
+ (
"(<b>sera déduit de son e-mail institutionnel</b>) "
if ScoDocSiteConfig.get("cas_uid_from_mail_regexp")
else ""
"<b>pa défaut identique à l'identifiant ScoDoc</b> "
if ScoDocSiteConfig.get("cas_uid_use_scodoc")
else (
"(<b>sera déduit de son e-mail institutionnel</b>) "
if ScoDocSiteConfig.get("cas_uid_from_mail_regexp")
else ""
)
)
+ (
"(service CAS activé)"
@ -537,7 +541,8 @@ def create_user_form(user_name=None, edit=0, all_roles=True):
"d",
{
"input_type": "separator",
"title": f"L'utilisateur sera créé dans le département {auth_dept or 'aucun'}",
"title": f"""L'utilisateur sera créé dans le département {
auth_dept or 'aucun'}""",
},
)
)
@ -606,240 +611,238 @@ def create_user_form(user_name=None, edit=0, all_roles=True):
content="\n".join(H) + "\n" + tf[1],
javascripts=["js/user_form.js"],
)
elif tf[0] == -1:
if tf[0] == -1:
return flask.redirect(url_for("users.index_html", scodoc_dept=g.scodoc_dept))
vals = tf[2]
roles = set(vals["roles"]).intersection(editable_roles_strings)
if not current_user.is_administrator():
# empeche modification des paramètres CAS
if "cas_allow_login" in vals:
vals["cas_allow_login"] = cas_allow_login_default
if "cas_allow_scodoc_login" in vals:
if the_user is None:
vals.pop("cas_allow_scodoc_login", None)
else:
vals["cas_allow_scodoc_login"] = the_user.cas_allow_scodoc_login
if not current_user.has_permission(Permission.UsersChangeCASId):
vals.pop("cas_id", None)
if "edit" in vals:
edit = int(vals["edit"])
else:
vals = tf[2]
roles = set(vals["roles"]).intersection(editable_roles_strings)
if not current_user.is_administrator():
# empeche modification des paramètres CAS
if "cas_allow_login" in vals:
vals["cas_allow_login"] = cas_allow_login_default
if "cas_allow_scodoc_login" in vals:
if the_user is None:
vals.pop("cas_allow_scodoc_login", None)
else:
vals["cas_allow_scodoc_login"] = the_user.cas_allow_scodoc_login
edit = 0
try:
force = int(vals.get("force", "0")[0])
except (IndexError, ValueError, TypeError):
force = 0
if not current_user.has_permission(Permission.UsersChangeCASId):
vals.pop("cas_id", None)
if "edit" in vals:
edit = int(vals["edit"])
else:
edit = 0
try:
force = int(vals.get("force", "0")[0])
except (IndexError, ValueError, TypeError):
force = 0
if edit:
user_name = initvalues["user_name"]
else:
user_name = vals["user_name"]
# ce login existe ?
err_msg = None
nb_existing_user = User.query.filter_by(user_name=user_name).count() > 0
if edit and (
nb_existing_user == 0
): # safety net, le user_name ne devrait pas changer
err_msg = f"identifiant {user_name} inexistant"
if not edit and nb_existing_user > 0:
err_msg = f"identifiant {user_name} déjà utilisé"
if err_msg:
H.append(tf_error_message(f"""Erreur: {err_msg}"""))
return render_template(
"base.j2",
content="\n".join(H) + "\n" + tf[1],
javascripts=["js/user_form.js"],
)
if edit:
user_name = initvalues["user_name"]
else:
user_name = vals["user_name"]
# ce login existe ?
err_msg = None
nb_existing_user = User.query.filter_by(user_name=user_name).count() > 0
if edit and (
nb_existing_user == 0
): # safety net, le user_name ne devrait pas changer
err_msg = f"identifiant {user_name} inexistant"
if not edit and nb_existing_user > 0:
err_msg = f"identifiant {user_name} déjà utilisé"
if err_msg:
H.append(tf_error_message(f"""Erreur: {err_msg}"""))
if not edit_only_roles:
ok_modif, msg = sco_users.check_modif_user(
edit,
enforce_optionals=not force,
user_name=user_name,
nom=vals["nom"],
prenom=vals["prenom"],
email=vals["email"],
dept=vals.get("dept", auth_dept),
roles=vals["roles"],
cas_id=vals.get("cas_id"), # pas présent si pas super-admin
)
if not ok_modif:
H.append(tf_error_message(msg))
return render_template(
"base.j2",
content="\n".join(H) + "\n" + tf[1],
javascripts=["js/user_form.js"],
)
if not edit_only_roles:
ok_modif, msg = sco_users.check_modif_user(
edit,
enforce_optionals=not force,
user_name=user_name,
nom=vals["nom"],
prenom=vals["prenom"],
email=vals["email"],
dept=vals.get("dept", auth_dept),
roles=vals["roles"],
cas_id=vals.get("cas_id"), # pas présent si pas super-admin
)
if not ok_modif:
H.append(tf_error_message(msg))
if "date_expiration" in vals:
try:
if vals["date_expiration"]:
vals["date_expiration"] = datetime.datetime.strptime(
vals["date_expiration"], scu.DATE_FMT
)
if vals["date_expiration"] < datetime.datetime.now():
H.append(tf_error_message("date expiration passée"))
return render_template(
"base.j2",
content="\n".join(H) + "\n" + tf[1],
javascripts=["js/user_form.js"],
)
else:
vals["date_expiration"] = None
except ValueError:
H.append(tf_error_message("date expiration invalide"))
return render_template(
"base.j2",
content="\n".join(H) + "\n" + tf[1],
javascripts=["js/user_form.js"],
)
if "date_expiration" in vals:
try:
if vals["date_expiration"]:
vals["date_expiration"] = datetime.datetime.strptime(
vals["date_expiration"], scu.DATE_FMT
)
if vals["date_expiration"] < datetime.datetime.now():
H.append(tf_error_message("date expiration passée"))
return render_template(
"base.j2",
content="\n".join(H) + "\n" + tf[1],
javascripts=["js/user_form.js"],
)
else:
vals["date_expiration"] = None
except ValueError:
H.append(tf_error_message("date expiration invalide"))
if edit: # modif utilisateur (mais pas password ni user_name !)
if (not can_choose_dept) and "dept" in vals:
del vals["dept"]
if "password" in vals:
del vals["passwordd"]
if "date_modif_passwd" in vals:
del vals["date_modif_passwd"]
if "user_name" in vals:
del vals["user_name"]
if (current_user.user_name == user_name) and "status" in vals:
del vals["status"] # no one can't change its own status
if "status" in vals:
vals["active"] = vals["status"] == ""
# Département:
if ("dept" in vals) and (vals["dept"] not in selectable_dept_acronyms):
del vals["dept"] # ne change pas de dept
# Traitement des roles: ne doit pas affecter les rôles
# que l'on en contrôle pas:
for role in orig_roles_strings: # { "Ens_RT", "Secr_CJ", ... }
if role and not role in editable_roles_strings:
roles.add(role)
vals["roles_string"] = ",".join(roles)
# ok, edit
if not edit_only_roles:
log(f"sco_users: editing {user_name} by {current_user.user_name}")
log(f"sco_users: previous_values={initvalues}")
log(f"sco_users: new_values={vals}")
else:
vals = {"roles_string": vals["roles_string"]}
the_user.from_dict(vals)
db.session.add(the_user)
db.session.commit()
flash(f"Utilisateur {user_name} modifié")
return flask.redirect(
url_for(
"users.user_info_page",
scodoc_dept=g.scodoc_dept,
user_name=user_name,
)
)
else: # création utilisateur
vals["roles_string"] = ",".join(vals["roles"])
# check identifiant
if not re.match(r"^[a-zA-Z0-9@\\\-_\\\.]+$", vals["user_name"]):
msg = tf_error_message(
"identifiant invalide (pas d'accents ni de caractères spéciaux)"
)
return render_template(
"base.j2",
content="\n".join(H) + msg + "\n" + tf[1],
javascripts=["js/user_form.js"],
)
# Traitement initial (mode) : 3 cas
# cf énumération Mode
# A: envoi de welcome + procedure de reset
# 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.
mode = Mode.WELCOME_AND_CHANGE_PASSWORD
else:
mode = Mode.WELCOME_ONLY
else:
mode = Mode.SILENT
# check passwords
if mode == Mode.WELCOME_AND_CHANGE_PASSWORD:
vals["password"] = generate_password()
else:
if vals["password"]:
if vals["password"] != vals["password2"]:
msg = tf_error_message(
"""Les deux mots de passes ne correspondent pas !"""
)
return render_template(
"base.j2",
content="\n".join(H) + "\n" + tf[1],
content="\n".join(H) + msg + "\n" + tf[1],
javascripts=["js/user_form.js"],
)
if edit: # modif utilisateur (mais pas password ni user_name !)
if (not can_choose_dept) and "dept" in vals:
del vals["dept"]
if "password" in vals:
del vals["passwordd"]
if "date_modif_passwd" in vals:
del vals["date_modif_passwd"]
if "user_name" in vals:
del vals["user_name"]
if (current_user.user_name == user_name) and "status" in vals:
del vals["status"] # no one can't change its own status
if "status" in vals:
vals["active"] = vals["status"] == ""
# Département:
if ("dept" in vals) and (vals["dept"] not in selectable_dept_acronyms):
del vals["dept"] # ne change pas de dept
# Traitement des roles: ne doit pas affecter les rôles
# que l'on en contrôle pas:
for role in orig_roles_strings: # { "Ens_RT", "Secr_CJ", ... }
if role and not role in editable_roles_strings:
roles.add(role)
vals["roles_string"] = ",".join(roles)
# ok, edit
if not edit_only_roles:
log(f"sco_users: editing {user_name} by {current_user.user_name}")
log(f"sco_users: previous_values={initvalues}")
log(f"sco_users: new_values={vals}")
else:
vals = {"roles_string": vals["roles_string"]}
the_user.from_dict(vals)
db.session.add(the_user)
db.session.commit()
flash(f"Utilisateur {user_name} modifié")
return flask.redirect(
url_for(
"users.user_info_page",
scodoc_dept=g.scodoc_dept,
user_name=user_name,
)
)
else: # création utilisateur
vals["roles_string"] = ",".join(vals["roles"])
# check identifiant
if not re.match(r"^[a-zA-Z0-9@\\\-_\\\.]+$", vals["user_name"]):
msg = tf_error_message(
"identifiant invalide (pas d'accents ni de caractères spéciaux)"
)
return render_template(
"base.j2",
content="\n".join(H) + msg + "\n" + tf[1],
javascripts=["js/user_form.js"],
)
# Traitement initial (mode) : 3 cas
# cf énumération Mode
# A: envoi de welcome + procedure de reset
# 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.
mode = Mode.WELCOME_AND_CHANGE_PASSWORD
else:
mode = Mode.WELCOME_ONLY
else:
mode = Mode.SILENT
# check passwords
if not is_valid_password(vals["password"]):
msg = tf_error_message(
"""Mot de passe trop simple, recommencez !"""
)
return render_template(
"base.j2",
content="\n".join(H) + msg + "\n" + tf[1],
javascripts=["js/user_form.js"],
)
# Département:
if not can_choose_dept:
vals["dept"] = auth_dept
else:
if auth_dept: # pas super-admin
if vals["dept"] not in selectable_dept_acronyms:
raise ScoValueError("département invalide")
# ok, go
log(f"""sco_users: new_user {vals["user_name"]} by {current_user.user_name}""")
the_user = User(user_name=user_name)
the_user.from_dict(vals, new_user=True)
db.session.add(the_user)
db.session.commit()
# envoi éventuel d'un message
if mode == Mode.WELCOME_AND_CHANGE_PASSWORD or mode == Mode.WELCOME_ONLY:
if mode == Mode.WELCOME_AND_CHANGE_PASSWORD:
vals["password"] = generate_password()
token = the_user.get_reset_password_token()
else:
if vals["password"]:
if vals["password"] != vals["password2"]:
msg = tf_error_message(
"""Les deux mots de passes ne correspondent pas !"""
)
return render_template(
"base.j2",
content="\n".join(H) + msg + "\n" + tf[1],
javascripts=["js/user_form.js"],
)
if not is_valid_password(vals["password"]):
msg = tf_error_message(
"""Mot de passe trop simple, recommencez !"""
)
return render_template(
"base.j2",
content="\n".join(H) + msg + "\n" + tf[1],
javascripts=["js/user_form.js"],
)
# Département:
if not can_choose_dept:
vals["dept"] = auth_dept
else:
if auth_dept: # pas super-admin
if vals["dept"] not in selectable_dept_acronyms:
raise ScoValueError("département invalide")
# ok, go
log(
f"""sco_users: new_user {vals["user_name"]} by {current_user.user_name}"""
token = None
cas_force = ScoDocSiteConfig.get("cas_force")
# Le from doit utiliser la préférence du département de l'utilisateur
email.send_email(
"[ScoDoc] Création de votre compte",
sender=email.get_from_addr(),
recipients=[the_user.email],
text_body=render_template(
"email/welcome.txt",
user=the_user,
token=token,
cas_force=cas_force,
),
html_body=render_template(
"email/welcome.j2",
user=the_user,
token=token,
cas_force=cas_force,
),
)
the_user = User(user_name=user_name)
the_user.from_dict(vals, new_user=True)
db.session.add(the_user)
db.session.commit()
# envoi éventuel d'un message
if mode == Mode.WELCOME_AND_CHANGE_PASSWORD or mode == Mode.WELCOME_ONLY:
if mode == Mode.WELCOME_AND_CHANGE_PASSWORD:
token = the_user.get_reset_password_token()
else:
token = None
cas_force = ScoDocSiteConfig.get("cas_force")
# Le from doit utiliser la préférence du département de l'utilisateur
email.send_email(
"[ScoDoc] Création de votre compte",
sender=email.get_from_addr(),
recipients=[the_user.email],
text_body=render_template(
"email/welcome.txt",
user=the_user,
token=token,
cas_force=cas_force,
),
html_body=render_template(
"email/welcome.j2",
user=the_user,
token=token,
cas_force=cas_force,
),
)
flash(f"Mail accueil envoyé à {the_user.email}")
flash(f"Mail accueil envoyé à {the_user.email}")
flash("Nouvel utilisateur créé")
return flask.redirect(
url_for(
"users.user_info_page",
scodoc_dept=g.scodoc_dept,
user_name=user_name,
)
flash("Nouvel utilisateur créé")
return flask.redirect(
url_for(
"users.user_info_page",
scodoc_dept=g.scodoc_dept,
user_name=user_name,
)
)
@bp.route("/import_users_generate_excel_sample")