diff --git a/app/auth/models.py b/app/auth/models.py
index 9e0049ac7a176138a355960f33bb4da83f52c6f2..440117ed9461db246a7177f2b1f52d8a9f491972 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 41c639bcf05470a109840b1a877605c7f0905827..5b1a18fa5d631a09e52eccf15795245441dab972 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 f5e23bfce8c804ab7304dd54ad7bd4a63d47aced..25d9b9ccb7c9eaa0b1d70c1691742da48de45e0e 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 56e76e299c70f5d6118f407db7aaf709098861fe..a54cb42c286cdd17cee294a3c4eaded2f21994e5 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 ed06fb6d398c3b03bbba1e22b13551b95c5a683d..d1300794b16ceb32188a242bf5f5f4cb66f06806 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 73fb34a65494b5501447da6a178fb52409c18406..bccc4ace3782e4d5255d9770dce5d2edf7f0fc19 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 %}
<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 %}
diff --git a/app/templates/config_cas.j2 b/app/templates/config_cas.j2
index b1d532f88f0cfcb3fd186dc46e8cd283f100fe28..a3bca2e4d94976b982ac96f9c58e022e114fee06 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()}}
+<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
diff --git a/app/templates/wtf.j2 b/app/templates/wtf.j2
index 7f69ca9ad39b0a9324bd0d064b06ef29e142650b..cefb2f18bafd0be553677cb8053c064d15868298 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
<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>
diff --git a/app/views/scodoc.py b/app/views/scodoc.py
index 8096e927a9efaf634b4b074bbd5df25ad0f1065e..fcdf857f973945f1c5a868b724e5d7b7eb3ecdfb 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 dbd15b05c2ebe80d57335b69840051c2b84340f1..3c147a6d676f62fbbe7f8bff2bd647f72f4f7f66 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 "
+ (
- "(<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"))
- return render_template(
- "base.j2",
- content="\n".join(H) + "\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,
- )
+ 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: # 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.SILENT
+ mode = Mode.WELCOME_ONLY
+ else:
+ mode = Mode.SILENT
- # check passwords
+ # 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) + 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}""")
+ 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("Nouvel utilisateur créé")
- return flask.redirect(
- url_for(
- "users.user_info_page",
- scodoc_dept=g.scodoc_dept,
- user_name=user_name,
- )
+ 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,
)
+ )
@bp.route("/import_users_generate_excel_sample")