Fix: édition des utilisateurs. #305
This commit is contained in:
parent
cbcff63b35
commit
257248aa2b
@ -730,11 +730,7 @@ var {field}_as = new bsn.AutoSuggest('{field}', {field}_opts);
|
||||
R.append("%s</td>" % title)
|
||||
R.append('<td class="tf-ro-field%s">' % klass)
|
||||
|
||||
if (
|
||||
input_type == "text"
|
||||
or input_type == "text_suggest"
|
||||
or input_type == "color"
|
||||
):
|
||||
if input_type in ("text", "text_suggest", "color", "datedmy"):
|
||||
R.append(("%(" + field + ")s") % self.values)
|
||||
elif input_type in ("radio", "menu", "checkbox", "boolcheckbox"):
|
||||
if input_type == "boolcheckbox":
|
||||
|
@ -47,6 +47,15 @@ class ScoValueError(ScoException):
|
||||
self.dest_url = dest_url
|
||||
|
||||
|
||||
class ScoPermissionDenied(ScoValueError):
|
||||
"""Permission non accordée (appli web)"""
|
||||
|
||||
def __init__(self, msg=None, dest_url=None):
|
||||
if msg is None:
|
||||
msg = "Opération non autorisée !"
|
||||
super().__init__(msg, dest_url=dest_url)
|
||||
|
||||
|
||||
class ScoBugCatcher(ScoException):
|
||||
"bug avec enquete en cours"
|
||||
|
||||
|
@ -15,8 +15,8 @@ _SCO_PERMISSIONS = (
|
||||
(1 << 2, "ScoView", "Voir"),
|
||||
(1 << 3, "ScoEnsView", "Voir les parties pour les enseignants"),
|
||||
(1 << 4, "ScoObservateur", "Observer (accès lecture restreint aux bulletins)"),
|
||||
(1 << 5, "ScoUsersAdmin", "Gérer les utilisateurs"),
|
||||
(1 << 6, "ScoUsersView", "Voir les utilisateurs"),
|
||||
(1 << 5, "ScoUsersAdmin", "Gérer les utilisateurs (de son département)"),
|
||||
(1 << 6, "ScoUsersView", "Voir les utilisateurs (de tous les dépts)"),
|
||||
(1 << 7, "ScoChangePreferences", "Modifier les préférences"),
|
||||
(1 << 8, "ScoChangeFormation", "Changer les formations"),
|
||||
(1 << 9, "ScoEditFormationTags", "Tagguer les formations"),
|
||||
|
@ -18,17 +18,27 @@
|
||||
{{user.date_expiration.isoformat() if user.date_expiration else "(sans limite)"}}
|
||||
<p>
|
||||
<ul>
|
||||
{% if (
|
||||
current_user.is_administrator()
|
||||
or current_user.has_permission(Permission.ScoUsersAdmin, user.dept)
|
||||
) %}
|
||||
<li><a class="stdlink" href="{{
|
||||
url_for( 'users.form_change_password',
|
||||
scodoc_dept=g.scodoc_dept, user_name=user.user_name)
|
||||
}}">modifier le mot de passe ou l'adresse mail</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if current_user.has_permission(Permission.ScoUsersAdmin, dept) %}
|
||||
<li><a class="stdlink" href="{{
|
||||
url_for('users.create_user_form', scodoc_dept=g.scodoc_dept,
|
||||
user_name=user.user_name, edit=1)
|
||||
}}">modifier ce compte</a>
|
||||
}}">modifier ce compte et ses rôles</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if (
|
||||
current_user.is_administrator()
|
||||
or current_user.has_permission(Permission.ScoUsersAdmin, user.dept)
|
||||
) %}
|
||||
<li><a class="stdlink" href="{{
|
||||
url_for('users.toggle_active_user', scodoc_dept=g.scodoc_dept,
|
||||
user_name=user.user_name)
|
||||
@ -45,7 +55,7 @@
|
||||
|
||||
{# Liste des permissions #}
|
||||
<div class="permissions">
|
||||
<p>Permissions de cet utilisateur dans le département {{dept}}:</p>
|
||||
<p><b>Permissions de cet utilisateur dans le département {{dept}}:</b></p>
|
||||
<ul>
|
||||
{% for p in Permission.description %}
|
||||
<li>{{Permission.description[p]}} :
|
||||
|
@ -68,7 +68,7 @@ from app.scodoc import sco_users
|
||||
from app.scodoc import sco_utils as scu
|
||||
from app.scodoc import sco_xml
|
||||
from app import log
|
||||
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
|
||||
from app.scodoc.sco_exceptions import AccessDenied, ScoPermissionDenied, ScoValueError
|
||||
from app.scodoc.sco_import_users import generate_password
|
||||
from app.scodoc.sco_permissions_check import can_handle_passwd
|
||||
from app.scodoc.TrivialFormulator import TrivialFormulator, tf_error_message
|
||||
@ -141,11 +141,71 @@ def index_html(all_depts=False, with_inactives=False, format="html"):
|
||||
)
|
||||
|
||||
|
||||
def _get_administrable_depts() -> list[str]:
|
||||
"""Liste des acronymes des départements dans lesquels l'utilisateur
|
||||
courant peut administrer des utilisateurs.
|
||||
Si SuperAdmin, tous les départements
|
||||
Sinon, les départements dans lesquels l'utilisateur a la permission ScoUsersAdmin
|
||||
"""
|
||||
#
|
||||
if current_user.is_administrator():
|
||||
log(f"create_user_form called by {current_user.user_name} (super admin)")
|
||||
administrable_dept_acronyms = sorted(
|
||||
[d.acronym for d in Departement.query.all()]
|
||||
)
|
||||
else:
|
||||
administrable_dept_acronyms = current_user.get_depts_with_permission(
|
||||
Permission.ScoUsersAdmin
|
||||
)
|
||||
if None in administrable_dept_acronyms:
|
||||
administrable_dept_acronyms = sorted(
|
||||
[d.acronym for d in Departement.query.all()]
|
||||
)
|
||||
|
||||
return administrable_dept_acronyms
|
||||
|
||||
|
||||
def _get_editable_roles(
|
||||
administrable_dept_acronyms: list = None, all_roles=True
|
||||
) -> set[tuple[Role, str]]:
|
||||
"""Rôles modifiables: ensemble de tuples (role, dept_acronym)
|
||||
(où dept_acronym est None si tous dept.)
|
||||
|
||||
Si all_roles, tous les rôles définis et modifiables par l'utilisateurs.
|
||||
Sinon, seulement les rôles "standards" de ScoDoc.
|
||||
"""
|
||||
if all_roles:
|
||||
# tous sauf SuperAdmin
|
||||
roles = [
|
||||
r
|
||||
for r in Role.query.all()
|
||||
if r.permissions != Permission.ALL_PERMISSIONS[0]
|
||||
]
|
||||
else:
|
||||
# Les rôles standards créés à l'initialisation de ScoDoc:
|
||||
roles = [
|
||||
Role.get_named_role(r) for r in sco_roles_default.ROLES_ATTRIBUABLES_DEPT
|
||||
]
|
||||
|
||||
# Génère toutes les combinaisons roles/départements
|
||||
editable_roles_set = {
|
||||
(r, dept) for r in roles for dept in administrable_dept_acronyms
|
||||
}
|
||||
if current_user.is_administrator():
|
||||
editable_roles_set |= {
|
||||
(Role.get_named_role(r), None)
|
||||
for r in sco_roles_default.ROLES_ATTRIBUABLES_SCODOC
|
||||
}
|
||||
# Un super-admin peut nommer d'autres super-admin:
|
||||
editable_roles_set |= {(Role.get_named_role("SuperAdmin"), None)}
|
||||
return editable_roles_set
|
||||
|
||||
|
||||
@bp.route("/create_user_form", methods=["GET", "POST"])
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoUsersAdmin)
|
||||
@scodoc7func
|
||||
def create_user_form(user_name=None, edit=0, all_roles=False):
|
||||
def create_user_form(user_name=None, edit=0, all_roles=True):
|
||||
"form. création ou édition utilisateur"
|
||||
if user_name is not None: # scodoc7func converti en int !
|
||||
user_name = str(user_name)
|
||||
@ -162,6 +222,7 @@ def create_user_form(user_name=None, edit=0, all_roles=False):
|
||||
)
|
||||
]
|
||||
F = html_sco_header.sco_footer()
|
||||
the_user: User = None
|
||||
if edit:
|
||||
if not user_name:
|
||||
raise ValueError("missing argument: user_name")
|
||||
@ -170,60 +231,6 @@ def create_user_form(user_name=None, edit=0, all_roles=False):
|
||||
raise ScoValueError("utilisateur inexistant")
|
||||
initvalues = the_user.to_dict()
|
||||
H.append(f"<h2>Modification de l'utilisateur {user_name}</h2>")
|
||||
else:
|
||||
H.append("<h2>Création d'un utilisateur</h2>")
|
||||
|
||||
is_super_admin = False
|
||||
if current_user.has_permission(Permission.ScoSuperAdmin, g.scodoc_dept):
|
||||
H.append("""<p class="warning">Vous êtes super administrateur !</p>""")
|
||||
is_super_admin = True
|
||||
|
||||
if all_roles:
|
||||
# tous sauf SuperAdmin
|
||||
standard_roles = [
|
||||
r
|
||||
for r in Role.query.all()
|
||||
if r.permissions != Permission.ALL_PERMISSIONS[0]
|
||||
]
|
||||
else:
|
||||
# Les rôles standards créés à l'initialisation de ScoDoc:
|
||||
standard_roles = [
|
||||
Role.get_named_role(r) for r in sco_roles_default.ROLES_ATTRIBUABLES_DEPT
|
||||
]
|
||||
# Départements auxquels ont peut associer des rôles via ce dialogue:
|
||||
# si SuperAdmin, tous les rôles standards dans tous les départements
|
||||
# sinon, les départements dans lesquels l'utilisateur a la permission ScoUsersAdmin
|
||||
if is_super_admin:
|
||||
log(f"create_user_form called by {current_user.user_name} (super admin)")
|
||||
administrable_dept_acronyms = [d.acronym for d in Departement.query.all()]
|
||||
else:
|
||||
# Si on n'est pas SuperAdmin, liste les départements dans lesquels on a la
|
||||
# permission ScoUsersAdmin
|
||||
administrable_dept_acronyms = sorted(
|
||||
set(
|
||||
[
|
||||
x.dept or ""
|
||||
for x in UserRole.query.filter_by(user=current_user)
|
||||
if x.role.has_permission(Permission.ScoUsersAdmin) and x.dept
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
editable_roles_set = {
|
||||
(r, dept) for r in standard_roles for dept in administrable_dept_acronyms
|
||||
}
|
||||
if current_user.is_administrator():
|
||||
editable_roles_set |= {
|
||||
(Role.get_named_role(r), None)
|
||||
for r in sco_roles_default.ROLES_ATTRIBUABLES_SCODOC
|
||||
}
|
||||
# Un super-admin peut nommer d'autres super-admin:
|
||||
editable_roles_set |= {(Role.get_named_role("SuperAdmin"), None)}
|
||||
#
|
||||
if not edit:
|
||||
submitlabel = "Créer utilisateur"
|
||||
orig_roles = set()
|
||||
else:
|
||||
submitlabel = "Modifier utilisateur"
|
||||
if "roles_string" in initvalues:
|
||||
initvalues["roles"] = initvalues["roles_string"].split(",")
|
||||
@ -243,6 +250,26 @@ def create_user_form(user_name=None, edit=0, all_roles=False):
|
||||
}
|
||||
if not initvalues["active"]:
|
||||
editable_roles_set = set() # can't change roles of a disabled user
|
||||
else:
|
||||
H.append("<h2>Création d'un utilisateur</h2>")
|
||||
submitlabel = "Créer utilisateur"
|
||||
orig_roles = set()
|
||||
|
||||
is_super_admin = current_user.is_administrator()
|
||||
if is_super_admin:
|
||||
H.append("""<p class="warning">Vous êtes super administrateur !</p>""")
|
||||
|
||||
administrable_dept_acronyms = _get_administrable_depts()
|
||||
if edit:
|
||||
if the_user.dept is None: # seul le super admin peut le toucher
|
||||
edit_only_roles = not current_user.is_administrator()
|
||||
else:
|
||||
edit_only_roles = the_user.dept not in administrable_dept_acronyms
|
||||
else:
|
||||
edit_only_roles = False # création nouvel utilisateur
|
||||
editable_roles_set = _get_editable_roles(
|
||||
administrable_dept_acronyms, all_roles=all_roles
|
||||
)
|
||||
editable_roles_strings = {
|
||||
r.name + "_" + (dept or "") for (r, dept) in editable_roles_set
|
||||
}
|
||||
@ -265,12 +292,29 @@ def create_user_form(user_name=None, edit=0, all_roles=False):
|
||||
for i, role_string in enumerate(displayed_roles_strings):
|
||||
if role_string not in editable_roles_strings:
|
||||
disabled_roles[i] = True
|
||||
# Formulaire:
|
||||
descr = [
|
||||
("edit", {"input_type": "hidden", "default": edit}),
|
||||
("nom", {"title": "Nom", "size": 20, "allow_null": False}),
|
||||
("prenom", {"title": "Prénom", "size": 20, "allow_null": False}),
|
||||
(
|
||||
"nom",
|
||||
{
|
||||
"title": "Nom",
|
||||
"size": 20,
|
||||
"allow_null": False,
|
||||
"readonly": edit_only_roles,
|
||||
},
|
||||
),
|
||||
(
|
||||
"prenom",
|
||||
{
|
||||
"title": "Prénom",
|
||||
"size": 20,
|
||||
"allow_null": False,
|
||||
"readonly": edit_only_roles,
|
||||
},
|
||||
),
|
||||
]
|
||||
if current_user.user_name != user_name:
|
||||
if current_user.user_name != user_name and not edit_only_roles:
|
||||
# no one can change its own status
|
||||
descr.append(
|
||||
(
|
||||
@ -316,13 +360,12 @@ def create_user_form(user_name=None, edit=0, all_roles=False):
|
||||
},
|
||||
),
|
||||
]
|
||||
else:
|
||||
else: # edition: on ne peut pas changer user_name
|
||||
descr += [
|
||||
(
|
||||
"user_name",
|
||||
{"input_type": "hidden", "default": initvalues["user_name"]},
|
||||
),
|
||||
("user_name", {"input_type": "hidden", "default": initvalues["user_name"]}),
|
||||
)
|
||||
]
|
||||
descr += [
|
||||
(
|
||||
@ -330,9 +373,12 @@ def create_user_form(user_name=None, edit=0, all_roles=False):
|
||||
{
|
||||
"title": "e-mail",
|
||||
"input_type": "text",
|
||||
"explanation": "requis, doit fonctionner",
|
||||
"explanation": "requis, doit fonctionner"
|
||||
if not edit_only_roles
|
||||
else "",
|
||||
"size": 20,
|
||||
"allow_null": False,
|
||||
"readonly": edit_only_roles,
|
||||
},
|
||||
)
|
||||
]
|
||||
@ -362,34 +408,11 @@ def create_user_form(user_name=None, edit=0, all_roles=False):
|
||||
},
|
||||
),
|
||||
]
|
||||
# Si auth n'a pas de departement (admin global)
|
||||
# propose de choisir librement le dept du nouvel utilisateur
|
||||
# sinon, menu proposant l'ensembe des départements dans lesquels
|
||||
# nous avons la permission ScoUserAdmin + le dept actuel de l'utilisateur
|
||||
# modifié.
|
||||
if not auth_dept:
|
||||
descr.append(
|
||||
(
|
||||
"dept",
|
||||
{
|
||||
"title": "Département",
|
||||
"input_type": "text",
|
||||
"size": 12,
|
||||
"allow_null": True,
|
||||
"explanation": """département de rattachement de l'utilisateur
|
||||
(s'il s'agit d'un administrateur, laisser vide si vous voulez
|
||||
qu'il puisse créer des utilisateurs dans d'autres départements)
|
||||
""",
|
||||
},
|
||||
)
|
||||
)
|
||||
can_choose_dept = True
|
||||
else:
|
||||
# Si SuperAdmin, propose de choisir librement le dept du nouvel utilisateur
|
||||
selectable_dept_acronyms = set(administrable_dept_acronyms)
|
||||
if edit and the_user.dept is not None: # ajoute dept actuel de l'utilisateur
|
||||
selectable_dept_acronyms |= {the_user.dept}
|
||||
if len(selectable_dept_acronyms) > 1:
|
||||
can_choose_dept = True
|
||||
if is_super_admin and len(selectable_dept_acronyms) > 1:
|
||||
selectable_dept_acronyms = sorted(list(selectable_dept_acronyms))
|
||||
descr.append(
|
||||
(
|
||||
@ -402,10 +425,11 @@ def create_user_form(user_name=None, edit=0, all_roles=False):
|
||||
"allowed_values": selectable_dept_acronyms,
|
||||
"default": g.scodoc_dept
|
||||
if g.scodoc_dept in selectable_dept_acronyms
|
||||
else "",
|
||||
else (auth_dept or ""),
|
||||
},
|
||||
)
|
||||
)
|
||||
can_choose_dept = True
|
||||
else: # pas de choix de département
|
||||
can_choose_dept = False
|
||||
if edit:
|
||||
@ -414,7 +438,7 @@ def create_user_form(user_name=None, edit=0, all_roles=False):
|
||||
"d",
|
||||
{
|
||||
"input_type": "separator",
|
||||
"title": f"L'utilisateur appartient au département {auth_dept}",
|
||||
"title": f"""L'utilisateur appartient au département {the_user.dept or "(tous)"}""",
|
||||
},
|
||||
)
|
||||
)
|
||||
@ -435,9 +459,12 @@ def create_user_form(user_name=None, edit=0, all_roles=False):
|
||||
{
|
||||
"title": "Date d'expiration", # j/m/a
|
||||
"input_type": "datedmy",
|
||||
"explanation": "j/m/a, laisser vide si pas de limite",
|
||||
"explanation": "j/m/a, laisser vide si pas de limite"
|
||||
if not edit_only_roles
|
||||
else "",
|
||||
"size": 9,
|
||||
"allow_null": True,
|
||||
"readonly": edit_only_roles,
|
||||
},
|
||||
),
|
||||
(
|
||||
@ -451,6 +478,9 @@ def create_user_form(user_name=None, edit=0, all_roles=False):
|
||||
"disabled_items": disabled_roles,
|
||||
},
|
||||
),
|
||||
]
|
||||
if not edit_only_roles:
|
||||
descr += [
|
||||
(
|
||||
"force",
|
||||
{
|
||||
@ -491,7 +521,7 @@ def create_user_form(user_name=None, edit=0, all_roles=False):
|
||||
else:
|
||||
edit = 0
|
||||
try:
|
||||
force = int(vals["force"][0])
|
||||
force = int(vals.get("force", "0")[0])
|
||||
except (IndexError, ValueError, TypeError):
|
||||
force = 0
|
||||
|
||||
@ -511,7 +541,9 @@ def create_user_form(user_name=None, edit=0, all_roles=False):
|
||||
if err:
|
||||
H.append(tf_error_message(f"""Erreur: {err}"""))
|
||||
return "\n".join(H) + "\n" + tf[1] + F
|
||||
ok, msg = sco_users.check_modif_user(
|
||||
|
||||
if not edit_only_roles:
|
||||
ok_modif, msg = sco_users.check_modif_user(
|
||||
edit,
|
||||
enforce_optionals=not force,
|
||||
user_name=user_name,
|
||||
@ -521,7 +553,7 @@ def create_user_form(user_name=None, edit=0, all_roles=False):
|
||||
dept=vals.get("dept", auth_dept),
|
||||
roles=vals["roles"],
|
||||
)
|
||||
if not ok:
|
||||
if not ok_modif:
|
||||
H.append(tf_error_message(msg))
|
||||
return "\n".join(H) + "\n" + tf[1] + F
|
||||
|
||||
@ -566,11 +598,14 @@ def create_user_form(user_name=None, edit=0, all_roles=False):
|
||||
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}")
|
||||
sco_users.user_edit(user_name, vals)
|
||||
flash(f"Utilisateur {user_name} modifié")
|
||||
else:
|
||||
sco_users.user_edit(user_name, {"roles_string": vals["roles_string"]})
|
||||
return flask.redirect(
|
||||
url_for(
|
||||
"users.user_info_page",
|
||||
@ -946,14 +981,19 @@ def toggle_active_user(user_name: str = None):
|
||||
"""Change active status of a user account"""
|
||||
if user_name is not None: # scodoc7func converti en int !
|
||||
user_name = str(user_name)
|
||||
|
||||
u = User.query.filter_by(user_name=user_name).first()
|
||||
if not u:
|
||||
raise ScoValueError("invalid user_name")
|
||||
# permission check:
|
||||
if not (
|
||||
current_user.is_administrator()
|
||||
or current_user.has_permission(Permission.ScoUsersAdmin, u.dept)
|
||||
):
|
||||
raise ScoPermissionDenied()
|
||||
form = DeactivateUserForm()
|
||||
if (
|
||||
request.method == "POST" and form.cancel.data
|
||||
): # if cancel button is clicked, the form.cancel.data will be True
|
||||
# flash
|
||||
if request.method == "POST" and form.cancel.data:
|
||||
# if cancel button is clicked, the form.cancel.data will be True
|
||||
return redirect(url_for("users.index_html", scodoc_dept=g.scodoc_dept))
|
||||
if form.validate_on_submit():
|
||||
u.active = not u.active
|
||||
|
Loading…
Reference in New Issue
Block a user