From 18263678c27744f33929ba2785adaedd6385ede5 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Fri, 29 Sep 2023 21:17:31 +0200 Subject: [PATCH] =?UTF-8?q?Configuration:=20=C3=A9diteur=20de=20r=C3=B4les?= =?UTF-8?q?=20et=20permissions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/assiduites.py | 10 +- app/api/billets_absences.py | 4 +- app/api/etudiants.py | 4 +- app/api/evaluations.py | 6 +- app/api/formations.py | 6 +- app/api/jury.py | 12 +- app/api/justificatifs.py | 16 +- app/api/semset.py | 2 +- app/api/users.py | 28 ++-- app/auth/models.py | 4 + app/auth/routes.py | 24 ++- app/entreprises/app_relations_entreprises.py | 2 +- app/entreprises/routes.py | 102 +++++------ app/forms/generic.py | 17 ++ app/forms/main/role_create.py | 58 +++++++ app/models/formsemestre.py | 10 +- app/models/moduleimpls.py | 6 +- app/scodoc/html_sidebar.py | 10 +- app/scodoc/sco_archives_etud.py | 2 +- app/scodoc/sco_bulletins.py | 6 +- app/scodoc/sco_bulletins_legacy.py | 4 +- app/scodoc/sco_bulletins_standard.py | 4 +- app/scodoc/sco_dept.py | 6 +- app/scodoc/sco_edit_module.py | 2 +- app/scodoc/sco_edit_ue.py | 8 +- app/scodoc/sco_formation_recap.py | 4 +- app/scodoc/sco_formations.py | 2 +- app/scodoc/sco_formsemestre_edit.py | 10 +- app/scodoc/sco_formsemestre_status.py | 20 +-- app/scodoc/sco_formsemestre_validation.py | 2 +- app/scodoc/sco_groups_view.py | 8 +- app/scodoc/sco_moduleimpl.py | 4 +- app/scodoc/sco_moduleimpl_inscriptions.py | 4 +- app/scodoc/sco_moduleimpl_status.py | 7 +- app/scodoc/sco_page_etud.py | 38 ++--- app/scodoc/sco_permissions.py | 60 +++---- app/scodoc/sco_permissions_check.py | 18 +- app/scodoc/sco_roles_default.py | 95 +++++------ app/scodoc/sco_synchro_etuds.py | 2 +- app/scodoc/sco_ue_external.py | 2 +- app/scodoc/sco_users.py | 4 +- app/static/css/role_editor.css | 48 +++++- app/templates/auth/user_info_page.j2 | 8 +- app/templates/base.j2 | 2 +- app/templates/but/parcour_formation.j2 | 2 +- app/templates/entreprises/_correspondant.j2 | 2 +- app/templates/entreprises/_offre.j2 | 10 +- app/templates/entreprises/contacts.j2 | 8 +- app/templates/entreprises/entreprises.j2 | 12 +- app/templates/entreprises/fiche_entreprise.j2 | 16 +- .../fiche_entreprise_validation.j2 | 2 +- app/templates/entreprises/nav.j2 | 4 +- .../{entreprises => }/form_confirmation.j2 | 2 +- app/templates/main/index.j2 | 2 +- app/templates/role_editor.j2 | 69 -------- app/templates/sco_value_error.j2 | 8 +- app/templates/scodoc/permission_info.j2 | 33 ++++ app/templates/scodoc/role_create.j2 | 22 +++ app/templates/scodoc/role_editor.j2 | 112 +++++++++++++ app/templates/scodoc/role_info.j2 | 46 +++++ app/templates/sidebar.j2 | 10 +- app/views/absences.py | 10 +- app/views/assiduites.py | 12 +- app/views/but_formation.py | 2 +- app/views/notes.py | 158 +++++++++--------- app/views/notes_formsemestre.py | 2 +- app/views/pn_modules.py | 4 +- app/views/refcomp.py | 10 +- app/views/scodoc.py | 84 +++++++++- app/views/scolar.py | 64 +++---- app/views/users.py | 22 +-- .../versions/57179ae34069_api_permissions.py | 2 +- scodoc.py | 2 +- tests/api/make_samples.py | 2 +- tests/api/test_api_permissions.py | 2 +- tests/api/test_api_users.py | 16 +- tests/ressources/samples/samples.csv | 6 +- tests/unit/test_users.py | 12 +- .../fakedatabase/create_test_api_database.py | 10 +- 79 files changed, 910 insertions(+), 561 deletions(-) create mode 100644 app/forms/generic.py create mode 100644 app/forms/main/role_create.py rename app/templates/{entreprises => }/form_confirmation.j2 (83%) delete mode 100644 app/templates/role_editor.j2 create mode 100644 app/templates/scodoc/permission_info.j2 create mode 100644 app/templates/scodoc/role_create.j2 create mode 100644 app/templates/scodoc/role_editor.j2 create mode 100644 app/templates/scodoc/role_info.j2 diff --git a/app/api/assiduites.py b/app/api/assiduites.py index 295fd2ab..64b0e509 100644 --- a/app/api/assiduites.py +++ b/app/api/assiduites.py @@ -451,7 +451,7 @@ def count_assiduites_formsemestre( @scodoc @as_json @login_required -@permission_required(Permission.ScoAbsChange) +@permission_required(Permission.AbsChange) def assiduite_create(etudid: int = None, nip=None, ine=None): """ Création d'une assiduité pour l'étudiant (etudid) @@ -506,7 +506,7 @@ def assiduite_create(etudid: int = None, nip=None, ine=None): @scodoc @as_json @login_required -@permission_required(Permission.ScoAbsChange) +@permission_required(Permission.AbsChange) def assiduites_create(): """ Création d'une assiduité ou plusieurs assiduites @@ -648,7 +648,7 @@ def _create_singular( @login_required @scodoc @as_json -@permission_required(Permission.ScoAbsChange) +@permission_required(Permission.AbsChange) def assiduite_delete(): """ Suppression d'une assiduité à partir de son id @@ -700,7 +700,7 @@ def _delete_singular(assiduite_id: int, database): @login_required @scodoc @as_json -@permission_required(Permission.ScoAbsChange) +@permission_required(Permission.AbsChange) def assiduite_edit(assiduite_id: int): """ Edition d'une assiduité à partir de son id @@ -740,7 +740,7 @@ def assiduite_edit(assiduite_id: int): @login_required @scodoc @as_json -@permission_required(Permission.ScoAbsChange) +@permission_required(Permission.AbsChange) def assiduites_edit(): """ Edition de plusieurs assiduités diff --git a/app/api/billets_absences.py b/app/api/billets_absences.py index 7ada60f6..98c89dfb 100644 --- a/app/api/billets_absences.py +++ b/app/api/billets_absences.py @@ -38,7 +38,7 @@ def billets_absence_etudiant(etudid: int): @api_web_bp.route("/billets_absence/create", methods=["POST"]) @login_required @scodoc -@permission_required(Permission.ScoAbsAddBillet) +@permission_required(Permission.AbsAddBillet) @as_json def billets_absence_create(): """Ajout d'un billet d'absence""" @@ -70,7 +70,7 @@ def billets_absence_create(): @api_web_bp.route("/billets_absence//delete", methods=["POST"]) @login_required @scodoc -@permission_required(Permission.ScoAbsAddBillet) +@permission_required(Permission.AbsAddBillet) @as_json def billets_absence_delete(billet_id: int): """Suppression d'un billet d'absence""" diff --git a/app/api/etudiants.py b/app/api/etudiants.py index 53f2ed3e..41a95760 100755 --- a/app/api/etudiants.py +++ b/app/api/etudiants.py @@ -178,11 +178,11 @@ def get_photo_image(etudid: int = None, nip: str = None, ine: str = None): @api_web_bp.route("/etudiant/etudid//photo", methods=["POST"]) @login_required @scodoc -@permission_required(Permission.ScoEtudChangeAdr) +@permission_required(Permission.EtudChangeAdr) @as_json def set_photo_image(etudid: int = None): """Enregistre la photo de l'étudiant.""" - allowed_depts = current_user.get_depts_with_permission(Permission.ScoEtudChangeAdr) + allowed_depts = current_user.get_depts_with_permission(Permission.EtudChangeAdr) query = Identite.query.filter_by(id=etudid) if not None in allowed_depts: # restreint aux départements autorisés: diff --git a/app/api/evaluations.py b/app/api/evaluations.py index b1ffee78..1834f8eb 100644 --- a/app/api/evaluations.py +++ b/app/api/evaluations.py @@ -146,7 +146,7 @@ def evaluation_notes(evaluation_id: int): @api_web_bp.route("/evaluation//notes/set", methods=["POST"]) @login_required @scodoc -@permission_required(Permission.ScoEnsView) +@permission_required(Permission.EnsView) @as_json def evaluation_set_notes(evaluation_id: int): """Écriture de notes dans une évaluation. @@ -187,7 +187,7 @@ def evaluation_set_notes(evaluation_id: int): @api_web_bp.route("/moduleimpl//evaluation/create", methods=["POST"]) @login_required @scodoc -@permission_required(Permission.ScoEnsView) # permission gérée dans la fonction +@permission_required(Permission.EnsView) # permission gérée dans la fonction @as_json def evaluation_create(moduleimpl_id: int): """Création d'une évaluation. @@ -251,7 +251,7 @@ def evaluation_create(moduleimpl_id: int): @api_web_bp.route("/evaluation//delete", methods=["POST"]) @login_required @scodoc -@permission_required(Permission.ScoEnsView) # permission gérée dans la fonction +@permission_required(Permission.EnsView) # permission gérée dans la fonction @as_json def evaluation_delete(evaluation_id: int): """Suppression d'une évaluation. diff --git a/app/api/formations.py b/app/api/formations.py index e8a145a5..8a5afe5b 100644 --- a/app/api/formations.py +++ b/app/api/formations.py @@ -251,7 +251,7 @@ def referentiel_competences(formation_id: int): @api_web_bp.route("/set_ue_parcours/", methods=["POST"]) @login_required @scodoc -@permission_required(Permission.ScoChangeFormation) +@permission_required(Permission.EditFormation) @as_json def set_ue_parcours(ue_id: int): """Associe UE et parcours BUT. @@ -286,7 +286,7 @@ def set_ue_parcours(ue_id: int): ) @login_required @scodoc -@permission_required(Permission.ScoChangeFormation) +@permission_required(Permission.EditFormation) @as_json def assoc_ue_niveau(ue_id: int, niveau_id: int): """Associe l'UE au niveau de compétence""" @@ -315,7 +315,7 @@ def assoc_ue_niveau(ue_id: int, niveau_id: int): ) @login_required @scodoc -@permission_required(Permission.ScoChangeFormation) +@permission_required(Permission.EditFormation) @as_json def desassoc_ue_niveau(ue_id: int): """Désassocie cette UE de son niveau de compétence diff --git a/app/api/jury.py b/app/api/jury.py index 9d4aad56..a48b6357 100644 --- a/app/api/jury.py +++ b/app/api/jury.py @@ -120,7 +120,7 @@ def _validation_ue_delete(etudid: int, validation_id: int): # (c'est le cas pour les validations de jury, mais pas pour les "antérieures" non # rattachées à un formsemestre) if not g.scodoc_dept: # accès API - if not current_user.has_permission(Permission.ScoEtudInscrit): + if not current_user.has_permission(Permission.EtudInscrit): return json_error(403, "opération non autorisée (117)") else: if validation.formsemestre: @@ -128,7 +128,7 @@ def _validation_ue_delete(etudid: int, validation_id: int): validation.formsemestre.dept_id != g.scodoc_dept_id ) or not validation.formsemestre.can_edit_jury(): return json_error(403, "opération non autorisée (123)") - elif not current_user.has_permission(Permission.ScoEtudInscrit): + elif not current_user.has_permission(Permission.EtudInscrit): # Validation non rattachée à un semestre: on doit être chef return json_error(403, "opération non autorisée (126)") @@ -150,7 +150,7 @@ def _validation_ue_delete(etudid: int, validation_id: int): ) @login_required @scodoc -@permission_required(Permission.ScoEtudInscrit) +@permission_required(Permission.EtudInscrit) @as_json def autorisation_inscription_delete(etudid: int, validation_id: int): "Efface cette validation" @@ -178,7 +178,7 @@ def autorisation_inscription_delete(etudid: int, validation_id: int): ) @login_required @scodoc -@permission_required(Permission.ScoEtudInscrit) +@permission_required(Permission.EtudInscrit) @as_json def validation_rcue_record(etudid: int): """Enregistre une validation de RCUE. @@ -305,7 +305,7 @@ def validation_rcue_record(etudid: int): ) @login_required @scodoc -@permission_required(Permission.ScoEtudInscrit) +@permission_required(Permission.EtudInscrit) @as_json def validation_rcue_delete(etudid: int, validation_id: int): "Efface cette validation" @@ -333,7 +333,7 @@ def validation_rcue_delete(etudid: int, validation_id: int): ) @login_required @scodoc -@permission_required(Permission.ScoEtudInscrit) +@permission_required(Permission.EtudInscrit) @as_json def validation_annee_but_delete(etudid: int, validation_id: int): "Efface cette validation" diff --git a/app/api/justificatifs.py b/app/api/justificatifs.py index a85685f7..bac373bf 100644 --- a/app/api/justificatifs.py +++ b/app/api/justificatifs.py @@ -241,7 +241,7 @@ def justificatifs_formsemestre(formsemestre_id: int, with_query: bool = False): @scodoc @login_required @as_json -@permission_required(Permission.ScoAbsChange) +@permission_required(Permission.AbsChange) def justif_create(etudid: int = None, nip=None, ine=None): """ Création d'un justificatif pour l'étudiant (etudid) @@ -372,7 +372,7 @@ def _create_singular( @login_required @scodoc @as_json -@permission_required(Permission.ScoAbsChange) +@permission_required(Permission.AbsChange) def justif_edit(justif_id: int): """ Edition d'un justificatif à partir de son id @@ -471,7 +471,7 @@ def justif_edit(justif_id: int): @login_required @scodoc @as_json -@permission_required(Permission.ScoAbsChange) +@permission_required(Permission.AbsChange) def justif_delete(): """ Suppression d'un justificatif à partir de son id @@ -534,7 +534,7 @@ def _delete_singular(justif_id: int, database): @scodoc @login_required @as_json -@permission_required(Permission.ScoAbsChange) +@permission_required(Permission.AbsChange) def justif_import(justif_id: int = None): """ Importation d'un fichier (création d'archive) @@ -579,7 +579,7 @@ def justif_import(justif_id: int = None): @api_web_bp.route("/justificatif//export/", methods=["POST"]) @scodoc @login_required -@permission_required(Permission.ScoAbsChange) +@permission_required(Permission.AbsChange) def justif_export(justif_id: int = None, filename: str = None): """ Retourne un fichier d'une archive d'un justificatif @@ -610,7 +610,7 @@ def justif_export(justif_id: int = None, filename: str = None): @scodoc @login_required @as_json -@permission_required(Permission.ScoAbsChange) +@permission_required(Permission.AbsChange) def justif_remove(justif_id: int = None): """ Supression d'un fichier ou d'une archive @@ -700,7 +700,7 @@ def justif_list(justif_id: int = None): for filename in filenames: if int(filename[1]) == current_user.id or current_user.has_permission( - Permission.ScoJustifView + Permission.AbsJustifView ): retour["filenames"].append(filename[0]) return retour @@ -712,7 +712,7 @@ def justif_list(justif_id: int = None): @scodoc @login_required @as_json -@permission_required(Permission.ScoAbsChange) +@permission_required(Permission.AbsChange) def justif_justifies(justif_id: int = None): """ Liste assiduite_id justifiées par le justificatif diff --git a/app/api/semset.py b/app/api/semset.py index 5eba3f3e..9869e6f8 100644 --- a/app/api/semset.py +++ b/app/api/semset.py @@ -23,7 +23,7 @@ # @api_web_bp.route("/semset/set_periode/", methods=["POST"]) # @login_required # @scodoc -# @permission_required(Permission.ScoEditApo) +# @permission_required(Permission.EditApogee) # # TODO à modifier pour utiliser @as_json # def semset_set_periode(semset_id: int): # "Change la période d'un semset" diff --git a/app/api/users.py b/app/api/users.py index cf017ee6..4fe89510 100644 --- a/app/api/users.py +++ b/app/api/users.py @@ -13,7 +13,7 @@ from flask import g, request from flask_json import as_json from flask_login import current_user, login_required -from app import db +from app import db, log from app.api import api_bp as bp, api_web_bp, API_CLIENT_ERROR from app.scodoc.sco_utils import json_error from app.auth.models import User, Role, UserRole @@ -29,7 +29,7 @@ from app.scodoc import sco_utils as scu @api_web_bp.route("/user/") @login_required @scodoc -@permission_required(Permission.ScoUsersView) +@permission_required(Permission.UsersView) @as_json def user_info(uid: int): """ @@ -39,7 +39,7 @@ def user_info(uid: int): if user is None: return json_error(404, "user not found") if g.scodoc_dept: - allowed_depts = current_user.get_depts_with_permission(Permission.ScoUsersView) + allowed_depts = current_user.get_depts_with_permission(Permission.UsersView) if (None not in allowed_depts) and (user.dept not in allowed_depts): return json_error(404, "user not found") @@ -78,7 +78,7 @@ def users_info_query(): query.join(UserRole, (UserRole.dept == User.dept) | (UserRole.dept == None)) .filter(UserRole.user == current_user) .join(Role, UserRole.role_id == Role.id) - .filter(Role.permissions.op("&")(Permission.ScoUsersView) != 0) + .filter(Role.permissions.op("&")(Permission.UsersView) != 0) ) query = query.order_by(User.user_name) @@ -89,7 +89,7 @@ def users_info_query(): @api_web_bp.route("/user/create", methods=["POST"]) @login_required @scodoc -@permission_required(Permission.ScoUsersAdmin) +@permission_required(Permission.UsersAdmin) @as_json def user_create(): """Création d'un utilisateur @@ -112,7 +112,7 @@ def user_create(): dept = data.get("dept") if dept == "@all": dept = None - allowed_depts = current_user.get_depts_with_permission(Permission.ScoUsersAdmin) + allowed_depts = current_user.get_depts_with_permission(Permission.UsersAdmin) if (None not in allowed_depts) and (dept not in allowed_depts): return json_error(403, "user_create: departement non autorise") if (dept is not None) and ( @@ -132,7 +132,7 @@ def user_create(): @api_web_bp.route("/user//edit", methods=["POST"]) @login_required @scodoc -@permission_required(Permission.ScoUsersAdmin) +@permission_required(Permission.UsersAdmin) @as_json def user_edit(uid: int): """Modification d'un utilisateur @@ -152,7 +152,7 @@ def user_edit(uid: int): if dest_dept is not False: if dest_dept == "@all": dest_dept = None - allowed_depts = current_user.get_depts_with_permission(Permission.ScoUsersAdmin) + allowed_depts = current_user.get_depts_with_permission(Permission.UsersAdmin) if (None not in allowed_depts) and ( (orig_dept not in allowed_depts) or (dest_dept not in allowed_depts) ): @@ -177,7 +177,7 @@ def user_edit(uid: int): @api_web_bp.route("/user//password", methods=["POST"]) @login_required @scodoc -@permission_required(Permission.ScoUsersAdmin) +@permission_required(Permission.UsersAdmin) @as_json def user_password(uid: int): """Modification du mot de passe d'un utilisateur @@ -194,7 +194,7 @@ def user_password(uid: int): return json_error(404, "user_password: missing password") if not is_valid_password(password): return json_error(API_CLIENT_ERROR, "user_password: invalid password") - allowed_depts = current_user.get_depts_with_permission(Permission.ScoUsersAdmin) + allowed_depts = current_user.get_depts_with_permission(Permission.UsersAdmin) if (None not in allowed_depts) and ((user.dept not in allowed_depts)): return json_error(403, "user_password: departement non autorise") user.set_password(password) @@ -271,7 +271,7 @@ def user_role_remove(uid: int, role_name: str, dept: str = None): @api_web_bp.route("/permissions") @login_required @scodoc -@permission_required(Permission.ScoUsersView) +@permission_required(Permission.UsersView) @as_json def list_permissions(): """Liste des noms de permissions définies""" @@ -282,7 +282,7 @@ def list_permissions(): @api_web_bp.route("/role/") @login_required @scodoc -@permission_required(Permission.ScoUsersView) +@permission_required(Permission.UsersView) @as_json def list_role(role_name: str): """Un rôle""" @@ -293,7 +293,7 @@ def list_role(role_name: str): @api_web_bp.route("/roles") @login_required @scodoc -@permission_required(Permission.ScoUsersView) +@permission_required(Permission.UsersView) @as_json def list_roles(): """Tous les rôles définis""" @@ -321,6 +321,7 @@ def role_permission_add(role_name: str, perm_name: str): role.add_permission(permission) db.session.add(role) db.session.commit() + log(f"role_permission_add({role_name}, {perm_name})") return role.to_dict() @@ -345,6 +346,7 @@ def role_permission_remove(role_name: str, perm_name: str): role.remove_permission(permission) db.session.add(role) db.session.commit() + log(f"role_permission_remove({role_name}, {perm_name})") return role.to_dict() diff --git a/app/auth/models.py b/app/auth/models.py index 31151191..d0e85e91 100644 --- a/app/auth/models.py +++ b/app/auth/models.py @@ -541,6 +541,10 @@ class Role(db.Model): "Remove all permissions from role" self.permissions = 0 + def get_named_permissions(self) -> list[str]: + "List of the names of the permissions associated to this rôle" + return Permission.permissions_names(self.permissions) + def set_named_permissions(self, permission_names: list[str]): """Set permissions, given as a list of permissions names. Raises ScoValueError if invalid permission.""" diff --git a/app/auth/routes.py b/app/auth/routes.py index 235b9e5b..7818e60b 100644 --- a/app/auth/routes.py +++ b/app/auth/routes.py @@ -21,7 +21,9 @@ from app.auth.forms import ( from app.auth.models import Role, User, invalid_user_name from app.auth.email import send_password_reset_email from app.decorators import admin_required +from app.forms.generic import SimpleConfirmationForm from app.models.config import ScoDocSiteConfig +from app.scodoc.sco_roles_default import SCO_ROLES_DEFAULTS from app.scodoc import sco_utils as scu _ = lambda x: x # sans babel @@ -158,9 +160,25 @@ def reset_password(token): @admin_required def reset_standard_roles_permissions(): "Réinitialise (recrée au besoin) les rôles standards de ScoDoc et leurs permissions" - Role.reset_standard_roles_permissions() - flash("rôles standards réinitialisés !") - return redirect(url_for("scodoc.configuration")) + form = SimpleConfirmationForm() + if request.method == "POST" and form.cancel.data: + return redirect(url_for("scodoc.configuration")) + if form.validate_on_submit(): + Role.reset_standard_roles_permissions() + flash("rôles standards réinitialisés !") + return redirect(url_for("scodoc.configuration")) + return render_template( + "form_confirmation.j2", + title="Réinitialiser les roles standards de ScoDoc ?", + form=form, + info_message=f"""

Les rôles standards seront recréés et leurs permissions + réinitialisées aux valeurs par défaut de ScoDoc. +

+

+ Les rôles standards sont: {', '.join(SCO_ROLES_DEFAULTS.keys())} +

+ """, + ) @bp.route("/cas_users_generate_excel_sample") diff --git a/app/entreprises/app_relations_entreprises.py b/app/entreprises/app_relations_entreprises.py index e8cce1e1..b8bd683d 100644 --- a/app/entreprises/app_relations_entreprises.py +++ b/app/entreprises/app_relations_entreprises.py @@ -131,7 +131,7 @@ def check_offre_depts(depts: list, offre_depts: list): """ Retourne vrai si l'utilisateur a le droit de visibilité sur l'offre """ - if current_user.has_permission(Permission.RelationsEntreprisesChange, None): + if current_user.has_permission(Permission.RelationsEntrepEdit, None): return True for offre_dept in offre_depts: if offre_dept.dept_id in depts: diff --git a/app/entreprises/routes.py b/app/entreprises/routes.py index f9eac4f9..60d567ab 100644 --- a/app/entreprises/routes.py +++ b/app/entreprises/routes.py @@ -62,7 +62,7 @@ from config import Config @bp.route("/", methods=["GET", "POST"]) -@permission_required(Permission.RelationsEntreprisesView) +@permission_required(Permission.RelationsEntrepView) def index(): """ Permet d'afficher une page avec la liste des entreprises (visible et active) et une liste des dernières opérations @@ -98,7 +98,7 @@ def index(): @bp.route("/logs", methods=["GET"]) -@permission_required(Permission.RelationsEntreprisesView) +@permission_required(Permission.RelationsEntrepView) def logs(): """ Permet d'afficher les logs (toutes les entreprises) @@ -115,7 +115,7 @@ def logs(): @bp.route("/correspondants", methods=["GET"]) -@permission_required(Permission.RelationsEntreprisesCorrespondants) +@permission_required(Permission.RelationsEntrepViewCorrs) def correspondants(): """ Permet d'afficher une page avec la liste des correspondants des entreprises visibles et une liste des dernières opérations @@ -141,7 +141,7 @@ def correspondants(): @bp.route("/validation", methods=["GET"]) -@permission_required(Permission.RelationsEntreprisesValidate) +@permission_required(Permission.RelationsEntrepValidate) def validation(): """ Permet d'afficher une page avec la liste des entreprises a valider (non visible) @@ -155,7 +155,7 @@ def validation(): @bp.route("/fiche_entreprise_validation/", methods=["GET"]) -@permission_required(Permission.RelationsEntreprisesValidate) +@permission_required(Permission.RelationsEntrepValidate) def fiche_entreprise_validation(entreprise_id): """ Permet d'afficher la fiche entreprise d'une entreprise a valider @@ -176,7 +176,7 @@ def fiche_entreprise_validation(entreprise_id): "/fiche_entreprise_validation//validate_entreprise", methods=["GET", "POST"], ) -@permission_required(Permission.RelationsEntreprisesValidate) +@permission_required(Permission.RelationsEntrepValidate) def validate_entreprise(entreprise_id): """ Permet de valider une entreprise @@ -214,7 +214,7 @@ def validate_entreprise(entreprise_id): "/fiche_entreprise_validation//delete_validation_entreprise", methods=["GET", "POST"], ) -@permission_required(Permission.RelationsEntreprisesValidate) +@permission_required(Permission.RelationsEntrepValidate) def delete_validation_entreprise(entreprise_id): """ Permet de supprimer une entreprise en attente de validation avec une formulaire de validation @@ -241,7 +241,7 @@ def delete_validation_entreprise(entreprise_id): flash("L'entreprise a été supprimée de la liste des entreprises à valider.") return redirect(url_for("entreprises.validation")) return render_template( - "entreprises/form_confirmation.j2", + "form_confirmation.j2", title="Supression entreprise", form=form, info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression", @@ -249,7 +249,7 @@ def delete_validation_entreprise(entreprise_id): @bp.route("/offres_recues", methods=["GET"]) -@permission_required(Permission.RelationsEntreprisesView) +@permission_required(Permission.RelationsEntrepView) def offres_recues(): """ Permet d'afficher la page où l'on peut voir les offres reçues @@ -290,7 +290,7 @@ def offres_recues(): @bp.route( "/offres_recues/delete_offre_recue/", methods=["GET", "POST"] ) -@permission_required(Permission.RelationsEntreprisesView) +@permission_required(Permission.RelationsEntrepView) def delete_offre_recue(envoi_offre_id): """ Permet de supprimer une offre reçue @@ -304,7 +304,7 @@ def delete_offre_recue(envoi_offre_id): @bp.route("/preferences", methods=["GET", "POST"]) -@permission_required(Permission.RelationsEntreprisesValidate) +@permission_required(Permission.RelationsEntrepValidate) def preferences(): """ Permet d'afficher la page des préférences du module gestion des relations entreprises @@ -327,7 +327,7 @@ def preferences(): @bp.route("/add_entreprise", methods=["GET", "POST"]) -@permission_required(Permission.RelationsEntreprisesChange) +@permission_required(Permission.RelationsEntrepEdit) def add_entreprise(): """ Permet d'ajouter une entreprise dans la base avec un formulaire @@ -385,7 +385,7 @@ def add_entreprise(): notes=form.notes.data.strip(), ) db.session.add(correspondant) - if current_user.has_permission(Permission.RelationsEntreprisesValidate, None): + if current_user.has_permission(Permission.RelationsEntrepValidate, None): entreprise.visible = True lien_entreprise = f"{entreprise.nom}" log = EntrepriseHistorique( @@ -414,7 +414,7 @@ def add_entreprise(): @bp.route("/fiche_entreprise/", methods=["GET"]) -@permission_required(Permission.RelationsEntreprisesView) +@permission_required(Permission.RelationsEntrepView) def fiche_entreprise(entreprise_id): """ Permet d'afficher la fiche entreprise d'une entreprise avec une liste des dernières opérations et @@ -456,7 +456,7 @@ def fiche_entreprise(entreprise_id): @bp.route("/fiche_entreprise//logs", methods=["GET"]) -@permission_required(Permission.RelationsEntreprisesView) +@permission_required(Permission.RelationsEntrepView) def logs_entreprise(entreprise_id): """ Permet d'afficher les logs d'une entreprise @@ -479,7 +479,7 @@ def logs_entreprise(entreprise_id): @bp.route("/fiche_entreprise//offres_expirees") -@permission_required(Permission.RelationsEntreprisesView) +@permission_required(Permission.RelationsEntrepView) def offres_expirees(entreprise_id): """ Permet d'afficher la liste des offres expirés d'une entreprise @@ -499,7 +499,7 @@ def offres_expirees(entreprise_id): @bp.route( "/fiche_entreprise//edit_entreprise", methods=["GET", "POST"] ) -@permission_required(Permission.RelationsEntreprisesChange) +@permission_required(Permission.RelationsEntrepEdit) def edit_entreprise(entreprise_id): """ Permet de modifier une entreprise de la base avec un formulaire @@ -580,7 +580,7 @@ def edit_entreprise(entreprise_id): @bp.route("/fiche_entreprise//desactiver", methods=["GET", "POST"]) -@permission_required(Permission.RelationsEntreprisesChange) +@permission_required(Permission.RelationsEntrepEdit) def fiche_entreprise_desactiver(entreprise_id): """ Permet de désactiver une entreprise @@ -609,7 +609,7 @@ def fiche_entreprise_desactiver(entreprise_id): url_for("entreprises.fiche_entreprise", entreprise_id=entreprise.id) ) return render_template( - "entreprises/form_confirmation.j2", + "form_confirmation.j2", title="Désactiver entreprise", form=form, info_message="Cliquez sur le bouton Modifier pour confirmer la désactivation", @@ -617,7 +617,7 @@ def fiche_entreprise_desactiver(entreprise_id): @bp.route("/fiche_entreprise//activer", methods=["GET", "POST"]) -@permission_required(Permission.RelationsEntreprisesChange) +@permission_required(Permission.RelationsEntrepEdit) def fiche_entreprise_activer(entreprise_id): """ Permet d'activer une entreprise @@ -645,7 +645,7 @@ def fiche_entreprise_activer(entreprise_id): url_for("entreprises.fiche_entreprise", entreprise_id=entreprise.id) ) return render_template( - "entreprises/form_confirmation.j2", + "form_confirmation.j2", title="Activer entreprise", form=form, info_message="Cliquez sur le bouton Modifier pour confirmer l'activaction", @@ -774,7 +774,7 @@ def delete_taxe_apprentissage(entreprise_id, taxe_id): url_for("entreprises.fiche_entreprise", entreprise_id=taxe.entreprise_id) ) return render_template( - "entreprises/form_confirmation.j2", + "form_confirmation.j2", title="Supprimer taxe apprentissage", form=form, info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression", @@ -782,7 +782,7 @@ def delete_taxe_apprentissage(entreprise_id, taxe_id): @bp.route("/fiche_entreprise//add_offre", methods=["GET", "POST"]) -@permission_required(Permission.RelationsEntreprisesChange) +@permission_required(Permission.RelationsEntrepEdit) def add_offre(entreprise_id): """ Permet d'ajouter une offre a une entreprise @@ -854,7 +854,7 @@ def add_offre(entreprise_id): "/fiche_entreprise//edit_offre/", methods=["GET", "POST"], ) -@permission_required(Permission.RelationsEntreprisesChange) +@permission_required(Permission.RelationsEntrepEdit) def edit_offre(entreprise_id, offre_id): """ Permet de modifier une offre @@ -930,7 +930,7 @@ def edit_offre(entreprise_id, offre_id): "/fiche_entreprise//delete_offre/", methods=["GET", "POST"], ) -@permission_required(Permission.RelationsEntreprisesChange) +@permission_required(Permission.RelationsEntrepEdit) def delete_offre(entreprise_id, offre_id): """ Permet de supprimer une offre @@ -970,7 +970,7 @@ def delete_offre(entreprise_id, offre_id): url_for("entreprises.fiche_entreprise", entreprise_id=offre.entreprise_id) ) return render_template( - "entreprises/form_confirmation.j2", + "form_confirmation.j2", title="Supression offre", form=form, info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression", @@ -981,7 +981,7 @@ def delete_offre(entreprise_id, offre_id): "/fiche_entreprise//expired/", methods=["GET", "POST"], ) -@permission_required(Permission.RelationsEntreprisesChange) +@permission_required(Permission.RelationsEntrepEdit) def expired(entreprise_id, offre_id): """ Permet de rendre expirée et non expirée une offre @@ -1006,7 +1006,7 @@ def expired(entreprise_id, offre_id): "/fiche_entreprise//add_site", methods=["GET", "POST"], ) -@permission_required(Permission.RelationsEntreprisesChange) +@permission_required(Permission.RelationsEntrepEdit) def add_site(entreprise_id): """ Permet d'ajouter un site a une entreprise @@ -1107,7 +1107,7 @@ def edit_site(entreprise_id, site_id): "/fiche_entreprise//site//add_correspondant", methods=["GET", "POST"], ) -@permission_required(Permission.RelationsEntreprisesChange) +@permission_required(Permission.RelationsEntrepEdit) def add_correspondant(entreprise_id, site_id): """ Permet d'ajouter un correspondant a une entreprise @@ -1163,7 +1163,7 @@ def add_correspondant(entreprise_id, site_id): "/fiche_entreprise//site//edit_correspondant/", methods=["GET", "POST"], ) -@permission_required(Permission.RelationsEntreprisesChange) +@permission_required(Permission.RelationsEntrepEdit) def edit_correspondant(entreprise_id, site_id, correspondant_id): """ Permet de modifier un correspondant @@ -1243,7 +1243,7 @@ def edit_correspondant(entreprise_id, site_id, correspondant_id): "/fiche_entreprise//site//delete_correspondant/", methods=["GET", "POST"], ) -@permission_required(Permission.RelationsEntreprisesChange) +@permission_required(Permission.RelationsEntrepEdit) def delete_correspondant(entreprise_id, site_id, correspondant_id): """ Permet de supprimer un correspondant @@ -1289,7 +1289,7 @@ def delete_correspondant(entreprise_id, site_id, correspondant_id): ) ) return render_template( - "entreprises/form_confirmation.j2", + "form_confirmation.j2", title="Supression correspondant", form=form, info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression", @@ -1297,7 +1297,7 @@ def delete_correspondant(entreprise_id, site_id, correspondant_id): @bp.route("/fiche_entreprise//contacts") -@permission_required(Permission.RelationsEntreprisesView) +@permission_required(Permission.RelationsEntrepView) def contacts(entreprise_id): """ Permet d'afficher une page avec la liste des contacts d'une entreprise @@ -1318,7 +1318,7 @@ def contacts(entreprise_id): "/fiche_entreprise//contacts/add_contact", methods=["GET", "POST"], ) -@permission_required(Permission.RelationsEntreprisesChange) +@permission_required(Permission.RelationsEntrepEdit) def add_contact(entreprise_id): """ Permet d'ajouter un contact avec une entreprise @@ -1374,7 +1374,7 @@ def add_contact(entreprise_id): "/fiche_entreprise//contacts/edit_contact/", methods=["GET", "POST"], ) -@permission_required(Permission.RelationsEntreprisesChange) +@permission_required(Permission.RelationsEntrepEdit) def edit_contact(entreprise_id, contact_id): """ Permet d'editer un contact avec une entreprise @@ -1430,7 +1430,7 @@ def edit_contact(entreprise_id, contact_id): "/fiche_entreprise//contacts/delete_contact/", methods=["GET", "POST"], ) -@permission_required(Permission.RelationsEntreprisesChange) +@permission_required(Permission.RelationsEntrepEdit) def delete_contact(entreprise_id, contact_id): """ Permet de supprimer un contact @@ -1458,7 +1458,7 @@ def delete_contact(entreprise_id, contact_id): url_for("entreprises.contacts", entreprise_id=contact.entreprise) ) return render_template( - "entreprises/form_confirmation.j2", + "form_confirmation.j2", title="Supression contact", form=form, info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression", @@ -1469,7 +1469,7 @@ def delete_contact(entreprise_id, contact_id): "/fiche_entreprise//add_stage_apprentissage", methods=["GET", "POST"], ) -@permission_required(Permission.RelationsEntreprisesChange) +@permission_required(Permission.RelationsEntrepEdit) def add_stage_apprentissage(entreprise_id): """ Permet d'ajouter un étudiant ayant réalisé un stage ou alternance @@ -1528,7 +1528,7 @@ def add_stage_apprentissage(entreprise_id): "/fiche_entreprise//edit_stage_apprentissage/", methods=["GET", "POST"], ) -@permission_required(Permission.RelationsEntreprisesChange) +@permission_required(Permission.RelationsEntrepEdit) def edit_stage_apprentissage(entreprise_id, stage_apprentissage_id): """ Permet de modifier un étudiant ayant réalisé un stage ou alternance sur la fiche de l'entreprise @@ -1598,7 +1598,7 @@ def edit_stage_apprentissage(entreprise_id, stage_apprentissage_id): "/fiche_entreprise//delete_stage_apprentissage/", methods=["GET", "POST"], ) -@permission_required(Permission.RelationsEntreprisesChange) +@permission_required(Permission.RelationsEntrepEdit) def delete_stage_apprentissage(entreprise_id, stage_apprentissage_id): """ Permet de supprimer un étudiant ayant réalisé un stage ou une alternance sur la fiche entreprise de l'entreprise @@ -1629,7 +1629,7 @@ def delete_stage_apprentissage(entreprise_id, stage_apprentissage_id): ) ) return render_template( - "entreprises/form_confirmation.j2", + "form_confirmation.j2", title="Supression stage/apprentissage", form=form, info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression", @@ -1640,7 +1640,7 @@ def delete_stage_apprentissage(entreprise_id, stage_apprentissage_id): "/fiche_entreprise//envoyer_offre/", methods=["GET", "POST"], ) -@permission_required(Permission.RelationsEntreprisesSend) +@permission_required(Permission.RelationsEntrepSend) def envoyer_offre(entreprise_id, offre_id): """ Permet d'envoyer une offre à un utilisateur ScoDoc @@ -1686,7 +1686,7 @@ def envoyer_offre(entreprise_id, offre_id): @bp.route("/etudiants") -@permission_required(Permission.RelationsEntreprisesChange) +@permission_required(Permission.RelationsEntrepEdit) @as_json def json_etudiants(): """ @@ -1717,7 +1717,7 @@ def json_etudiants(): @bp.route("/responsables") -@permission_required(Permission.RelationsEntreprisesChange) +@permission_required(Permission.RelationsEntrepEdit) def json_responsables(): """ Permet de récuperer un JSON avec tous les utilisateurs ScoDoc @@ -1743,7 +1743,7 @@ def json_responsables(): @bp.route("/export_donnees") -@permission_required(Permission.RelationsEntreprisesExport) +@permission_required(Permission.RelationsEntrepExport) def export_donnees(): """ Permet d'exporter la liste des entreprises sous format excel (.xlsx) @@ -1759,7 +1759,7 @@ def export_donnees(): @bp.route("/import_donnees/get_file_sample") -@permission_required(Permission.RelationsEntreprisesExport) +@permission_required(Permission.RelationsEntrepExport) def import_donnees_get_file_sample(): """ Permet de récupérer un fichier exemple vide pour pouvoir importer des entreprises @@ -1771,7 +1771,7 @@ def import_donnees_get_file_sample(): @bp.route("/import_donnees", methods=["GET", "POST"]) -@permission_required(Permission.RelationsEntreprisesExport) +@permission_required(Permission.RelationsEntrepExport) def import_donnees(): """ Permet d'importer des entreprises à partir d'un fichier excel (.xlsx) @@ -1850,7 +1850,7 @@ def import_donnees(): @bp.route( "/fiche_entreprise//offre//get_offre_file//" ) -@permission_required(Permission.RelationsEntreprisesView) +@permission_required(Permission.RelationsEntrepView) def get_offre_file(entreprise_id, offre_id, filedir, filename): """ Permet de télécharger un fichier d'une offre @@ -1884,7 +1884,7 @@ def get_offre_file(entreprise_id, offre_id, filedir, filename): "/fiche_entreprise//offre//add_offre_file", methods=["GET", "POST"], ) -@permission_required(Permission.RelationsEntreprisesChange) +@permission_required(Permission.RelationsEntrepEdit) def add_offre_file(entreprise_id, offre_id): """ Permet d'ajouter un fichier à une offre @@ -1927,7 +1927,7 @@ def add_offre_file(entreprise_id, offre_id): "/fiche_entreprise//offre//delete_offre_file/", methods=["GET", "POST"], ) -@permission_required(Permission.RelationsEntreprisesChange) +@permission_required(Permission.RelationsEntrepEdit) def delete_offre_file(entreprise_id, offre_id, filedir): """ Permet de supprimer un fichier d'une offre @@ -1959,7 +1959,7 @@ def delete_offre_file(entreprise_id, offre_id, filedir): ) ) return render_template( - "entreprises/form_confirmation.j2", + "form_confirmation.j2", title="Suppression fichier d'une offre", form=form, info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression", diff --git a/app/forms/generic.py b/app/forms/generic.py new file mode 100644 index 00000000..ec301f8d --- /dev/null +++ b/app/forms/generic.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +"""Formulaires génériques +""" + +from flask_wtf import FlaskForm +from wtforms import ( + SubmitField, +) + +SUBMIT_MARGE = {"style": "margin-bottom: 10px;"} + + +class SimpleConfirmationForm(FlaskForm): + "bête dialogue de confirmation" + submit = SubmitField("OK", render_kw=SUBMIT_MARGE) + cancel = SubmitField("Annuler", render_kw=SUBMIT_MARGE) diff --git a/app/forms/main/role_create.py b/app/forms/main/role_create.py new file mode 100644 index 00000000..c3a2b150 --- /dev/null +++ b/app/forms/main/role_create.py @@ -0,0 +1,58 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +############################################################################## +# +# ScoDoc +# +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Emmanuel Viennet emmanuel.viennet@viennet.net +# +############################################################################## + +""" +Formulaires création département +""" + +from flask_wtf import FlaskForm +from wtforms import SubmitField, validators +from wtforms.fields.simple import StringField, BooleanField + +from app.models import SHORT_STR_LEN + + +class CreateRoleForm(FlaskForm): + """Formulaire création rôle""" + + name = StringField( + label="Nom du rôle", + validators=[ + validators.regexp( + r"^[a-zA-Z0-9]*$", + message="Ne doit comporter que lettres et des chiffres", + ), + validators.Length( + max=SHORT_STR_LEN, + message=f"Le nom ne doit pas dépasser {SHORT_STR_LEN} caractères", + ), + validators.DataRequired("vous devez spécifier le nom du rôle"), + ], + ) + + submit = SubmitField("Créer ce rôle") + cancel = SubmitField("Annuler", render_kw={"formnovalidate": True}) diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py index a5b20b12..f4a4ea9c 100644 --- a/app/models/formsemestre.py +++ b/app/models/formsemestre.py @@ -424,7 +424,7 @@ class FormSemestre(db.Model): def can_be_edited_by(self, user): """Vrai si user peut modifier ce semestre (est chef ou l'un des responsables)""" - if not user.has_permission(Permission.ScoImplement): # pas chef + if not user.has_permission(Permission.EditFormSemestre): # pas chef if not self.resp_can_edit or user.id not in [ resp.id for resp in self.responsables ]: @@ -607,7 +607,7 @@ class FormSemestre(db.Model): def est_chef_or_diretud(self, user: User = None): "Vrai si utilisateur (par def. current) est admin, chef dept ou responsable du semestre" user = user or current_user - return user.has_permission(Permission.ScoImplement) or self.est_responsable( + return user.has_permission(Permission.EditFormSemestre) or self.est_responsable( user ) @@ -618,7 +618,7 @@ class FormSemestre(db.Model): if not self.etat: return False # semestre verrouillé user = user or current_user - if user.has_permission(Permission.ScoEtudChangeGroups): + if user.has_permission(Permission.EtudChangeGroups): return True # typiquement admin, chef dept return self.est_responsable(user) @@ -632,9 +632,9 @@ class FormSemestre(db.Model): def can_edit_pv(self, user: User = None): "Vrai si utilisateur (par def. current) peut editer un PV de jury de ce semestre" user = user or current_user - # Autorise les secrétariats, repérés via la permission ScoEtudChangeAdr + # Autorise les secrétariats, repérés via la permission EtudChangeAdr return self.est_chef_or_diretud(user) or user.has_permission( - Permission.ScoEtudChangeAdr + Permission.EtudChangeAdr ) def annee_scolaire(self) -> int: diff --git a/app/models/moduleimpls.py b/app/models/moduleimpls.py index 27df7f48..3ccff4cf 100644 --- a/app/models/moduleimpls.py +++ b/app/models/moduleimpls.py @@ -107,7 +107,7 @@ class ModuleImpl(db.Model): """ # acces pour resp. moduleimpl et resp. form semestre (dir etud) if ( - user.has_permission(Permission.ScoEditAllEvals) + user.has_permission(Permission.EditAllEvals) or user.id == self.responsable_id or user.id in (r.id for r in self.formsemestre.responsables) ): @@ -131,7 +131,7 @@ class ModuleImpl(db.Model): if not self.formsemestre.etat: return False # semestre verrouillé is_dir_etud = user.id in (u.id for u in self.formsemestre.responsables) - can_edit_all_notes = user.has_permission(Permission.ScoEditAllNotes) + can_edit_all_notes = user.has_permission(Permission.EditAllNotes) if sco_cursus_dut.formsemestre_has_decisions(self.formsemestre_id): # il y a des décisions de jury dans ce semestre ! return can_edit_all_notes or is_dir_etud @@ -155,7 +155,7 @@ class ModuleImpl(db.Model): return False # -- check access # admin ou resp. semestre avec flag resp_can_change_resp - if user.has_permission(Permission.ScoImplement): + if user.has_permission(Permission.EditFormSemestre): return True if ( user.id in [resp.id for resp in self.formsemestre.responsables] diff --git a/app/scodoc/html_sidebar.py b/app/scodoc/html_sidebar.py index 1f8ede5d..0613ec02 100755 --- a/app/scodoc/html_sidebar.py +++ b/app/scodoc/html_sidebar.py @@ -56,18 +56,18 @@ def sidebar_common(): Programmes
""" ] - if current_user.has_permission(Permission.ScoAbsChange): + if current_user.has_permission(Permission.AbsChange): H.append( f""" Assiduité
""" ) if current_user.has_permission( - Permission.ScoUsersAdmin - ) or current_user.has_permission(Permission.ScoUsersView): + Permission.UsersAdmin + ) or current_user.has_permission(Permission.UsersView): H.append( f"""Utilisateurs
""" ) - if current_user.has_permission(Permission.ScoChangePreferences): + if current_user.has_permission(Permission.EditPreferences): H.append( f"""Paramétrage
""" @@ -126,7 +126,7 @@ def sidebar(etudid: int = None):
{ nbabsjust } J., { nbabsnj } N.J.""" ) H.append("