diff --git a/backend/gn_module_monitoring/command/cmd.py b/backend/gn_module_monitoring/command/cmd.py index cd1ed30d5..a6469300b 100644 --- a/backend/gn_module_monitoring/command/cmd.py +++ b/backend/gn_module_monitoring/command/cmd.py @@ -19,7 +19,7 @@ from .utils import ( process_export_csv, - insert_permission_object, + process_available_permissions, remove_monitoring_module, add_nomenclature, available_modules, @@ -134,10 +134,8 @@ def cmd_install_monitoring_module(module_code): DB.session.add(module) DB.session.commit() - # Insert permission object - if config["module"].get("permission_objects"): - id_module = module.id_module - insert_permission_object(id_module, config["module"].get("permission_objects")) + # Ajouter les permissions disponibles + process_available_permissions(module_code) #  run specific sql if (module_config_dir_path / "synthese.sql").exists: @@ -181,10 +179,10 @@ def cmd_install_monitoring_module(module_code): return -@click.command("update_permission_objects") -@click.argument("module_code") +@click.command("update_module_available_permissions") +@click.argument("module_code", required=False, default="") @with_appcontext -def cmd_update_perm_module_cmd(module_code): +def cmd_process_available_permission_module(module_code): """ Mise à jour (uniquement insertion) des objets permissions associés au module Défini par le paramètre permission_objects du fichier module.json @@ -193,28 +191,13 @@ def cmd_update_perm_module_cmd(module_code): module_code ([string]): code du sous module """ - try: - module = get_module("module_code", module_code) - except Exception: - print("le module n'existe pas") - return - path_module = monitoring_module_config_path(module_code) / "module.json" - if not path_module.is_file(): - print(f"Il n'y a pas de fichier {path_module} pour ce module") - return - config_module = json_from_file(path_module, None) - if not config_module: - print("Il y a un problème avec le fichier {}".format(path_module)) - return + if module_code: + return process_available_permissions(module_code) + + for module in installed_modules(): + process_available_permissions(module['module_code']) - print(f"Insertion des objets de permissions pour le module {module_code}") - # Insert permission object - if "permission_objects" in config_module: - id_module = module.id_module - insert_permission_object(id_module, config_module["permission_objects"]) - else: - print("no permission") @click.command("remove") @@ -264,7 +247,7 @@ def synchronize_synthese(module_code, offset): commands = [ cmd_process_export_csv, cmd_install_monitoring_module, - cmd_update_perm_module_cmd, + cmd_process_available_permission_module, cmd_remove_monitoring_module_cmd, cmd_add_module_nomenclature_cli, cmd_process_all, diff --git a/backend/gn_module_monitoring/command/utils.py b/backend/gn_module_monitoring/command/utils.py index bbc64cac8..b1cb27b62 100644 --- a/backend/gn_module_monitoring/command/utils.py +++ b/backend/gn_module_monitoring/command/utils.py @@ -5,7 +5,8 @@ from sqlalchemy.orm.exc import NoResultFound from geonature.utils.env import DB, BACKEND_DIR -from geonature.core.gn_permissions.models import TObjects +from geonature.core.gn_permissions.models import PermObject +from geonature.core.gn_commons.models import TModules from pypnnomenclature.models import TNomenclatures, BibNomenclaturesTypes @@ -15,8 +16,9 @@ SUB_MODULE_CONFIG_DIR ) -from ..modules.repositories import get_module, get_source_by_code, get_modules +from ..config.repositories import get_config +from ..modules.repositories import get_module, get_source_by_code, get_modules ''' utils.py @@ -34,7 +36,8 @@ def process_for_all_module(process_func): process_func(module.module_code) return - +def getMonitoringPermissionObjectLabel_dict(): + return __import__('gn_module_monitoring').monitoring.definitions.MonitoringPermissionObjectLabel_dict def process_export_csv(module_code=None): ''' @@ -71,41 +74,80 @@ def process_export_csv(module_code=None): print('{} - export csv erreur dans le script {} : {}'.format(module_code, f ,e)) -def insert_permission_object(id_module, permissions): - """ Insertion de l'association permission object +def process_available_permissions(module_code): + try: + module = get_module("module_code", module_code) + except Exception: + print("le module n'existe pas") + return - Args: - id_module ([type]): id du module - permissions ([type]): liste des permissions à associer au module + config = get_config(module_code, force=True) + if not config: + print(f"Il y a un problème de configuration pour le module {module_code}") + return - Raises: - e: [description] - """ - for perm in permissions: - print(f"Insert perm {perm}") - # load object - try: - object = DB.session.query(TObjects).filter(TObjects.code_object == perm).one() - # save - txt = (""" - INSERT INTO gn_permissions.cor_object_module (id_module, id_object) - VALUES ({id_module}, {id_object}) - """.format( - id_module=id_module, - id_object=object.id_object - ) - ) - try: - DB.engine.execution_options(autocommit=True).execute(txt) - print(f" Ok") - except IntegrityError: - DB.session.rollback() - print(f" Impossible d'insérer la permission {perm} pour des raisons d'intégrités") - except NoResultFound as e: - print(f" Permission {perm} does'nt exists") - except Exception as e: - print(f" Impossible d'insérer la permission {perm} :{e}") + insert_module_available_permissions(module_code, "ALL") + + # Insert permission object + for permission_object_code in config.get("permission_objects", []): + insert_module_available_permissions(module_code, permission_object_code) + + +def insert_module_available_permissions(module_code, perm_object_code): + print(f'process permissions for (module_code, perm_object)= ({module_code},{perm_object_code})') + + object_label = getMonitoringPermissionObjectLabel_dict().get(perm_object_code) + if not object_label: + print(f"L'object {perm_object_code} n'est pas traité") + + try: + module = TModules.query.filter_by(module_code=module_code).one() + except NoResultFound: + print("Le module {module_code} n'est pas présent") + return + + try: + perm_object = PermObject.query.filter_by(code_object=perm_object_code).one() + except NoResultFound: + print("L'object de permission {module_code} n'est pas présent") + return + + txt_cor_object_module = f""" + INSERT INTO gn_permissions.cor_object_module( + id_module, + id_object + ) + VALUES({module.id_module}, {perm_object.id_object}) + ON CONFLICT DO NOTHING + """ + DB.engine.execution_options(autocommit=True).execute(txt_cor_object_module) + + txt_perm_available = (f""" + INSERT INTO gn_permissions.t_permissions_available ( + id_module, + id_object, + id_action, + label, + scope_filter) + SELECT + {module.id_module}, + {perm_object.id_object}, + a.id_action, + v.label, + true + FROM + ( VALUES + ('C', 'Créer des {object_label}'), + ('R', 'Voir les {object_label}'), + ('U', 'Modifier les {object_label}'), + ('D', 'Supprimer des {object_label}'), + ('E', 'Exporter les {object_label}') + ) AS v (action_code, label) + JOIN gn_permissions.bib_actions a ON v.action_code = a.code_action + ON CONFLICT DO NOTHING + """) + DB.engine.execution_options(autocommit=True).execute(txt_perm_available) def remove_monitoring_module(module_code): try: @@ -116,20 +158,18 @@ def remove_monitoring_module(module_code): # remove module in db try: - # HACK pour le moment suprresion avec un sql direct - # Car il y a un soucis de delete cascade dans les modèles sqlalchemy - txt = (""" - DELETE FROM gn_commons.t_modules WHERE id_module ={} - """.format( - module.id_module - ) - ) + # suppression des permissions disponibles pour ce module + txt = f"DELETE FROM gn_permissions.t_permissions_available WHERE id_module = {module.id_module}" + DB.engine.execution_options(autocommit=True).execute(txt) + # HACK pour le moment suppresion avec un sql direct + # Car il y a un soucis de delete cascade dans les modèles sqlalchemy + txt = f"""DELETE FROM gn_commons.t_modules WHERE id_module ={module.id_module}""" DB.engine.execution_options(autocommit=True).execute(txt) except IntegrityError: print("Impossible de supprimer le module car il y a des données associées") return - except Exception: + except Exception as e: print("Impossible de supprimer le module") raise(e) @@ -141,7 +181,7 @@ def remove_monitoring_module(module_code): DB.session.commit() except Exception as e: print("Impossible de supprimer la source {}".format(str(e))) - return + # return # run specific sql TODO # remove nomenclature TODO return @@ -262,4 +302,4 @@ def available_modules(): module = json_from_file(module_file) available_modules_.append({**module, 'module_code': d}) break - return available_modules_ \ No newline at end of file + return available_modules_ diff --git a/backend/gn_module_monitoring/migrations/fc90d31c677f_declare_available_permissions.py b/backend/gn_module_monitoring/migrations/fc90d31c677f_declare_available_permissions.py index 144768ea6..fa77b931d 100644 --- a/backend/gn_module_monitoring/migrations/fc90d31c677f_declare_available_permissions.py +++ b/backend/gn_module_monitoring/migrations/fc90d31c677f_declare_available_permissions.py @@ -36,7 +36,7 @@ def upgrade(): FROM ( VALUES - ('MONITORINGS', 'ALL', 'R', False, 'Accéder à monitoring') + ('MONITORINGS', 'ALL', 'R', False, 'Accéder au module') ) AS v (module_code, object_code, action_code, scope_filter, label) JOIN gn_commons.t_modules m ON m.module_code = v.module_code @@ -76,6 +76,15 @@ def upgrade(): """ ) + op.execute(""" + INSERT INTO gn_permissions.t_objects (code_object, description_object) + VALUES + ('GNM_MODULES', 'Permissions sur les modules') + ON CONFLICT DO NOTHING + ; + """ +) + def downgrade(): op.execute( diff --git a/backend/gn_module_monitoring/monitoring/definitions.py b/backend/gn_module_monitoring/monitoring/definitions.py index f61ceb538..ca759cd4e 100644 --- a/backend/gn_module_monitoring/monitoring/definitions.py +++ b/backend/gn_module_monitoring/monitoring/definitions.py @@ -44,7 +44,18 @@ 'site': "GNM_SITES", 'sites_group': "GNM_GRP_SITES", 'visite': "GNM_VISITES", - 'observation': "GNM_OBSERVATIONS" + 'observation': "GNM_OBSERVATIONS", + 'module': "GNM_MODULES" } +MonitoringPermissionObjectLabel_dict = { + 'ALL': 'objects (sites, visites, observations, etc...)', + 'GNM_SITES': 'sites', + 'GNM_OBSERVATIONS': 'observations', + 'GNM_VISITES': 'visites', + 'GNM_GRP_SITES': 'groupes de sites', + 'GNM_MODULES': 'modules' + } + + monitoring_definitions.set(MonitoringObjects_dict, MonitoringModels_dict) diff --git a/backend/gn_module_monitoring/monitoring/repositories.py b/backend/gn_module_monitoring/monitoring/repositories.py index 8c14ee719..e2b051285 100644 --- a/backend/gn_module_monitoring/monitoring/repositories.py +++ b/backend/gn_module_monitoring/monitoring/repositories.py @@ -50,7 +50,7 @@ def get(self, value=None, field_name=None, depth=0): return self except Exception as e: - raise GeoNatureError('MONITORING : get_object : {}'.format(e)) + raise GeoNatureError(f'MONITORING : get_object {self._module_code} {self._object_type} ({field_name}={value}) : {e}') def process_post_data_properties(self, post_data): diff --git a/backend/gn_module_monitoring/monitoring/serializer.py b/backend/gn_module_monitoring/monitoring/serializer.py index 0d888e969..0aef6447d 100644 --- a/backend/gn_module_monitoring/monitoring/serializer.py +++ b/backend/gn_module_monitoring/monitoring/serializer.py @@ -156,15 +156,6 @@ def serialize(self, depth=1): 'id': self._id, } - if self._object_type == 'module': - monitoring_object_dict['cruved'] = get_scopes_by_action(module_code=self._module_code) - monitoring_object_dict['cruved_objects'] = {} - monitoring_object_dict['cruved_objects']['site'] = get_scopes_by_action(module_code=self._module_code, object_code="GNM_SITES") - monitoring_object_dict['cruved_objects']['sites_group'] = get_scopes_by_action(module_code=self._module_code, object_code="GNM_GRP_SITES") - monitoring_object_dict['cruved_objects']['visite'] = get_scopes_by_action(module_code=self._module_code, object_code="GNM_VISITES") - monitoring_object_dict['cruved_objects']['observation'] = get_scopes_by_action(module_code=self._module_code, object_code="GNM_OBSERVATIONS") - - properties['id_parent'] = to_int(self.id_parent()) if(children): monitoring_object_dict['children'] = children diff --git a/backend/gn_module_monitoring/routes/monitoring.py b/backend/gn_module_monitoring/routes/monitoring.py index 6432d9567..394014693 100644 --- a/backend/gn_module_monitoring/routes/monitoring.py +++ b/backend/gn_module_monitoring/routes/monitoring.py @@ -4,50 +4,75 @@ ''' +from pathlib import Path +from werkzeug.exceptions import NotFound from flask import request, send_from_directory, url_for, g, current_app +import datetime as dt + +from sqlalchemy.orm import joinedload + from utils_flask_sqla.response import ( json_resp, json_resp_accept_empty_list ) +from utils_flask_sqla.response import to_csv_resp, to_json_resp +from utils_flask_sqla_geo.generic import GenericTableGeo +from utils_flask_sqla.generic import serializeQuery -from werkzeug.exceptions import NotFound from ..blueprint import blueprint from geonature.core.gn_permissions.decorators import check_cruved_scope from geonature.core.gn_commons.models.base import TModules -from geonature.core.gn_permissions.models import TObjects +from geonature.core.gn_permissions.models import TObjects, Permission + +from geonature.utils.env import DB, ROOT_DIR +import geonature.utils.filemanager as fm -# from geonature.utils.errors import GeoNatureError +from gn_module_monitoring import MODULE_CODE from ..monitoring.definitions import monitoring_definitions, MonitoringPermissions_dict from ..modules.repositories import get_module from ..utils.utils import to_int from ..config.repositories import get_config -from gn_module_monitoring import MODULE_CODE -from utils_flask_sqla_geo.generic import GenericTableGeo -from geonature.utils.env import DB, ROOT_DIR -import datetime as dt -from utils_flask_sqla.response import to_csv_resp, to_json_resp -from utils_flask_sqla.generic import serializeQuery -import geonature.utils.filemanager as fm -from pathlib import Path @blueprint.url_value_preprocessor def set_current_module(endpoint, values): - requested_module = values.get("module_code") or MODULE_CODE - g.current_module = TModules.query.filter_by(module_code=requested_module).first_or_404( - f"No module with code {requested_module} {endpoint}" + # recherche du sous-module courrant + requested_module_code = values.get("module_code") or MODULE_CODE + current_module = ( + TModules.query + .options(joinedload(TModules.objects)) + .filter_by(module_code=requested_module_code) + .first_or_404(f"No module with code {requested_module_code} {endpoint}") ) + g.current_module = current_module + # recherche de l'object de permission courrant object_type = values.get("object_type") if object_type: - request_code_object = MonitoringPermissions_dict.get(object_type, 'ALL') - g.current_object = TObjects.query.filter_by(code_object=request_code_object).first_or_404( - f"No permission object with code {request_code_object} {endpoint}" + + requested_permission_object_code = MonitoringPermissions_dict.get(object_type) + + if requested_permission_object_code is None: + # error ? + return + + # Test si l'object de permission existe + requested_permission_object = ( + TObjects.query + .filter_by(code_object=requested_permission_object_code) + .first_or_404(f"No permission object with code {requested_permission_object_code} {endpoint}") ) + # si l'object de permission est associé au module => il devient l'objet courant + # - sinon se sera 'ALL' par defaut + for module_perm_object in current_module.objects: + if module_perm_object == requested_permission_object: + g.current_object = requested_permission_object + return + @blueprint.route('/object///', methods=['GET']) @blueprint.route( '/object//', diff --git a/docs/changelog.md b/docs/changelog.md index ee29f941a..6d2c2b209 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,27 @@ CHANGELOG ========= +0.7.0 (2023-05-23) +------------------ + +Nécessite GeoNature version 2.13.0 (ou plus) + +**Evolutions** + +* Gestion de l'évolution des permissions et création des permissions disponible à l'installation des modules. +* Récupération des permissions depuis le service `ModulesService` de GeoNature. + +**⚠️ Notes de version** + +* La variable `permission_objects` est déplacée du fichier `module.json` au fichier `config.json` + +* Utiliser la commande pour générer les permissions disponible pour les sous-modules déjà installés + +``` +geonature monitorings update_module_available_permissions +``` + + 0.6.0 (2023-05-23) ------------------ diff --git a/docs/sous_module.md b/docs/sous_module.md index 69eaff4be..f16ebd513 100644 --- a/docs/sous_module.md +++ b/docs/sous_module.md @@ -82,6 +82,15 @@ Dans le fichier `config.json` : } ``` +* `permission_objects`: liste des objets permissions à associer au + module. Elle peut contenir les valeurs suivantes + `["GNM_MODULES", "GNM_GRP_SITES", "GNM_SITES", "GNM_VISITES", "GNM_OBSERVATIONS"]` + + * Par exemple, pour les sites, par défaut l'api va vérifier les droits pour l'objet de permission `ALL` associé au sous-module. + + * Si des permission sont définie pour ce module et l'object `GNM_SITES`, l'api des sites ira vérifier les droits en rapport avec `GNM_SITES`. + + # Configuration des objets Dans le fichier `module.json`, deux variables doivent obligatoirement @@ -94,10 +103,6 @@ Dans le fichier `module.json`, deux variables doivent obligatoirement Une variable optionnelle permet de configurer les objets faisant l'objet de permission: -* `permission_objects`: liste des objets permissions à associer au - module. Elle peut contenir les valeurs suivantes - `["GNM_GRP_SITES", "GNM_SITES", "GNM_VISITES", "GNM_OBSERVATIONS"]` - Dans le cas général (`module.json`, `site.json`, `visit.json`, `observation.json`) on peut redéfinir au besoin certaines variables. diff --git a/frontend/app/components/monitoring-form/monitoring-form.component.html b/frontend/app/components/monitoring-form/monitoring-form.component.html index b69e435e6..9987e3068 100644 --- a/frontend/app/components/monitoring-form/monitoring-form.component.html +++ b/frontend/app/components/monitoring-form/monitoring-form.component.html @@ -126,7 +126,7 @@

Attention

color="warn" class="float-left" (click)="bDeleteModal = true" - *ngIf="obj.id && (currentUser['cruved_object'][obj.objectType] || currentUser['cruved']).D >= obj.cruved('D')" + *ngIf="obj.id && currentUser.moduleCruved[obj.objectType].D >= obj.cruved('D')" > Supprimer diff --git a/frontend/app/components/monitoring-lists/monitoring-lists.component.html b/frontend/app/components/monitoring-lists/monitoring-lists.component.html index 55acb6f81..5f6c0839e 100644 --- a/frontend/app/components/monitoring-lists/monitoring-lists.component.html +++ b/frontend/app/components/monitoring-lists/monitoring-lists.component.html @@ -16,7 +16,7 @@ class="ml-2 mb-2" (click)="child0.config['display_filter'] = !child0.config['display_filter']" matTooltip="{{child0.config['display_filter'] ? 'Cacher' : 'Afficher'}} les filtres" - *ngIf="obj.moduleCode && (currentUser['cruved_object'][child0.objectType] || currentUser['cruved']).R >= child0.cruved('R')" + *ngIf="obj.moduleCode && currentUser.moduleCruved[obj.objectType].R >= child0.cruved('R')" > filter_alt Ajouter {{ (child0.template["label_art_undef_new"] || "") }} diff --git a/frontend/app/components/monitoring-object/monitoring-object.component.ts b/frontend/app/components/monitoring-object/monitoring-object.component.ts index e5264e669..108649986 100644 --- a/frontend/app/components/monitoring-object/monitoring-object.component.ts +++ b/frontend/app/components/monitoring-object/monitoring-object.component.ts @@ -56,7 +56,7 @@ export class MonitoringObjectComponent implements OnInit { const container = document.getElementById("object"); const height = this._commonService.calcCardContentHeight(); container.style.height = height - 40 + "px"; - this.heightMap = height - 80 + "px"; + setTimeout(() => {this.heightMap = height - 80 + "px";}); } ngOnInit() { @@ -66,11 +66,6 @@ export class MonitoringObjectComponent implements OnInit { if (elements.length >= 1) { elements[0].remove(); } - this.currentUser = this._auth.getCurrentUser(); - - this.currentUser["cruved"] = {}; - this.currentUser["cruved_objects"] = {}; - of(true) .pipe( mergeMap(() => { @@ -81,6 +76,7 @@ export class MonitoringObjectComponent implements OnInit { }), mergeMap(() => { + this.initCurrentUser(); return this.initData(); // recupérations des données Nomenclature, Taxonomie, Utilisateur.. et mise en cache }), @@ -110,6 +106,11 @@ export class MonitoringObjectComponent implements OnInit { }); } + initCurrentUser() { + this.currentUser = this._auth.getCurrentUser(); + this.currentUser['moduleCruved'] = this._configService.moduleCruved(this.obj.moduleCode); + } + getModuleSet() { // Verifie si le module est configué this.module.get(0).subscribe(() => { @@ -132,8 +133,6 @@ export class MonitoringObjectComponent implements OnInit { initSites() { return this.module.get(1).subscribe(() => { // TODO liste indépendantes carte et listes - this.currentUser["cruved"] = this.module.userCruved; - this.currentUser["cruved_object"] = this.module.userCruvedObject; // affichage des groupes de site uniquement si l'objet est un module if ( diff --git a/frontend/app/components/monitoring-properties/monitoring-properties.component.html b/frontend/app/components/monitoring-properties/monitoring-properties.component.html index 90891d5dc..9189186c8 100644 --- a/frontend/app/components/monitoring-properties/monitoring-properties.component.html +++ b/frontend/app/components/monitoring-properties/monitoring-properties.component.html @@ -68,7 +68,6 @@ attr.aria-labelledby="nav-medias-tab" *ngIf="obj.properties['medias'] && obj.properties['medias'].length" > - @@ -79,7 +78,7 @@ (click)="onEditClick()" *ngIf=" !bEdit && - (currentUser['cruved_object'][obj.objectType] || currentUser['cruved']).U >= obj.cruved('U') + currentUser?.moduleCruved[obj.objectType].U >= obj.cruved('U') " > Éditer {{ (obj.template['label_art_def'] || "") }} @@ -106,7 +105,7 @@ *ngIf=" !bEdit && obj.objectType === 'module' && - currentUser?.cruved.U >= 3 && + currentUser?.moduleCruved.module.U >= 3 && obj.properties['b_synthese'] " matTooltip="Attention, cette opération peux prendre du temps" diff --git a/frontend/app/services/config.service.ts b/frontend/app/services/config.service.ts index 85e9bd528..7acc5e04e 100644 --- a/frontend/app/services/config.service.ts +++ b/frontend/app/services/config.service.ts @@ -5,6 +5,7 @@ import { of } from 'rxjs'; import { mergeMap } from 'rxjs/operators'; import { ConfigService as GnConfigService } from '@geonature/services/config.service'; + @Injectable() export class ConfigService { private _config; @@ -66,6 +67,29 @@ export class ConfigService { return this._moduleService.currentModule.module_path; } + moduleCruved(module_code) { + const permObjectDict = { + site: "GNM_SITES", + sites_group: "GNM_GRP_SITES", + visite: "GNM_VISITES", + observation: "GNM_OBSERVATIONS", + module: "GNM_MODULES", + } + + const module = this._moduleService.getModule(module_code) + const moduleCruved ={} + + + for (const [objectCode, permObjectCode] of Object.entries(permObjectDict) ) { + moduleCruved[objectCode] = ( + module.objects.find(o => o.code_object == permObjectDict[objectCode]) + || module.cruved + ) + } + + return moduleCruved + } + moduleMonitoringCode() { return this.appConfig.MONITORINGS.MODULE_CODE; }