CAS: ajout option pour utiliser par défaut le même uid ScoDoc et CAS + cosmetic formulaires
This commit is contained in:
parent
c38b8aa297
commit
9ae2181904
@ -355,12 +355,15 @@ class User(UserMixin, ScoDocModel):
|
|||||||
|
|
||||||
super().from_dict(data, excluded={"user_name", "roles_string", "roles"})
|
super().from_dict(data, excluded={"user_name", "roles_string", "roles"})
|
||||||
|
|
||||||
# Set cas_id using regexp if configured:
|
if ScoDocSiteConfig.cas_uid_use_scodoc():
|
||||||
exp = ScoDocSiteConfig.get("cas_uid_from_mail_regexp")
|
self.cas_id = self.user_name
|
||||||
if exp and self.email_institutionnel:
|
else:
|
||||||
cas_id = ScoDocSiteConfig.extract_cas_id(self.email_institutionnel)
|
# Set cas_id using regexp if configured:
|
||||||
if cas_id:
|
exp = ScoDocSiteConfig.get("cas_uid_from_mail_regexp")
|
||||||
self.cas_id = cas_id
|
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):
|
def get_token(self, expires_in=3600):
|
||||||
"Un jeton pour cet user. Stocké en base, non commité."
|
"Un jeton pour cet user. Stocké en base, non commité."
|
||||||
|
@ -98,6 +98,15 @@ class ConfigCASForm(FlaskForm):
|
|||||||
validators=[Optional(), check_cas_uid_from_mail_regexp],
|
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(
|
cas_edt_id_from_xml_regexp = StringField(
|
||||||
label="Optionnel: expression pour extraire l'identifiant edt",
|
label="Optionnel: expression pour extraire l'identifiant edt",
|
||||||
description="""regexp python appliquée à la réponse XML du serveur CAS pour
|
description="""regexp python appliquée à la réponse XML du serveur CAS pour
|
||||||
|
@ -105,6 +105,7 @@ class ScoDocSiteConfig(db.Model):
|
|||||||
"cas_validate_route": str,
|
"cas_validate_route": str,
|
||||||
"cas_attribute_id": str,
|
"cas_attribute_id": str,
|
||||||
"cas_uid_from_mail_regexp": str,
|
"cas_uid_from_mail_regexp": str,
|
||||||
|
"cas_uid_use_scodoc": bool,
|
||||||
"cas_edt_id_from_xml_regexp": str,
|
"cas_edt_id_from_xml_regexp": str,
|
||||||
# Assiduité
|
# Assiduité
|
||||||
"morning_time": str,
|
"morning_time": str,
|
||||||
@ -239,6 +240,12 @@ class ScoDocSiteConfig(db.Model):
|
|||||||
cfg = ScoDocSiteConfig.query.filter_by(name="cas_force").first()
|
cfg = ScoDocSiteConfig.query.filter_by(name="cas_force").first()
|
||||||
return cfg is not None and cfg.value
|
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
|
@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"""
|
||||||
@ -404,7 +411,7 @@ class ScoDocSiteConfig(db.Model):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def extract_cas_id(cls, email_addr: str) -> str | None:
|
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")
|
exp = cls.get("cas_uid_from_mail_regexp")
|
||||||
if not exp or not email_addr:
|
if not exp or not email_addr:
|
||||||
return None
|
return None
|
||||||
|
@ -1246,7 +1246,8 @@ a.discretelink:hover {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.help {
|
.help,
|
||||||
|
.help-block {
|
||||||
max-width: var(--sco-content-max-width);
|
max-width: var(--sco-content-max-width);
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
@ -1260,6 +1261,10 @@ a.discretelink:hover {
|
|||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.help-block {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
div.sco_box,
|
div.sco_box,
|
||||||
div.sco_help {
|
div.sco_help {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
@ -4919,7 +4924,6 @@ table.formation_table_recap td.heures_tp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
div.cas_settings {
|
div.cas_settings {
|
||||||
margin-left: -15px;
|
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
border: 1px dashed rgb(191, 34, 191);
|
border: 1px dashed rgb(191, 34, 191);
|
||||||
background-color: #feb4e54f;
|
background-color: #feb4e54f;
|
||||||
|
@ -430,11 +430,21 @@ textarea {
|
|||||||
color: #333;
|
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-group input,
|
||||||
.form-control {
|
.form-control {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 4px;
|
||||||
border: 1px solid #ced4da;
|
border: 1px solid #ced4da;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
{% block app_content %}
|
{% block app_content %}
|
||||||
<h1>Chargement des configurations CAS des utilisateurs</h1>
|
<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
|
<p style="color: red">A utiliser pour modifier le paramétrage CAS de
|
||||||
<b>comptes utilisateurs existants</b>
|
<b>comptes utilisateurs existants</b>
|
||||||
</p>
|
</p>
|
||||||
@ -32,21 +32,26 @@
|
|||||||
<li style="margin-bottom:32px;">Revenez sur cette page et chargez le fichier dans ScoDoc.
|
<li style="margin-bottom:32px;">Revenez sur cette page et chargez le fichier dans ScoDoc.
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</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="row">
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
{{ wtf.quick_form(form) }}
|
{{ wtf.quick_form(form) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</div>
|
||||||
</ul>
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
{% extends "base.j2" %}
|
{% extends "base.j2" %}
|
||||||
{% import 'wtf.j2' as wtf %}
|
{% import 'wtf.j2' as wtf %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block styles %}
|
||||||
|
{{super()}}
|
||||||
|
<style>
|
||||||
|
div.checkbox label { font-weight: normal; }
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block app_content %}
|
{% block app_content %}
|
||||||
<h1>Configuration du Service d'Authentification Central (CAS)</h1>
|
<h1>Configuration du Service d'Authentification Central (CAS)</h1>
|
||||||
|
|
||||||
@ -18,14 +26,33 @@
|
|||||||
{{ wtf.form_field(form.cas_enable) }}
|
{{ wtf.form_field(form.cas_enable) }}
|
||||||
{{ wtf.form_field(form.cas_force) }}
|
{{ wtf.form_field(form.cas_force) }}
|
||||||
{{ wtf.form_field(form.cas_allow_for_new_users) }}
|
{{ wtf.form_field(form.cas_allow_for_new_users) }}
|
||||||
{{ wtf.form_field(form.cas_server) }}
|
<div class="scobox">
|
||||||
{{ wtf.form_field(form.cas_login_route) }}
|
<div class="scobox-title">Routes CAS</div>
|
||||||
{{ wtf.form_field(form.cas_logout_route) }}
|
{{ wtf.form_field(form.cas_server) }}
|
||||||
{{ wtf.form_field(form.cas_validate_route) }}
|
{{ wtf.form_field(form.cas_login_route) }}
|
||||||
{{ wtf.form_field(form.cas_attribute_id) }}
|
{{ wtf.form_field(form.cas_logout_route) }}
|
||||||
{{ wtf.form_field(form.cas_uid_from_mail_regexp) }}
|
{{ wtf.form_field(form.cas_validate_route) }}
|
||||||
{{ wtf.form_field(form.cas_edt_id_from_xml_regexp) }}
|
</div>
|
||||||
<div class="cas_settings">
|
<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_verify) }}
|
||||||
{{ wtf.form_field(form.cas_ssl_certificate_file) }}
|
{{ wtf.form_field(form.cas_ssl_certificate_file) }}
|
||||||
<div class="cas_etat_certif_ssl">Certificat SSL
|
<div class="cas_etat_certif_ssl">Certificat SSL
|
||||||
|
@ -43,6 +43,9 @@ the necessary fix for required=False attributes, but will also not set the requi
|
|||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
{{field()|safe}} {{field.label.text|safe}}
|
{{field()|safe}} {{field.label.text|safe}}
|
||||||
|
{%- if field.description %}
|
||||||
|
<div class="help-block">{{field.description|safe}}</div>
|
||||||
|
{%- endif %}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
{% endcall %}
|
{% endcall %}
|
||||||
@ -108,12 +111,12 @@ the necessary fix for required=False attributes, but will also not set the requi
|
|||||||
{%- if field.errors %}
|
{%- if field.errors %}
|
||||||
{%- for error in field.errors %}
|
{%- for error in field.errors %}
|
||||||
{% call _hz_form_wrap(horizontal_columns, form_type, required=required) %}
|
{% call _hz_form_wrap(horizontal_columns, form_type, required=required) %}
|
||||||
<p class="help-block">{{error}}</p>
|
<div class="help-block">{{error}}</div>
|
||||||
{% endcall %}
|
{% endcall %}
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
{%- elif field.description -%}
|
{%- elif field.description -%}
|
||||||
{% call _hz_form_wrap(horizontal_columns, form_type, required=required) %}
|
{% 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 %}
|
{% endcall %}
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
{%- else -%}
|
{%- else -%}
|
||||||
@ -126,10 +129,10 @@ the necessary fix for required=False attributes, but will also not set the requi
|
|||||||
|
|
||||||
{%- if field.errors %}
|
{%- if field.errors %}
|
||||||
{%- for error in field.errors %}
|
{%- for error in field.errors %}
|
||||||
<p class="help-block">{{error}}</p>
|
<div class="help-block">{{error}}</div>
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
{%- elif field.description -%}
|
{%- elif field.description -%}
|
||||||
<p class="help-block">{{field.description|safe}}</p>
|
<div class="help-block">{{field.description|safe}}</div>
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -296,6 +296,11 @@ def config_cas():
|
|||||||
"cas_uid_from_mail_regexp", form.data["cas_uid_from_mail_regexp"]
|
"cas_uid_from_mail_regexp", form.data["cas_uid_from_mail_regexp"]
|
||||||
):
|
):
|
||||||
flash("Expression extraction identifiant CAS enregistrée")
|
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(
|
if ScoDocSiteConfig.set(
|
||||||
"cas_edt_id_from_xml_regexp", form.data["cas_edt_id_from_xml_regexp"]
|
"cas_edt_id_from_xml_regexp", form.data["cas_edt_id_from_xml_regexp"]
|
||||||
):
|
):
|
||||||
@ -313,7 +318,7 @@ def config_cas():
|
|||||||
set_cas_configuration()
|
set_cas_configuration()
|
||||||
return redirect(url_for("scodoc.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_enable.data = ScoDocSiteConfig.get("cas_enable")
|
||||||
form.cas_force.data = ScoDocSiteConfig.get("cas_force")
|
form.cas_force.data = ScoDocSiteConfig.get("cas_force")
|
||||||
form.cas_allow_for_new_users.data = ScoDocSiteConfig.get(
|
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(
|
form.cas_uid_from_mail_regexp.data = ScoDocSiteConfig.get(
|
||||||
"cas_uid_from_mail_regexp"
|
"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(
|
form.cas_edt_id_from_xml_regexp.data = ScoDocSiteConfig.get(
|
||||||
"cas_edt_id_from_xml_regexp"
|
"cas_edt_id_from_xml_regexp"
|
||||||
)
|
)
|
||||||
|
@ -404,9 +404,13 @@ def create_user_form(user_name=None, edit=0, all_roles=True):
|
|||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"explanation": "id du compte utilisateur sur le CAS de l'établissement "
|
"explanation": "id du compte utilisateur sur le CAS de l'établissement "
|
||||||
+ (
|
+ (
|
||||||
"(<b>sera déduit de son e-mail institutionnel</b>) "
|
"<b>pa défaut identique à l'identifiant ScoDoc</b> "
|
||||||
if ScoDocSiteConfig.get("cas_uid_from_mail_regexp")
|
if ScoDocSiteConfig.get("cas_uid_use_scodoc")
|
||||||
else ""
|
else (
|
||||||
|
"(<b>sera déduit de son e-mail institutionnel</b>) "
|
||||||
|
if ScoDocSiteConfig.get("cas_uid_from_mail_regexp")
|
||||||
|
else ""
|
||||||
|
)
|
||||||
)
|
)
|
||||||
+ (
|
+ (
|
||||||
"(service CAS activé)"
|
"(service CAS activé)"
|
||||||
@ -537,7 +541,8 @@ def create_user_form(user_name=None, edit=0, all_roles=True):
|
|||||||
"d",
|
"d",
|
||||||
{
|
{
|
||||||
"input_type": "separator",
|
"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],
|
content="\n".join(H) + "\n" + tf[1],
|
||||||
javascripts=["js/user_form.js"],
|
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))
|
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:
|
else:
|
||||||
vals = tf[2]
|
edit = 0
|
||||||
roles = set(vals["roles"]).intersection(editable_roles_strings)
|
try:
|
||||||
if not current_user.is_administrator():
|
force = int(vals.get("force", "0")[0])
|
||||||
# empeche modification des paramètres CAS
|
except (IndexError, ValueError, TypeError):
|
||||||
if "cas_allow_login" in vals:
|
force = 0
|
||||||
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):
|
if edit:
|
||||||
vals.pop("cas_id", None)
|
user_name = initvalues["user_name"]
|
||||||
if "edit" in vals:
|
else:
|
||||||
edit = int(vals["edit"])
|
user_name = vals["user_name"]
|
||||||
else:
|
# ce login existe ?
|
||||||
edit = 0
|
err_msg = None
|
||||||
try:
|
nb_existing_user = User.query.filter_by(user_name=user_name).count() > 0
|
||||||
force = int(vals.get("force", "0")[0])
|
if edit and (
|
||||||
except (IndexError, ValueError, TypeError):
|
nb_existing_user == 0
|
||||||
force = 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:
|
if not edit_only_roles:
|
||||||
user_name = initvalues["user_name"]
|
ok_modif, msg = sco_users.check_modif_user(
|
||||||
else:
|
edit,
|
||||||
user_name = vals["user_name"]
|
enforce_optionals=not force,
|
||||||
# ce login existe ?
|
user_name=user_name,
|
||||||
err_msg = None
|
nom=vals["nom"],
|
||||||
nb_existing_user = User.query.filter_by(user_name=user_name).count() > 0
|
prenom=vals["prenom"],
|
||||||
if edit and (
|
email=vals["email"],
|
||||||
nb_existing_user == 0
|
dept=vals.get("dept", auth_dept),
|
||||||
): # safety net, le user_name ne devrait pas changer
|
roles=vals["roles"],
|
||||||
err_msg = f"identifiant {user_name} inexistant"
|
cas_id=vals.get("cas_id"), # pas présent si pas super-admin
|
||||||
if not edit and nb_existing_user > 0:
|
)
|
||||||
err_msg = f"identifiant {user_name} déjà utilisé"
|
if not ok_modif:
|
||||||
if err_msg:
|
H.append(tf_error_message(msg))
|
||||||
H.append(tf_error_message(f"""Erreur: {err_msg}"""))
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"base.j2",
|
"base.j2",
|
||||||
content="\n".join(H) + "\n" + tf[1],
|
content="\n".join(H) + "\n" + tf[1],
|
||||||
javascripts=["js/user_form.js"],
|
javascripts=["js/user_form.js"],
|
||||||
)
|
)
|
||||||
|
if "date_expiration" in vals:
|
||||||
if not edit_only_roles:
|
try:
|
||||||
ok_modif, msg = sco_users.check_modif_user(
|
if vals["date_expiration"]:
|
||||||
edit,
|
vals["date_expiration"] = datetime.datetime.strptime(
|
||||||
enforce_optionals=not force,
|
vals["date_expiration"], scu.DATE_FMT
|
||||||
user_name=user_name,
|
)
|
||||||
nom=vals["nom"],
|
if vals["date_expiration"] < datetime.datetime.now():
|
||||||
prenom=vals["prenom"],
|
H.append(tf_error_message("date expiration passée"))
|
||||||
email=vals["email"],
|
return render_template(
|
||||||
dept=vals.get("dept", auth_dept),
|
"base.j2",
|
||||||
roles=vals["roles"],
|
content="\n".join(H) + "\n" + tf[1],
|
||||||
cas_id=vals.get("cas_id"), # pas présent si pas super-admin
|
javascripts=["js/user_form.js"],
|
||||||
)
|
)
|
||||||
if not ok_modif:
|
else:
|
||||||
H.append(tf_error_message(msg))
|
vals["date_expiration"] = None
|
||||||
|
except ValueError:
|
||||||
|
H.append(tf_error_message("date expiration invalide"))
|
||||||
return render_template(
|
return render_template(
|
||||||
"base.j2",
|
"base.j2",
|
||||||
content="\n".join(H) + "\n" + tf[1],
|
content="\n".join(H) + "\n" + tf[1],
|
||||||
javascripts=["js/user_form.js"],
|
javascripts=["js/user_form.js"],
|
||||||
)
|
)
|
||||||
if "date_expiration" in vals:
|
|
||||||
try:
|
if edit: # modif utilisateur (mais pas password ni user_name !)
|
||||||
if vals["date_expiration"]:
|
if (not can_choose_dept) and "dept" in vals:
|
||||||
vals["date_expiration"] = datetime.datetime.strptime(
|
del vals["dept"]
|
||||||
vals["date_expiration"], scu.DATE_FMT
|
if "password" in vals:
|
||||||
)
|
del vals["passwordd"]
|
||||||
if vals["date_expiration"] < datetime.datetime.now():
|
if "date_modif_passwd" in vals:
|
||||||
H.append(tf_error_message("date expiration passée"))
|
del vals["date_modif_passwd"]
|
||||||
return render_template(
|
if "user_name" in vals:
|
||||||
"base.j2",
|
del vals["user_name"]
|
||||||
content="\n".join(H) + "\n" + tf[1],
|
if (current_user.user_name == user_name) and "status" in vals:
|
||||||
javascripts=["js/user_form.js"],
|
del vals["status"] # no one can't change its own status
|
||||||
)
|
if "status" in vals:
|
||||||
else:
|
vals["active"] = vals["status"] == ""
|
||||||
vals["date_expiration"] = None
|
# Département:
|
||||||
except ValueError:
|
if ("dept" in vals) and (vals["dept"] not in selectable_dept_acronyms):
|
||||||
H.append(tf_error_message("date expiration invalide"))
|
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(
|
return render_template(
|
||||||
"base.j2",
|
"base.j2",
|
||||||
content="\n".join(H) + "\n" + tf[1],
|
content="\n".join(H) + msg + "\n" + tf[1],
|
||||||
javascripts=["js/user_form.js"],
|
javascripts=["js/user_form.js"],
|
||||||
)
|
)
|
||||||
|
if not is_valid_password(vals["password"]):
|
||||||
if edit: # modif utilisateur (mais pas password ni user_name !)
|
msg = tf_error_message(
|
||||||
if (not can_choose_dept) and "dept" in vals:
|
"""Mot de passe trop simple, recommencez !"""
|
||||||
del vals["dept"]
|
)
|
||||||
if "password" in vals:
|
return render_template(
|
||||||
del vals["passwordd"]
|
"base.j2",
|
||||||
if "date_modif_passwd" in vals:
|
content="\n".join(H) + msg + "\n" + tf[1],
|
||||||
del vals["date_modif_passwd"]
|
javascripts=["js/user_form.js"],
|
||||||
if "user_name" in vals:
|
)
|
||||||
del vals["user_name"]
|
# Département:
|
||||||
if (current_user.user_name == user_name) and "status" in vals:
|
if not can_choose_dept:
|
||||||
del vals["status"] # no one can't change its own status
|
vals["dept"] = auth_dept
|
||||||
if "status" in vals:
|
else:
|
||||||
vals["active"] = vals["status"] == ""
|
if auth_dept: # pas super-admin
|
||||||
# Département:
|
if vals["dept"] not in selectable_dept_acronyms:
|
||||||
if ("dept" in vals) and (vals["dept"] not in selectable_dept_acronyms):
|
raise ScoValueError("département invalide")
|
||||||
del vals["dept"] # ne change pas de dept
|
# ok, go
|
||||||
# Traitement des roles: ne doit pas affecter les rôles
|
log(f"""sco_users: new_user {vals["user_name"]} by {current_user.user_name}""")
|
||||||
# que l'on en contrôle pas:
|
the_user = User(user_name=user_name)
|
||||||
for role in orig_roles_strings: # { "Ens_RT", "Secr_CJ", ... }
|
the_user.from_dict(vals, new_user=True)
|
||||||
if role and not role in editable_roles_strings:
|
db.session.add(the_user)
|
||||||
roles.add(role)
|
db.session.commit()
|
||||||
|
# envoi éventuel d'un message
|
||||||
vals["roles_string"] = ",".join(roles)
|
if mode == Mode.WELCOME_AND_CHANGE_PASSWORD or mode == Mode.WELCOME_ONLY:
|
||||||
|
|
||||||
# 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:
|
if mode == Mode.WELCOME_AND_CHANGE_PASSWORD:
|
||||||
vals["password"] = generate_password()
|
token = the_user.get_reset_password_token()
|
||||||
else:
|
else:
|
||||||
if vals["password"]:
|
token = None
|
||||||
if vals["password"] != vals["password2"]:
|
cas_force = ScoDocSiteConfig.get("cas_force")
|
||||||
msg = tf_error_message(
|
# Le from doit utiliser la préférence du département de l'utilisateur
|
||||||
"""Les deux mots de passes ne correspondent pas !"""
|
email.send_email(
|
||||||
)
|
"[ScoDoc] Création de votre compte",
|
||||||
return render_template(
|
sender=email.get_from_addr(),
|
||||||
"base.j2",
|
recipients=[the_user.email],
|
||||||
content="\n".join(H) + msg + "\n" + tf[1],
|
text_body=render_template(
|
||||||
javascripts=["js/user_form.js"],
|
"email/welcome.txt",
|
||||||
)
|
user=the_user,
|
||||||
if not is_valid_password(vals["password"]):
|
token=token,
|
||||||
msg = tf_error_message(
|
cas_force=cas_force,
|
||||||
"""Mot de passe trop simple, recommencez !"""
|
),
|
||||||
)
|
html_body=render_template(
|
||||||
return render_template(
|
"email/welcome.j2",
|
||||||
"base.j2",
|
user=the_user,
|
||||||
content="\n".join(H) + msg + "\n" + tf[1],
|
token=token,
|
||||||
javascripts=["js/user_form.js"],
|
cas_force=cas_force,
|
||||||
)
|
),
|
||||||
# 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)
|
flash(f"Mail accueil envoyé à {the_user.email}")
|
||||||
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éé")
|
flash("Nouvel utilisateur créé")
|
||||||
return flask.redirect(
|
return flask.redirect(
|
||||||
url_for(
|
url_for(
|
||||||
"users.user_info_page",
|
"users.user_info_page",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
user_name=user_name,
|
user_name=user_name,
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/import_users_generate_excel_sample")
|
@bp.route("/import_users_generate_excel_sample")
|
||||||
|
Loading…
Reference in New Issue
Block a user