Skip to content

Commit

Permalink
Merge pull request #250 from PnX-SI/feat/permissions
Browse files Browse the repository at this point in the history
Creation des permissions disponibles à partir de l'objet tree
  • Loading branch information
amandine-sahl authored Oct 5, 2023
2 parents 6a52b48 + e84ae37 commit 0c67a59
Show file tree
Hide file tree
Showing 24 changed files with 384 additions and 159 deletions.
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,15 @@ Les permissions ne sont implémentées que partiellement. La notion de portée (

La gestion des permissions pour les rôles (utilisateur ou groupe) se réalise au niveau de l'interface d'administration des permissions de GeoNature.

Il est possible de spécifier les permissions pour chaque type d'objet (groupes de sites, sites, visites et observations).
Les permissions sont définis pour chaque type d'objet (modules, groupes de sites, sites, visites et observations) :
* MONITORINGS_MODULES - R : permet a l'utilisateur d'accéder au module, de le voir dans la liste des modules
* MONITORINGS_MODULES - U : action administrateur qui permet de configurer le module et de synchroniser la synthèse
* MONITORINGS_MODULES - E : action qui permet aux utilisateurs d'exporter les données (si défini par le module)
* MONITORINGS_GRP_SITES - CRUD : action de lire, créer, modifier, supprimer un groupe de site
* MONITORINGS_SITES - CRUD : action de lire, créer, modifier, supprimer un site
* MONITORINGS_VISITES - CRUD : action de lire, créer, modifier, supprimer les visites, observations, observations détails

Si aucune permission n'est associée à l'objet, les permissions auront comme valeur celles associées au sous-module.
Par défaut, dès qu'un utilisateur a un droit supérieur à 0 pour une action (c-a-d aucune portée) il peut réaliser cette action.

Par défaut, dès qu'un utilisateur a un droit supérieur à 0 pour une action (c-a-d aucune portée) il peut réaliser cette action. Il est possible de surcharger les paramètres au niveau des fichiers de configuration des objets du module. (cf doc de configuration des sous-modules).

Il est possible de mettre à jour les permissions disponibles pour un module en utilisant la commande `update_module_available_permissions`
98 changes: 58 additions & 40 deletions backend/gn_module_monitoring/command/utils.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import os
from pathlib import Path

from flask import current_app
from sqlalchemy import and_, text
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm.exc import NoResultFound

from geonature.utils.env import DB, BACKEND_DIR
from geonature.core.gn_permissions.models import PermObject
from geonature.core.gn_permissions.models import PermObject, PermissionAvailable, PermAction
from geonature.core.gn_commons.models import TModules

from pypnnomenclature.models import TNomenclatures, BibNomenclaturesTypes
Expand All @@ -16,13 +18,30 @@

from ..modules.repositories import get_module, get_source_by_code, get_modules


"""
utils.py
fonctions pour les commandes du module monitoring
"""


PERMISSION_LABEL = {
"MONITORINGS_MODULES": {"label": "modules", "actions": ["R", "U", "E"]},
"MONITORINGS_GRP_SITES": {"label": "groupes de sites", "actions": ["C", "R", "U", "D"]},
"MONITORINGS_SITES": {"label": "sites", "actions": ["C", "R", "U", "D"]},
"MONITORINGS_VISITES": {"label": "visites", "actions": ["C", "R", "U", "D"]},
}

ACTION_LABEL = {
"C": "Créer des",
"R": "Voir les",
"U": "Modifier les",
"D": "Supprimer des",
"E": "Exporter les",
}


def process_for_all_module(process_func):
"""
boucle sur les répertoire des module
Expand All @@ -34,12 +53,6 @@ def process_for_all_module(process_func):
return


def getMonitoringPermissionObjectLabel_dict():
return __import__(
"gn_module_monitoring"
).monitoring.definitions.MonitoringPermissionObjectLabel_dict


def process_export_csv(module_code=None):
"""
fonction qui va chercher les fichier sql de exports/csv et qui les joue
Expand Down Expand Up @@ -85,19 +98,20 @@ def process_available_permissions(module_code):
print(f"Il y a un problème de configuration pour le module {module_code}")
return

insert_module_available_permissions(module_code, "ALL")
tree = config.get("tree", [])

module_objects = [k for k in extract_keys(tree, keys=[])]

permission_level = current_app.config["MONITORINGS"].get("PERMISSION_LEVEL", {})

# Insert permission object
for permission_object_code in config.get("permission_objects", []):
insert_module_available_permissions(module_code, permission_object_code)
for permission_object_code in module_objects:
print(f"Création des permissions pour {module_code} : {permission_object_code}")
insert_module_available_permissions(module_code, permission_level[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)
object_label = PERMISSION_LABEL.get(perm_object_code)["label"]

if not object_label:
print(f"L'object {perm_object_code} n'est pas traité")
Expand All @@ -124,31 +138,24 @@ def insert_module_available_permissions(module_code, perm_object_code):
"""
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)
# Création d'une permission disponible pour chaque action
object_actions = PERMISSION_LABEL.get(perm_object_code)["actions"]
for action in object_actions:
permaction = PermAction.query.filter_by(code_action=action).one()
try:
perm = PermissionAvailable.query.filter_by(
module=module, object=perm_object, action=permaction
).one()
except NoResultFound:
perm = PermissionAvailable(
module=module,
object=perm_object,
action=permaction,
label=f"{ACTION_LABEL[action]} {object_label}",
scope_filter=True,
)
DB.session.add(perm)
DB.session.commit()


def remove_monitoring_module(module_code):
Expand Down Expand Up @@ -313,3 +320,14 @@ def available_modules():
available_modules_.append({**module, "module_code": d})
break
return available_modules_


def extract_keys(test_dict, keys=[]):
"""
FOnction permettant d'extraire de façon récursive les clés d'un dictionnaire
"""
for key, val in test_dict.items():
keys.append(key)
if isinstance(val, dict):
extract_keys(val, keys)
return keys
15 changes: 14 additions & 1 deletion backend/gn_module_monitoring/conf_schema_toml.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,21 @@
from marshmallow import Schema, fields, validates_schema, ValidationError


# Permissions associés à chaque objet monitoring
PERMISSION_LEVEL_DEFAULT = {
"module": "MONITORINGS_MODULES",
"site": "MONITORINGS_SITES",
"sites_group": "MONITORINGS_GRP_SITES",
"visit": "MONITORINGS_VISITES",
"observation": "MONITORINGS_VISITES",
"observation_detail": "MONITORINGS_VISITES",
}


class GnModuleSchemaConf(Schema):
pass
PERMISSION_LEVEL = fields.Dict(
keys=fields.Str(), values=fields.Str(), load_default=PERMISSION_LEVEL_DEFAULT
)


# AREA_TYPE = fields.List(fields.String(), missing=["COM", "M1", "M5", "M10"])
Expand Down
1 change: 0 additions & 1 deletion backend/gn_module_monitoring/config/generic/module.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"cruved": {"C":4, "U":3, "D": 4},
"id_field_name": "id_module",
"description_field_name": "module_label",
"label": "Module",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"cruved": {"C":1, "U":1, "D": 1},
"id_field_name": "id_observation",
"description_field_name": "id_observation",
"chained": true,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"cruved": {"C":1, "U":1, "D": 1},
"id_field_name": "id_observation_detail",
"description_field_name": "id_observation_detail",
"chained": true,
Expand Down
1 change: 0 additions & 1 deletion backend/gn_module_monitoring/config/generic/site.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"cruved": {"C":1, "U":1, "D": 1},
"chained": true,
"id_field_name": "id_base_site",
"description_field_name": "base_site_name",
Expand Down
4 changes: 1 addition & 3 deletions backend/gn_module_monitoring/config/generic/sites_group.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"cruved": {"C":1, "U":1, "D": 1},
"id_field_name": "id_sites_group",
"chained": true,
"description_field_name": "sites_group_name",
Expand Down Expand Up @@ -60,6 +59,5 @@
"attribut_label": "Médias",
"schema_dot_table": "gn_monitoring.t_sites_groups"
}
}
}
}

2 changes: 1 addition & 1 deletion backend/gn_module_monitoring/config/generic/visit.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"cruved": {"C":1, "U":1, "D": 1},
"id_field_name": "id_base_visit",
"chained": true,
"description_field_name": "visit_date_min",
Expand Down Expand Up @@ -83,6 +82,7 @@
"type_util": "dataset",
"attribut_label": "Jeu de données",
"module_code": "__MODULE.MODULE_CODE",
"creatable_in_module": "__MODULE.MODULE_CODE.MONITORINGS_VISITES",
"required": true
},
"nb_observations": {
Expand Down
1 change: 0 additions & 1 deletion backend/gn_module_monitoring/config/repositories.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ def get_config(module_code=None, force=False):

config = config_from_files("config", module_code)
get_config_objects(module_code, config)

# customize config
if module:
custom = {}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""Rename GNM_ to MONITORINGS_
Revision ID: 3ffeea74a9dd
Revises: fc90d31c677f
Create Date: 2023-10-02 12:00:30.382163
"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = "3ffeea74a9dd"
down_revision = "fc90d31c677f"
branch_labels = None
depends_on = None


def upgrade():
op.execute(
"""
UPDATE gn_permissions.t_objects
SET code_object = REPLACE(code_object, 'GNM_', 'MONITORINGS_')
WHERE code_object like 'GNM_%'
"""
)


def downgrade():
op.execute(
"""
UPDATE gn_permissions.t_objects
SET code_object = REPLACE(code_object, 'MONITORINGS_', 'GNM_')
WHERE code_object like 'MONITORINGS_%'
"""
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
"""Delete object ALL
Revision ID: 6a15625a0f4a
Revises: c1528c94d350
Create Date: 2023-10-02 13:53:05.682108
"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = "6a15625a0f4a"
down_revision = "c1528c94d350"
branch_labels = None
depends_on = None


def upgrade():
# Suppression des permissions available de ALL pour les modules monitorings
op.execute(
"""
WITH to_del AS (
SELECT tp.*
FROM gn_permissions.t_permissions_available AS tp
JOIN gn_commons.t_modules AS tm
ON tm.id_module = tp.id_module AND tm."type" = 'monitoring_module'
JOIN gn_permissions.t_objects AS o
ON o.id_object = tp.id_object AND code_object = 'ALL'
)
DELETE FROM gn_permissions.t_permissions_available AS tp
USING to_del td
WHERE tp.id_module = td.id_module
AND tp.id_object = td.id_object
AND tp.id_action = td.id_action
AND tp."label" = td."label"
AND tp.scope_filter = td.scope_filter
AND tp.sensitivity_filter = td.sensitivity_filter;
"""
)

# Suppression des permissions de ALL pour les modules monitorings
op.execute(
"""
WITH to_del AS (
SELECT DISTINCT tp.id_permission
FROM gn_permissions.t_permissions AS tp
JOIN gn_commons.t_modules AS tm
ON tm.id_module = tp.id_module AND tm."type" = 'monitoring_module'
JOIN gn_permissions.t_objects AS o
ON o.id_object = tp.id_object AND code_object = 'ALL'
)
DELETE FROM gn_permissions.t_permissions AS tp
WHERE tp.id_permission IN (SELECT id_permission FROM to_del);
"""
)


def downgrade():
# Creations des permissions available de ALL pour les modules monitorings
# a partir de GNM_MODULES
op.execute(
"""
INSERT INTO gn_permissions.t_permissions_available
(id_module, id_object, id_action, "label", scope_filter, sensitivity_filter)
SELECT
tp.id_module,
gn_permissions.get_id_object('ALL') AS id_object,
tp.id_action,
tp."label",
tp.scope_filter,
tp.sensitivity_filter
FROM gn_permissions.t_permissions_available AS tp
JOIN gn_commons.t_modules AS tm
ON tm.id_module = tp.id_module AND tm."type" = 'monitoring_module'
JOIN gn_permissions.t_objects AS o
ON o.id_object = tp.id_object AND code_object = 'MONITORINGS_MODULES';
"""
)
# Creations des permissions de ALL pour les modules monitorings
# a partir de GNM_MODULES
op.execute(
"""
INSERT INTO gn_permissions.t_permissions
(id_role, id_action, id_module, id_object, scope_value, sensitivity_filter)
SELECT
tp.id_role,
tp.id_action,
tp.id_module,
gn_permissions.get_id_object('ALL') AS id_object,
tp.scope_value,
tp.sensitivity_filter
FROM gn_permissions.t_permissions AS tp
JOIN gn_commons.t_modules AS tm
ON tm.id_module = tp.id_module AND tm."type" = 'monitoring_module'
JOIN gn_permissions.t_objects AS o
ON o.id_object = tp.id_object AND code_object = 'MONITORINGS_MODULES';
"""
)
Loading

0 comments on commit 0c67a59

Please sign in to comment.