diff --git a/app/auth/models.py b/app/auth/models.py index 9e0049ac..440117ed 100644 --- a/app/auth/models.py +++ b/app/auth/models.py @@ -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é." diff --git a/app/forms/main/config_cas.py b/app/forms/main/config_cas.py index 41c639bc..5b1a18fa 100644 --- a/app/forms/main/config_cas.py +++ b/app/forms/main/config_cas.py @@ -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 diff --git a/app/models/config.py b/app/models/config.py index f5e23bfc..25d9b9cc 100644 --- a/app/models/config.py +++ b/app/models/config.py @@ -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 diff --git a/app/static/css/scodoc.css b/app/static/css/scodoc.css index 56e76e29..a54cb42c 100644 --- a/app/static/css/scodoc.css +++ b/app/static/css/scodoc.css @@ -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; diff --git a/app/static/css/scodoc97.css b/app/static/css/scodoc97.css index ed06fb6d..d1300794 100644 --- a/app/static/css/scodoc97.css +++ b/app/static/css/scodoc97.css @@ -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; diff --git a/app/templates/auth/cas_users_import_config.j2 b/app/templates/auth/cas_users_import_config.j2 index 73fb34a6..bccc4ace 100644 --- a/app/templates/auth/cas_users_import_config.j2 +++ b/app/templates/auth/cas_users_import_config.j2 @@ -4,7 +4,7 @@ {% block app_content %}

Chargement des configurations CAS des utilisateurs

-
+

A utiliser pour modifier le paramétrage CAS de comptes utilisateurs existants

@@ -32,21 +32,26 @@
  • Revenez sur cette page et chargez le fichier dans ScoDoc.
  • +
    + +
    +
    Étape 1: exporter fichier Excel à charger
    + +
    + +
    +
    Étape 2: charger le fichier Excel modifié
    - - - +
    {% endblock %} diff --git a/app/templates/config_cas.j2 b/app/templates/config_cas.j2 index b1d532f8..a3bca2e4 100644 --- a/app/templates/config_cas.j2 +++ b/app/templates/config_cas.j2 @@ -1,6 +1,14 @@ {% extends "base.j2" %} {% import 'wtf.j2' as wtf %} + +{% block styles %} +{{super()}} + +{% endblock %} + {% block app_content %}

    Configuration du Service d'Authentification Central (CAS)

    @@ -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) }} -
    +
    +
    Routes CAS
    + {{ 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) }} +
    +
    +
    Identifiant utilisateur CAS
    +
    + 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 + export/import excel. +
    + {{ wtf.form_field(form.cas_uid_from_mail_regexp) }} + {{ wtf.form_field(form.cas_uid_use_scodoc) }} +
    +
    + {{ wtf.form_field(form.cas_edt_id_from_xml_regexp) }} +
    +
    +
    Certificat serveur CAS
    {{ wtf.form_field(form.cas_ssl_verify) }} {{ wtf.form_field(form.cas_ssl_certificate_file) }}
    Certificat SSL diff --git a/app/templates/wtf.j2 b/app/templates/wtf.j2 index 7f69ca9a..cefb2f18 100644 --- a/app/templates/wtf.j2 +++ b/app/templates/wtf.j2 @@ -43,6 +43,9 @@ the necessary fix for required=False attributes, but will also not set the requi
    {% 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) %} -

    {{error}}

    +
    {{error}}
    {% endcall %} {%- endfor %} {%- elif field.description -%} {% call _hz_form_wrap(horizontal_columns, form_type, required=required) %} -

    {{field.description|safe}}

    +
    {{field.description|safe}}
    {% 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 %} -

    {{error}}

    +
    {{error}}
    {%- endfor %} {%- elif field.description -%} -

    {{field.description|safe}}

    +
    {{field.description|safe}}
    {%- endif %} {%- endif %}
    diff --git a/app/views/scodoc.py b/app/views/scodoc.py index 8096e927..fcdf857f 100644 --- a/app/views/scodoc.py +++ b/app/views/scodoc.py @@ -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" ) diff --git a/app/views/users.py b/app/views/users.py index dbd15b05..3c147a6d 100644 --- a/app/views/users.py +++ b/app/views/users.py @@ -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 " + ( - "(sera déduit de son e-mail institutionnel) " - if ScoDocSiteConfig.get("cas_uid_from_mail_regexp") - else "" + "pa défaut identique à l'identifiant ScoDoc " + if ScoDocSiteConfig.get("cas_uid_use_scodoc") + else ( + "(sera déduit de son e-mail institutionnel) " + 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")