|
|
|
-- v0 2020.05.07 AF
|
|
-- ...
|
|
-- v7 2020.05.29 LL
|
|
-- v8 2020.05.29 LL
|
|
-- v9 2020.05.30 LL
|
|
-- v10 2020.06.02 LL
|
|
-- v11 2020.06.11 LL (asgard 0.7.0)
|
|
-- v12 2020.06.25 LL (asgard 0.7.1)
|
|
-- v13 2020.08.03 LL (asgard 1.0.0)
|
|
-- v14 2020.08.27 LL (asgard 1.0.0)
|
|
-- v15 2020.08.31 LL (asgard 1.0.0)
|
|
/*
|
|
trigger AFTER :
|
|
- meilleure prise en compte des valeurs NULL comme éléments
|
|
des array de ctrl, qui auraient pu fausser certains tests -
|
|
"= ANY(array_remove(NEW.ctrl, NULL))"
|
|
au lieu de "= ANY(NEW.ctrl)" ;
|
|
- simplification de la structuration des remontées d'erreurs ;
|
|
- le trigger n'ignore plus les INSERT pour l'application des
|
|
droits du producteur, ce qui empêchait la mise en cohérence des
|
|
propriétaires lors du référencement.
|
|
|
|
trigger BEFORE :
|
|
- meilleure prise en compte des valeurs NULL comme éléments
|
|
des array de ctrl, qui auraient pu fausser certains tests -
|
|
"= ANY(array_remove(NEW.ctrl, NULL))" au lieu de "= ANY(NEW.ctrl)" ;
|
|
- ajout de contrôles pour les modifications par g_admin sur des
|
|
schémas dont un super-utilisateur est producteur, y compris référencement ;
|
|
- amélioration du message d'erreur en cas de tentative de déférencement d'un
|
|
schéma de la nomenclature ;
|
|
- quelques HINT remplacés par DETAIL, qui paraissait plus approprié.
|
|
*/
|
|
|
|
|
|
-- MOT DE PASSE DE CONTRÔLE : 'x7-A;#rzo'
|
|
-- peut être modifié sans risque par rechercher/remplacer
|
|
|
|
|
|
---------------------------------------------
|
|
------ 5 - TRIGGERS SUR GESTION_SCHEMA ------
|
|
---------------------------------------------
|
|
/* 5.1 - TRIGGER BEFORE
|
|
5.2 - TRIGGER AFTER */
|
|
|
|
|
|
------ 5.1 - TRIGGER BEFORE ------
|
|
|
|
-- FUNCTION: z_asgard_admin.asgard_on_modify_gestion_schema_before()
|
|
|
|
CREATE OR REPLACE FUNCTION z_asgard_admin.asgard_on_modify_gestion_schema_before() RETURNS trigger
|
|
LANGUAGE plpgsql
|
|
AS $BODY$
|
|
/* OBJET : Fonction exécutée par le trigger asgard_on_modify_gestion_schema_before,
|
|
qui valide les informations saisies dans la table de gestion.
|
|
CIBLES : z_asgard_admin.gestion_schema.
|
|
PORTEE : FOR EACH ROW.
|
|
DECLENCHEMENT : BEFORE INSERT, UPDATE, DELETE.*/
|
|
DECLARE
|
|
n_role text ;
|
|
BEGIN
|
|
|
|
------ INSERT PAR UN UTILISATEUR NON HABILITE ------
|
|
IF TG_OP = 'INSERT' AND NOT has_database_privilege(current_database(), 'CREATE')
|
|
-- même si creation vaut faux, seul un rôle habilité à créer des
|
|
-- schéma peut ajouter des lignes dans la table de gestion
|
|
THEN
|
|
RAISE EXCEPTION 'TB1. Vous devez être habilité à créer des schémas pour réaliser cette opération.' ;
|
|
END IF ;
|
|
|
|
------ APPLICATION DES VALEURS PAR DEFAUT ------
|
|
-- au tout début car de nombreux tests sont faits par la
|
|
-- suite sur "NOT NEW.creation"
|
|
IF TG_OP IN ('INSERT', 'UPDATE')
|
|
THEN
|
|
NEW.creation := coalesce(NEW.creation, False) ;
|
|
NEW.nomenclature := coalesce(NEW.nomenclature, False) ;
|
|
END IF ;
|
|
|
|
------ EFFACEMENT D'UN ENREGISTREMENT ------
|
|
IF TG_OP = 'DELETE'
|
|
THEN
|
|
-- on n'autorise pas l'effacement si creation vaut True
|
|
-- avec une exception pour les commandes envoyées par la fonction
|
|
-- de maintenance asgard_sortie_gestion_schema
|
|
IF OLD.creation AND (OLD.ctrl[1] IS NULL OR NOT OLD.ctrl[1] = 'EXIT')
|
|
THEN
|
|
RAISE EXCEPTION 'TB2. Opération interdite (schéma %). L''effacement n''est autorisé que si creation vaut False.', OLD.nom_schema
|
|
USING HINT = 'Pour déréférencer un schéma sans le supprimer, vous pouvez utiliser la fonction z_asgard_admin.asgard_sortie_gestion_schema.' ;
|
|
END IF;
|
|
|
|
-- on n'autorise pas l'effacement pour les schémas de la nomenclature
|
|
IF OLD.nomenclature
|
|
THEN
|
|
IF OLD.ctrl[1] = 'EXIT'
|
|
THEN
|
|
RAISE EXCEPTION 'TB26. Opération interdite (schéma %). Le déréférencement n''est pas autorisé pour les schémas de la nomenclature nationale.', OLD.nom_schema
|
|
USING HINT = 'Si vous tenez à déréférencer cet enregistrement, basculez préalablement nomenclature sur False.' ;
|
|
ELSE
|
|
RAISE EXCEPTION 'TB3. Opération interdite (schéma %). L''effacement n''est pas autorisé pour les schémas de la nomenclature nationale.', OLD.nom_schema
|
|
USING HINT = 'Si vous tenez à supprimer définitivement cet enregistrement, basculez préalablement nomenclature sur False.' ;
|
|
END IF ;
|
|
END IF ;
|
|
END IF;
|
|
|
|
------ DE-CREATION D'UN SCHEMA ------
|
|
IF TG_OP = 'UPDATE'
|
|
THEN
|
|
-- si bloc valait déjà d (schéma "mis à la corbeille")
|
|
-- on exécute une commande de suppression du schéma. Toute autre modification sur
|
|
-- la ligne est ignorée.
|
|
IF OLD.bloc = 'd' AND OLD.creation AND NOT NEW.creation AND NEW.ctrl[2] IS NULL
|
|
AND OLD.nom_schema IN (SELECT nspname FROM pg_catalog.pg_namespace)
|
|
THEN
|
|
-- on bloque tout de même les tentatives de suppression
|
|
-- par un utilisateur qui n'aurait pas des droits suffisants (a priori
|
|
-- uniquement dans le cas de g_admin avec un schéma appartenant à un
|
|
-- super-utilisateur).
|
|
IF NOT pg_has_role(OLD.producteur, 'USAGE')
|
|
THEN
|
|
RAISE EXCEPTION 'TB23. Opération interdite (schéma %).', OLD.nom_schema
|
|
USING DETAIL = 'Seul les membres du rôle producteur ' || OLD.producteur || ' peuvent supprimer ce schéma.' ;
|
|
ELSE
|
|
EXECUTE 'DROP SCHEMA ' || quote_ident(OLD.nom_schema) || ' CASCADE' ;
|
|
RAISE NOTICE '... Le schéma % a été supprimé.', OLD.nom_schema ;
|
|
RETURN NULL ;
|
|
END IF ;
|
|
-- sinon, on n'autorise creation à passer de true à false que si le schéma
|
|
-- n'existe plus (permet notamment à l'event trigger qui gère les
|
|
-- suppressions de mettre creation à false)
|
|
ELSIF OLD.creation and NOT NEW.creation
|
|
AND NEW.nom_schema IN (SELECT nspname FROM pg_catalog.pg_namespace)
|
|
THEN
|
|
RAISE EXCEPTION 'TB4. Opération interdite (schéma %). Le champ creation ne peut passer de True à False si le schéma existe.', NEW.nom_schema
|
|
USING HINT = 'Si vous supprimez physiquement le schéma avec la commande DROP SCHEMA, creation basculera sur False automatiquement.' ;
|
|
END IF ;
|
|
END IF ;
|
|
|
|
IF TG_OP <> 'DELETE'
|
|
THEN
|
|
------ PROHIBITION DE LA SAISIE MANUELLE DES OID ------
|
|
-- vérifié grâce au champ ctrl
|
|
IF NEW.ctrl[2] IS NULL
|
|
OR NOT array_length(NEW.ctrl, 1) >= 2
|
|
OR NEW.ctrl[1] IS NULL
|
|
OR NOT NEW.ctrl[1] IN ('CREATE', 'RENAME', 'OWNER', 'DROP', 'SELF', 'EXIT')
|
|
OR NOT NEW.ctrl[2] = 'x7-A;#rzo'
|
|
-- ctrl NULL ou invalide
|
|
THEN
|
|
|
|
IF NEW.ctrl[1] = 'EXIT'
|
|
THEN
|
|
RAISE EXCEPTION 'TB17. Opération interdite (schéma %).', coalesce(NEW.nom_schema, '?')
|
|
USING HINT = 'Pour déréférencer un schéma, veuillez utiliser la fonction z_asgard_admin.asgard_sortie_gestion_schema.' ;
|
|
END IF ;
|
|
|
|
-- réinitialisation du champ ctrl, qui peut contenir des informations
|
|
-- issues de commandes antérieures (dans ctrl[1])
|
|
NEW.ctrl := ARRAY['MANUEL', NULL]::text[] ;
|
|
|
|
IF TG_OP = 'INSERT' AND (
|
|
NEW.oid_producteur IS NOT NULL
|
|
OR NEW.oid_lecteur IS NOT NULL
|
|
OR NEW.oid_editeur IS NOT NULL
|
|
OR NEW.oid_schema IS NOT NULL
|
|
)
|
|
-- cas d'un INSERT manuel pour lequel des OID ont été saisis
|
|
-- on les remet à NULL
|
|
THEN
|
|
NEW.oid_producteur = NULL ;
|
|
NEW.oid_editeur = NULL ;
|
|
NEW.oid_lecteur = NULL ;
|
|
NEW.oid_schema = NULL ;
|
|
ELSIF TG_OP = 'UPDATE'
|
|
THEN
|
|
IF NOT coalesce(NEW.oid_producteur, -1) = coalesce(OLD.oid_producteur, -1)
|
|
OR NOT coalesce(NEW.oid_editeur, -1) = coalesce(OLD.oid_editeur, -1)
|
|
OR NOT coalesce(NEW.oid_lecteur, -1) = coalesce(OLD.oid_lecteur, -1)
|
|
OR NOT coalesce(NEW.oid_schema, -1) = coalesce(OLD.oid_schema, -1)
|
|
-- cas d'un UPDATE avec modification des OID
|
|
-- on les remet à OLD
|
|
THEN
|
|
NEW.oid_producteur = OLD.oid_producteur ;
|
|
NEW.oid_editeur = OLD.oid_editeur ;
|
|
NEW.oid_lecteur = OLD.oid_lecteur ;
|
|
NEW.oid_schema = OLD.oid_schema ;
|
|
END IF ;
|
|
END IF ;
|
|
ELSE
|
|
-- suppression du mot de passe de contrôle.
|
|
-- ctrl[1] est par contre conservé - il sera utilisé
|
|
-- par le trigger AFTER pour connaître l'opération
|
|
-- à l'origine de son déclenchement.
|
|
NEW.ctrl[2] := NULL ;
|
|
END IF ;
|
|
|
|
------ REQUETES AUTO A IGNORER ------
|
|
-- les remontées du trigger AFTER (SELF)
|
|
-- sont exclues, car les contraintes ont déjà
|
|
-- été validées (et pose problèmes avec les
|
|
-- contrôles d'OID sur les UPDATE, car ceux-ci
|
|
-- ne seront pas nécessairement déjà remplis) ;
|
|
-- les requêtes EXIT de même, car c'est un
|
|
-- pré-requis à la suppression qui ne fait
|
|
-- que modifier le champ ctrl
|
|
IF NEW.ctrl[1] IN ('SELF', 'EXIT')
|
|
THEN
|
|
-- aucune action
|
|
RETURN NEW ;
|
|
END IF ;
|
|
|
|
------ VERROUILLAGE DES CHAMPS LIES A LA NOMENCLATURE ------
|
|
-- modifiables uniquement par l'ADL
|
|
IF TG_OP = 'UPDATE'
|
|
THEN
|
|
IF (OLD.nomenclature OR NEW.nomenclature) AND NOT pg_has_role('g_admin', 'MEMBER') AND (
|
|
NOT coalesce(OLD.nomenclature, False) = coalesce(NEW.nomenclature, False)
|
|
OR NOT coalesce(OLD.niv1, '') = coalesce(NEW.niv1, '')
|
|
OR NOT coalesce(OLD.niv1_abr, '') = coalesce(NEW.niv1_abr, '')
|
|
OR NOT coalesce(OLD.niv2, '') = coalesce(NEW.niv2, '')
|
|
OR NOT coalesce(OLD.niv2_abr, '') = coalesce(NEW.niv2_abr, '')
|
|
OR NOT coalesce(OLD.nom_schema, '') = coalesce(NEW.nom_schema, '')
|
|
OR NOT coalesce(OLD.bloc, '') = coalesce(NEW.bloc, '')
|
|
)
|
|
THEN
|
|
RAISE EXCEPTION 'TB18. Opération interdite (schéma %).', NEW.nom_schema
|
|
USING DETAIL = 'Seuls les membres de g_admin sont habilités à modifier les champs nomenclature et - pour les schémas de la nomenclature - bloc, niv1, niv1_abr, niv2, niv2_abr et nom_schema.' ;
|
|
END IF ;
|
|
ELSIF TG_OP = 'INSERT'
|
|
THEN
|
|
IF NEW.nomenclature AND NOT pg_has_role('g_admin', 'MEMBER')
|
|
THEN
|
|
RAISE EXCEPTION 'TB19. Opération interdite (schéma %).', NEW.nom_schema
|
|
USING DETAIL = 'Seuls les membres de g_admin sont autorisés à ajouter des schémas à la nomenclature (nomenclature = True).' ;
|
|
END IF ;
|
|
END IF ;
|
|
|
|
------ NETTOYAGE DES CHAÎNES VIDES ------
|
|
-- si l'utilisateur a entré des chaînes vides on met des NULL
|
|
NEW.editeur := nullif(NEW.editeur, '') ;
|
|
NEW.lecteur := nullif(NEW.lecteur, '') ;
|
|
NEW.bloc := nullif(NEW.bloc, '') ;
|
|
NEW.niv1 := nullif(NEW.niv1, '') ;
|
|
NEW.niv1_abr := nullif(NEW.niv1_abr, '') ;
|
|
NEW.niv2 := nullif(NEW.niv2, '') ;
|
|
NEW.niv2_abr := nullif(NEW.niv2_abr, '') ;
|
|
NEW.nom_schema := nullif(NEW.nom_schema, '') ;
|
|
-- si producteur est vide on met par défaut g_admin
|
|
NEW.producteur := coalesce(nullif(NEW.producteur, ''), 'g_admin') ;
|
|
|
|
------ NETTOYAGE DES CHAMPS OID ------
|
|
-- pour les rôles de lecteur et éditeur,
|
|
-- si le champ de nom est vidé par l'utilisateur,
|
|
-- on vide en conséquence l'OID
|
|
IF NEW.editeur IS NULL
|
|
THEN
|
|
NEW.oid_editeur := NULL ;
|
|
END IF ;
|
|
IF NEW.lecteur IS NULL
|
|
THEN
|
|
NEW.oid_lecteur := NULL ;
|
|
END IF ;
|
|
-- si le schéma n'est pas créé, on s'assure que les champs
|
|
-- d'OID restent vides
|
|
-- à noter que l'event trigger sur DROP SCHEMA vide
|
|
-- déjà le champ oid_schema
|
|
IF NOT NEW.creation
|
|
THEN
|
|
NEW.oid_schema := NULL ;
|
|
NEW.oid_lecteur := NULL ;
|
|
NEW.oid_editeur := NULL ;
|
|
NEW.oid_producteur := NULL ;
|
|
END IF ;
|
|
|
|
------ VALIDITE DES NOMS DE ROLES ------
|
|
-- dans le cas d'un schéma pré-existant, on s'assure que les rôles qui
|
|
-- ne changent pas sont toujours valides (qu'ils existent et que le nom
|
|
-- n'a pas été modifié entre temps)
|
|
-- si tel est le cas, on les met à jour et on le note dans
|
|
-- ctrl, pour que le trigger AFTER sache qu'il ne s'agit
|
|
-- pas réellement de nouveaux rôles sur lesquels les droits
|
|
-- devraient être réappliqués
|
|
IF TG_OP = 'UPDATE' AND NEW.creation
|
|
THEN
|
|
-- producteur
|
|
IF OLD.creation AND OLD.producteur = NEW.producteur
|
|
THEN
|
|
SELECT rolname INTO n_role
|
|
FROM pg_catalog.pg_roles
|
|
WHERE pg_roles.oid = NEW.oid_producteur ;
|
|
IF NOT FOUND
|
|
-- le rôle producteur n'existe pas
|
|
THEN
|
|
-- cas invraisemblable, car un rôle ne peut pas être
|
|
-- supprimé alors qu'il est propriétaire d'un schéma, et la
|
|
-- commande ALTER SCHEMA OWNER TO aurait été interceptée
|
|
-- mais, s'il advient, on repart du propriétaire
|
|
-- renseigné dans pg_namespace
|
|
SELECT replace(nspowner::regrole::text, '"', ''), nspowner
|
|
INTO NEW.producteur, NEW.oid_producteur
|
|
FROM pg_catalog.pg_namespace
|
|
WHERE pg_namespace.oid = NEW.oid_schema ;
|
|
RAISE NOTICE '[table de gestion] ANOMALIE. Schéma %. L''OID actuellement renseigné pour le producteur est invalide. Poursuite avec l''OID du propriétaire courant du schéma.', NEW.nom_schema ;
|
|
NEW.ctrl := array_append(NEW.ctrl, 'CLEAN producteur') ;
|
|
ELSIF NOT n_role = NEW.producteur
|
|
-- libellé obsolète du producteur
|
|
THEN
|
|
NEW.producteur := n_role ;
|
|
RAISE NOTICE '[table de gestion] Schéma %. Mise à jour du libellé du rôle producteur, renommé entre temps.', NEW.nom_schema
|
|
USING DETAIL = 'Ancien nom "' || OLD.producteur || '", nouveau nom "' || NEW.producteur || '".' ;
|
|
NEW.ctrl := array_append(NEW.ctrl, 'CLEAN producteur') ;
|
|
END IF ;
|
|
END IF ;
|
|
-- éditeur
|
|
IF OLD.creation AND OLD.editeur = NEW.editeur
|
|
AND NOT NEW.editeur = 'public'
|
|
THEN
|
|
SELECT rolname INTO n_role
|
|
FROM pg_catalog.pg_roles
|
|
WHERE pg_roles.oid = NEW.oid_editeur ;
|
|
IF NOT FOUND
|
|
-- le rôle éditeur n'existe pas
|
|
THEN
|
|
NEW.editeur := NULL ;
|
|
NEW.oid_editeur := NULL ;
|
|
RAISE NOTICE '[table de gestion] Schéma %. Le rôle éditeur n''existant plus, il est déréférencé.', NEW.nom_schema
|
|
USING DETAIL = 'Ancien nom "' || OLD.editeur || '".' ;
|
|
NEW.ctrl := array_append(NEW.ctrl, 'CLEAN editeur') ;
|
|
ELSIF NOT n_role = NEW.editeur
|
|
-- libellé obsolète de l'éditeur
|
|
THEN
|
|
NEW.editeur := n_role ;
|
|
RAISE NOTICE '[table de gestion] Schéma %. Mise à jour du libellé du rôle éditeur, renommé entre temps.', NEW.nom_schema
|
|
USING DETAIL = 'Ancien nom "' || OLD.editeur || '", nouveau nom "' || NEW.editeur || '".' ;
|
|
NEW.ctrl := array_append(NEW.ctrl, 'CLEAN editeur') ;
|
|
END IF ;
|
|
END IF ;
|
|
-- lecteur
|
|
IF OLD.creation AND OLD.lecteur = NEW.lecteur
|
|
AND NOT NEW.lecteur = 'public'
|
|
THEN
|
|
SELECT rolname INTO n_role
|
|
FROM pg_catalog.pg_roles
|
|
WHERE pg_roles.oid = NEW.oid_lecteur ;
|
|
IF NOT FOUND
|
|
-- le rôle lecteur n'existe pas
|
|
THEN
|
|
NEW.lecteur := NULL ;
|
|
NEW.oid_lecteur := NULL ;
|
|
RAISE NOTICE '[table de gestion] Schéma %. Le rôle lecteur n''existant plus, il est déréférencé.', NEW.nom_schema
|
|
USING DETAIL = 'Ancien nom "' || OLD.lecteur || '".' ;
|
|
NEW.ctrl := array_append(NEW.ctrl, 'CLEAN lecteur') ;
|
|
ELSIF NOT n_role = NEW.lecteur
|
|
-- libellé obsolète du lecteur
|
|
THEN
|
|
NEW.lecteur := n_role ;
|
|
RAISE NOTICE '[table de gestion] Schéma %. Mise à jour du libellé du rôle lecteur, renommé entre temps.', NEW.nom_schema
|
|
USING DETAIL = 'Ancien nom "' || OLD.lecteur || '", nouveau nom "' || NEW.lecteur || '".' ;
|
|
NEW.ctrl := array_append(NEW.ctrl, 'CLEAN lecteur') ;
|
|
END IF ;
|
|
END IF ;
|
|
END IF ;
|
|
|
|
------ NON RESPECT DES CONTRAINTES ------
|
|
-- non nullité de nom_schema
|
|
IF NEW.nom_schema IS NULL
|
|
THEN
|
|
RAISE EXCEPTION 'TB8. Saisie incorrecte. Le nom du schéma doit être renseigné (champ nom_schema).' ;
|
|
END IF ;
|
|
|
|
-- unicité de nom_schema
|
|
-- -> contrôlé après les manipulations sur les blocs de
|
|
-- la partie suivante.
|
|
|
|
-- unicité de oid_schema
|
|
IF TG_OP = 'INSERT' AND NEW.oid_schema IN (SELECT gestion_schema_etr.oid_schema FROM z_asgard.gestion_schema_etr
|
|
WHERE gestion_schema_etr.oid_schema IS NOT NULL)
|
|
THEN
|
|
RAISE EXCEPTION 'TB11. Saisie incorrecte (schéma %). Un schéma de même OID est déjà répertorié dans la table de gestion.', NEW.nom_schema ;
|
|
ELSIF TG_OP = 'UPDATE'
|
|
THEN
|
|
-- cas (très hypothétique) d'une modification d'OID
|
|
IF NOT coalesce(NEW.oid_schema, -1) = coalesce(OLD.oid_schema, -1)
|
|
AND NEW.oid_schema IN (SELECT gestion_schema_etr.oid_schema FROM z_asgard.gestion_schema_etr
|
|
WHERE gestion_schema_etr.oid_schema IS NOT NULL)
|
|
THEN
|
|
RAISE EXCEPTION 'TB12. Saisie incorrecte (schéma %). Un schéma de même OID est déjà répertorié dans la table de gestion.', NEW.nom_schema ;
|
|
END IF ;
|
|
END IF ;
|
|
|
|
-- non répétition des rôles
|
|
IF NOT ((NEW.oid_lecteur IS NULL OR NOT NEW.oid_lecteur = NEW.oid_producteur)
|
|
AND (NEW.oid_editeur IS NULL OR NOT NEW.oid_editeur = NEW.oid_producteur)
|
|
AND (NEW.oid_lecteur IS NULL OR NEW.oid_editeur IS NULL OR NOT NEW.oid_lecteur = NEW.oid_editeur))
|
|
THEN
|
|
RAISE EXCEPTION 'TB13. Saisie incorrecte (schéma %). Les rôles producteur, lecteur et éditeur doivent être distincts.', NEW.nom_schema ;
|
|
END IF ;
|
|
END IF ;
|
|
|
|
------ COHERENCE BLOC/NOM DU SCHEMA ------
|
|
IF TG_OP IN ('INSERT', 'UPDATE')
|
|
THEN
|
|
IF NEW.bloc IS NULL AND NEW.nom_schema ~ '^[a-z]_'
|
|
-- si bloc est NULL, mais que le nom du schéma
|
|
-- comporte un préfixe, on met ce préfixe dans bloc
|
|
THEN
|
|
NEW.bloc := substring(NEW.nom_schema, '^([a-z])_') ;
|
|
RAISE NOTICE '[table de gestion] Mise à jour du bloc pour le schéma %.', NEW.nom_schema || ' (' || NEW.bloc || ')' ;
|
|
ELSIF NEW.bloc IS NULL
|
|
-- si bloc est NULL, et que (sous-entendu) le nom du schéma ne
|
|
-- respecte pas la nomenclature, on avertit l'utilisateur
|
|
THEN
|
|
RAISE NOTICE '[table de gestion] Le nom du schéma % ne respecte pas la nomenclature.', NEW.nom_schema
|
|
USING HINT = 'Si vous saisissez un préfixe dans le champ bloc, il sera automatiquement ajouté au nom du schéma.' ;
|
|
ELSIF NOT NEW.nom_schema ~ ('^'|| NEW.bloc || '_') AND NOT NEW.bloc = 'd'
|
|
-- le bloc est renseigné mais le nom du schéma ne correspond pas
|
|
-- (et il ne s'agit pas d'un schéma mis à la corbeille) :
|
|
-- si le nom est de la forme 'a_...', alors :
|
|
-- - dans le cas d'un UPDATE avec modification du nom
|
|
-- du schéma et pas du bloc, on se fie au nom du schéma
|
|
-- et on change le bloc ;
|
|
-- - si bloc n'est pas une lettre, on renvoie une erreur ;
|
|
-- - dans les autres cas, on se fie au bloc et change le
|
|
-- préfixe.
|
|
-- Si le nom ne comporte pas de préfixe :
|
|
-- - si le bloc est une lettre, on l'ajoute au début du
|
|
-- nom (sans doubler l'underscore, si le nom commençait par
|
|
-- un underscore) ;
|
|
-- - sinon on renvoie une erreur.
|
|
THEN
|
|
IF NEW.nom_schema ~ '^([a-z])?_'
|
|
THEN
|
|
IF TG_OP = 'UPDATE'
|
|
THEN
|
|
IF NOT NEW.nom_schema = OLD.nom_schema AND NEW.bloc = OLD.bloc
|
|
THEN
|
|
NEW.bloc := substring(NEW.nom_schema, '^([a-z])_') ;
|
|
RAISE NOTICE '[table de gestion] Mise à jour du bloc pour le schéma %.', NEW.nom_schema || ' (' || NEW.bloc || ')' ;
|
|
ELSIF NOT NEW.bloc ~ '^[a-z]$'
|
|
THEN
|
|
RAISE EXCEPTION 'TB14. Saisie invalide (schéma %). Le bloc doit être une lettre minuscule ou rien.', NEW.nom_schema ;
|
|
ELSE
|
|
NEW.nom_schema := regexp_replace(NEW.nom_schema, '^([a-z])?_', NEW.bloc || '_') ;
|
|
RAISE NOTICE '[table de gestion] Mise à jour du préfixe du schéma %.', NEW.nom_schema || ' d''après son bloc (' || NEW.bloc || ')' ;
|
|
END IF ;
|
|
ELSIF NOT NEW.bloc ~ '^[a-z]$'
|
|
THEN
|
|
RAISE EXCEPTION 'TB15. Saisie invalide (schéma %). Le bloc doit être une lettre minuscule ou rien.', NEW.nom_schema ;
|
|
ELSE
|
|
NEW.nom_schema := regexp_replace(NEW.nom_schema, '^([a-z])?_', NEW.bloc || '_') ;
|
|
RAISE NOTICE '[table de gestion] Mise à jour du préfixe du schéma %.', NEW.nom_schema || ' d''après son bloc (' || NEW.bloc || ')' ;
|
|
END IF ;
|
|
ELSIF NOT NEW.bloc ~ '^[a-z]$'
|
|
THEN
|
|
RAISE EXCEPTION 'TB16. Saisie invalide (schéma %). Le bloc doit être une lettre minuscule ou rien.', NEW.nom_schema ;
|
|
ELSE
|
|
NEW.nom_schema := NEW.bloc || '_' || NEW.nom_schema ;
|
|
RAISE NOTICE '[table de gestion] Mise à jour du préfixe du schéma %.', NEW.nom_schema || ' d''après son bloc (' || NEW.bloc || ')' ;
|
|
END IF ;
|
|
-- le trigger AFTER se chargera de renommer physiquement le
|
|
-- schéma d'autant que de besoin
|
|
END IF ;
|
|
END IF ;
|
|
|
|
------ NON RESPECT DES CONTRAINTES (SUITE) ------
|
|
-- unicité de nom_schema
|
|
IF TG_OP IN ('INSERT', 'UPDATE')
|
|
THEN
|
|
IF TG_OP = 'INSERT' AND NEW.nom_schema IN (SELECT gestion_schema_etr.nom_schema FROM z_asgard.gestion_schema_etr)
|
|
THEN
|
|
RAISE EXCEPTION 'TB9. Saisie incorrecte (schéma %). Un schéma de même nom est déjà répertorié dans la table de gestion.', NEW.nom_schema ;
|
|
ELSIF TG_OP = 'UPDATE'
|
|
THEN
|
|
-- cas d'un changement de nom
|
|
IF NOT NEW.nom_schema = OLD.nom_schema
|
|
AND NEW.nom_schema IN (SELECT gestion_schema_etr.nom_schema FROM z_asgard.gestion_schema_etr)
|
|
THEN
|
|
RAISE EXCEPTION 'TB10. Saisie incorrecte (schéma %). Un schéma de même nom est déjà répertorié dans la table de gestion.', NEW.nom_schema ;
|
|
END IF ;
|
|
END IF ;
|
|
END IF ;
|
|
|
|
------ MISE À LA CORBEILLE ------
|
|
-- notification de l'utilisateur
|
|
IF TG_OP = 'UPDATE'
|
|
THEN
|
|
-- schéma existant dont bloc bascule sur 'd'
|
|
-- ou schéma créé par bascule de creation sur True dans bloc vaut 'd'
|
|
IF NEW.creation AND NEW.bloc = 'd' AND (NOT OLD.bloc = 'd' OR OLD.bloc IS NULL)
|
|
OR NEW.creation AND NOT OLD.creation AND NEW.bloc = 'd'
|
|
THEN
|
|
RAISE NOTICE '[table de gestion] Le schéma % a été mis à la corbeille (bloc = ''d'').', NEW.nom_schema
|
|
USING HINT = 'Si vous basculez creation sur False, le schéma et son contenu seront automatiquement supprimés.' ;
|
|
-- restauration
|
|
ELSIF NEW.creation AND OLD.bloc = 'd' AND (NOT NEW.bloc = 'd' OR NEW.bloc IS NULL)
|
|
THEN
|
|
RAISE NOTICE '[table de gestion] Le schéma % a été retiré de la corbeille (bloc ne vaut plus ''d'').', NEW.nom_schema ;
|
|
END IF ;
|
|
ELSIF TG_OP = 'INSERT'
|
|
THEN
|
|
-- nouveau schéma dont bloc vaut 'd'
|
|
IF NEW.creation AND NEW.bloc = 'd'
|
|
THEN
|
|
RAISE NOTICE '[table de gestion] Le schéma % a été mis à la corbeille (bloc = ''d'').', NEW.nom_schema
|
|
USING HINT = 'Si vous basculez creation sur False, le schéma et son contenu seront automatiquement supprimés.' ;
|
|
END IF ;
|
|
END IF ;
|
|
|
|
------ SCHEMAS DES SUPER-UTILISATEURS ------
|
|
-- concerne uniquement les membres de g_admin, qui voient tous
|
|
-- les schémas, y compris ceux des super-utilisateurs dont ils
|
|
-- ne sont pas membres. Les contrôles suivants bloquent dans ce
|
|
-- cas les tentatives de mise à jour des champs nom_schema,
|
|
-- producteur, editeur et lecteur, ainsi que les création de schéma
|
|
-- via un INSERT ou un UPDATE.
|
|
IF TG_OP = 'UPDATE'
|
|
THEN
|
|
IF NOT pg_has_role(OLD.producteur, 'USAGE')
|
|
AND OLD.producteur IN (SELECT rolname FROM pg_catalog.pg_roles WHERE rolsuper)
|
|
AND OLD.creation
|
|
AND (
|
|
NOT OLD.nom_schema = NEW.nom_schema
|
|
OR NOT OLD.producteur = NEW.producteur AND (NEW.ctrl IS NULL OR NOT 'CLEAN producteur' = ANY(array_remove(NEW.ctrl, NULL)))
|
|
OR NOT coalesce(OLD.editeur, '') = coalesce(NEW.editeur, '') AND (NEW.ctrl IS NULL OR NOT 'CLEAN editeur' = ANY(array_remove(NEW.ctrl, NULL)))
|
|
OR NOT coalesce(OLD.lecteur, '') = coalesce(NEW.lecteur, '') AND (NEW.ctrl IS NULL OR NOT 'CLEAN lecteur' = ANY(array_remove(NEW.ctrl, NULL)))
|
|
)
|
|
THEN
|
|
RAISE EXCEPTION 'TB20. Opération interdite (schéma %).', OLD.nom_schema
|
|
USING DETAIL = 'Seul le rôle producteur ' || OLD.producteur || ' (super-utilisateur) peut modifier ce schéma.' ;
|
|
ELSIF NOT pg_has_role(NEW.producteur, 'USAGE')
|
|
AND NEW.producteur IN (SELECT rolname FROM pg_catalog.pg_roles WHERE rolsuper)
|
|
AND NEW.creation AND NOT OLD.creation
|
|
THEN
|
|
RAISE EXCEPTION 'TB21. Opération interdite (schéma %).', NEW.nom_schema
|
|
USING DETAIL = 'Seul le super-utilisateur ' || NEW.producteur || ' peut créer un schéma dont il est identifié comme producteur.' ;
|
|
ELSIF NOT pg_has_role(NEW.producteur, 'USAGE')
|
|
AND NEW.producteur IN (SELECT rolname FROM pg_catalog.pg_roles WHERE rolsuper)
|
|
AND NEW.creation
|
|
AND NOT OLD.producteur = NEW.producteur AND (NEW.ctrl IS NULL OR NOT 'CLEAN producteur' = ANY(array_remove(NEW.ctrl, NULL)))
|
|
THEN
|
|
RAISE EXCEPTION 'TB24. Opération interdite (schéma %).', NEW.nom_schema
|
|
USING DETAIL = 'Seul le super-utilisateur ' || NEW.producteur || ' peut se désigner comme producteur d''un schéma.' ;
|
|
END IF ;
|
|
ELSIF TG_OP = 'INSERT'
|
|
THEN
|
|
IF NOT pg_has_role(NEW.producteur, 'USAGE')
|
|
AND NEW.producteur IN (SELECT rolname FROM pg_catalog.pg_roles WHERE rolsuper)
|
|
AND NEW.creation
|
|
AND NOT NEW.nom_schema IN (SELECT nspname FROM pg_catalog.pg_namespace)
|
|
-- on exclut les schémas en cours de référencement, qui sont gérés
|
|
-- juste après, avec leur propre message d'erreur
|
|
THEN
|
|
RAISE EXCEPTION 'TB22. Opération interdite (schéma %).', NEW.nom_schema
|
|
USING DETAIL = 'Seul le super-utilisateur ' || NEW.producteur || ' peut créer un schéma dont il est identifié comme producteur.' ;
|
|
ELSIF NOT pg_has_role(NEW.producteur, 'USAGE')
|
|
AND NEW.producteur IN (SELECT rolname FROM pg_catalog.pg_roles WHERE rolsuper)
|
|
AND NEW.nom_schema IN (SELECT nspname FROM pg_catalog.pg_namespace)
|
|
-- schéma pré-existant en cours de référencement
|
|
THEN
|
|
RAISE EXCEPTION 'TB25. Opération interdite (schéma %).', NEW.nom_schema
|
|
USING DETAIL = 'Seul le super-utilisateur ' || NEW.producteur || ' peut référencer dans ASGARD un schéma dont il est identifié comme producteur.' ;
|
|
END IF ;
|
|
END IF ;
|
|
|
|
------ RETURN ------
|
|
IF TG_OP IN ('UPDATE', 'INSERT')
|
|
THEN
|
|
RETURN NEW ;
|
|
ELSIF TG_OP = 'DELETE'
|
|
THEN
|
|
RETURN OLD ;
|
|
END IF ;
|
|
|
|
END
|
|
$BODY$ ;
|
|
|
|
ALTER FUNCTION z_asgard_admin.asgard_on_modify_gestion_schema_before()
|
|
OWNER TO g_admin ;
|
|
|
|
COMMENT ON FUNCTION z_asgard_admin.asgard_on_modify_gestion_schema_before() IS 'ASGARD. Fonction appelée par le trigger qui valide les modifications de la table de gestion.';
|
|
|
|
|
|
-- Trigger: asgard_on_modify_gestion_schema_before
|
|
|
|
CREATE TRIGGER asgard_on_modify_gestion_schema_before
|
|
BEFORE INSERT OR DELETE OR UPDATE
|
|
ON z_asgard_admin.gestion_schema
|
|
FOR EACH ROW
|
|
EXECUTE PROCEDURE z_asgard_admin.asgard_on_modify_gestion_schema_before() ;
|
|
|
|
COMMENT ON TRIGGER asgard_on_modify_gestion_schema_before ON z_asgard_admin.gestion_schema IS 'ASGARD. Trigger qui valide les modifications de la table de gestion.';
|
|
|
|
|
|
|
|
------ 5.2 - TRIGGER AFTER ------
|
|
|
|
-- FUNCTION: z_asgard_admin.asgard_on_modify_gestion_schema_after()
|
|
|
|
CREATE OR REPLACE FUNCTION z_asgard_admin.asgard_on_modify_gestion_schema_after() RETURNS trigger
|
|
LANGUAGE plpgsql
|
|
AS $BODY$
|
|
/* OBJET : Fonction exécutée par le trigger asgard_on_modify_gestion_schema_after,
|
|
qui répercute physiquement les modifications de la table de gestion.
|
|
CIBLES : z_asgard_admin.gestion_schema.
|
|
PORTEE : FOR EACH ROW.
|
|
DECLENCHEMENT : AFTER INSERT OR UPDATE.*/
|
|
DECLARE
|
|
utilisateur text ;
|
|
createur text ;
|
|
administrateur text ;
|
|
e_mssg text ;
|
|
e_hint text ;
|
|
e_detl text ;
|
|
b_superuser boolean ;
|
|
b_test boolean ;
|
|
l_commande text[] ;
|
|
c text ;
|
|
c_reverse text ;
|
|
a_producteur text ;
|
|
a_editeur text ;
|
|
a_lecteur text ;
|
|
n int ;
|
|
BEGIN
|
|
|
|
------ REQUETES AUTO A IGNORER ------
|
|
-- les remontées du trigger lui-même (SELF),
|
|
-- ainsi que des event triggers sur les
|
|
-- suppressions de schémas (DROP), n'appellent
|
|
-- aucune action, elles sont donc exclues dès
|
|
-- le départ
|
|
-- les remontées des changements de noms sont
|
|
-- conservées, pour le cas où la mise en
|
|
-- cohérence avec "bloc" aurait conduit à une
|
|
-- modification du nom par le trigger BEFORE
|
|
-- (géré au point suivant)
|
|
-- les remontées des créations et changements
|
|
-- de propriétaire (CREATE et OWNER) appellent
|
|
-- des opérations sur les droits plus lourdes
|
|
-- qui ne permettent pas de les exclure en
|
|
-- amont
|
|
IF NEW.ctrl[1] IN ('SELF', 'DROP')
|
|
THEN
|
|
-- aucune action
|
|
RETURN NULL ;
|
|
END IF ;
|
|
|
|
------ MANIPULATIONS PREALABLES ------
|
|
utilisateur := current_user ;
|
|
|
|
-- si besoin pour les futures opérations sur les rôles,
|
|
-- récupération du nom d'un rôle dont current_user est membre
|
|
-- et qui a l'attribut CREATEROLE. Autant que possible, la
|
|
-- requête renvoie current_user lui-même. On exclut d'office les
|
|
-- rôles NOINHERIT qui ne pourront pas avoir simultanément les
|
|
-- droits du propriétaire de NEW et OLD.producteur
|
|
SELECT rolname INTO createur FROM pg_roles
|
|
WHERE pg_has_role(rolname, 'MEMBER') AND rolcreaterole AND rolinherit
|
|
ORDER BY rolname = current_user DESC ;
|
|
|
|
IF TG_OP = 'UPDATE'
|
|
THEN
|
|
-- la validité de OLD.producteur n'ayant
|
|
-- pas été contrôlée par le trigger BEFORE,
|
|
-- on le fait maintenant
|
|
SELECT rolname INTO a_producteur
|
|
FROM pg_catalog.pg_roles
|
|
WHERE pg_roles.oid = OLD.oid_producteur ;
|
|
-- pour la suite, on emploira toujours
|
|
-- a_producteur à la place de OLD.producteur
|
|
-- pour les opérations sur les droits.
|
|
-- Il est réputé non NULL pour un schéma
|
|
-- pré-existant (OLD.creation vaut True),
|
|
-- dans la mesure où un rôle ne peut être
|
|
-- supprimé s'il est propriétaire d'un
|
|
-- schéma et où tous les changements de
|
|
-- propriétaires sont remontés par event
|
|
-- triggers (+ contrôles pour assurer la
|
|
-- non-modification manuelle des OID).
|
|
IF NOT FOUND AND OLD.creation AND (NEW.ctrl IS NULL OR NOT 'CLEAN producteur' = ANY(array_remove(NEW.ctrl, NULL)))
|
|
THEN
|
|
RAISE NOTICE '[table de gestion] ANOMALIE. Schéma %. L''OID actuellement renseigné pour le producteur dans la table de gestion est invalide. Poursuite avec l''OID du propriétaire courant du schéma.', OLD.nom_schema ;
|
|
SELECT replace(nspowner::regrole::text, '"', '') INTO a_producteur
|
|
FROM pg_catalog.pg_namespace
|
|
WHERE pg_namespace.oid = NEW.oid_schema ;
|
|
IF NOT FOUND
|
|
THEN
|
|
RAISE EXCEPTION 'TA1. Anomalie critique (schéma %). Le propriétaire du schéma est introuvable.', OLD.nom_schema ;
|
|
END IF ;
|
|
END IF ;
|
|
END IF ;
|
|
|
|
------ MISE EN APPLICATION D'UN CHANGEMENT DE NOM DE SCHEMA ------
|
|
IF NOT NEW.oid_schema::regnamespace::text = quote_ident(NEW.nom_schema)
|
|
-- le schéma existe et ne porte pas déjà le nom NEW.nom_schema
|
|
THEN
|
|
EXECUTE 'ALTER SCHEMA '|| NEW.oid_schema::regnamespace::text ||
|
|
' RENAME TO ' || quote_ident(NEW.nom_schema) ;
|
|
RAISE NOTICE '... Le schéma % a été renommé.', NEW.nom_schema ;
|
|
END IF ;
|
|
-- exclusion des remontées d'event trigger correspondant
|
|
-- à des changements de noms
|
|
IF NEW.ctrl[1] = 'RENAME'
|
|
THEN
|
|
-- aucune action
|
|
RETURN NULL ;
|
|
END IF ;
|
|
|
|
------ PREPARATION DU PRODUCTEUR ------
|
|
-- on ne s'intéresse pas aux cas :
|
|
-- - d'un schéma qui n'a pas/plus vocation à exister
|
|
-- (creation vaut False) ;
|
|
-- - d'un schéma pré-existant dont les rôles ne changent pas
|
|
-- ou dont le libellé a juste été nettoyé par le trigger
|
|
-- BEFORE.
|
|
-- ils sont donc exclus au préalable
|
|
-- si le moindre rôle a changé, il faudra être membre du
|
|
-- groupe propriétaire/producteur pour pouvoir modifier
|
|
-- les privilèges en conséquence
|
|
b_test := False ;
|
|
IF NOT NEW.creation
|
|
THEN
|
|
b_test := True ;
|
|
ELSIF TG_OP = 'UPDATE'
|
|
THEN
|
|
IF OLD.creation
|
|
AND (NEW.producteur = OLD.producteur OR 'CLEAN producteur' = ANY(array_remove(NEW.ctrl, NULL)))
|
|
AND (coalesce(NEW.editeur, '') = coalesce(OLD.editeur, '') OR 'CLEAN editeur' = ANY(array_remove(NEW.ctrl, NULL)))
|
|
AND (coalesce(NEW.lecteur, '') = coalesce(OLD.lecteur, '') OR 'CLEAN lecteur' = ANY(array_remove(NEW.ctrl, NULL)))
|
|
THEN
|
|
b_test := True ;
|
|
END IF ;
|
|
END IF ;
|
|
|
|
IF NOT b_test
|
|
THEN
|
|
IF NOT NEW.producteur IN (SELECT rolname FROM pg_catalog.pg_roles)
|
|
-- si le producteur désigné n'existe pas, on le crée
|
|
-- ou renvoie une erreur si les privilèges de l'utilisateur
|
|
-- sont insuffisants
|
|
THEN
|
|
IF createur IS NULL
|
|
THEN
|
|
RAISE EXCEPTION 'TA2. Opération interdite. Vous n''êtes pas habilité à créer le rôle %.', NEW.producteur
|
|
USING HINT = 'Être membre d''un rôle disposant des attributs CREATEROLE et INHERIT est nécessaire pour créer de nouveaux producteurs.' ;
|
|
END IF ;
|
|
EXECUTE 'SET ROLE ' || quote_ident(createur) ;
|
|
EXECUTE 'CREATE ROLE ' || quote_ident(NEW.producteur) ;
|
|
RAISE NOTICE '... Le rôle de groupe % a été créé.', NEW.producteur ;
|
|
EXECUTE 'SET ROLE ' || quote_ident(utilisateur) ;
|
|
ELSE
|
|
-- si le rôle producteur existe, on vérifie qu'il n'a pas l'option LOGIN
|
|
-- les superusers avec LOGIN (comme postgres) sont tolérés
|
|
-- paradoxe ou non, dans l'état actuel des choses, cette erreur se
|
|
-- déclenche aussi lorsque la modification ne porte que sur les rôles
|
|
-- lecteur/éditeur
|
|
SELECT rolsuper INTO b_superuser
|
|
FROM pg_roles WHERE rolname = NEW.producteur AND rolcanlogin ;
|
|
IF NOT b_superuser
|
|
THEN
|
|
RAISE EXCEPTION 'TA3. Opération interdite (schéma %). Le producteur/propriétaire du schéma ne doit pas être un rôle de connexion.', NEW.nom_schema ;
|
|
END IF ;
|
|
END IF ;
|
|
b_superuser := coalesce(b_superuser, False) ;
|
|
|
|
-- mise à jour du champ d'OID du producteur
|
|
IF NEW.ctrl[1] IS NULL OR NOT NEW.ctrl[1] IN ('OWNER', 'CREATE')
|
|
-- pas dans le cas d'une remontée de commande directe
|
|
-- où l'OID du producteur sera déjà renseigné
|
|
-- et uniquement s'il a réellement été modifié (ce
|
|
-- qui n'est pas le cas si les changements ne portent
|
|
-- que sur les rôles lecteur/éditeur)
|
|
THEN
|
|
UPDATE z_asgard.gestion_schema_etr
|
|
SET oid_producteur = quote_ident(NEW.producteur)::regrole::oid,
|
|
ctrl = ARRAY['SELF', 'x7-A;#rzo']
|
|
WHERE nom_schema = NEW.nom_schema AND (
|
|
oid_producteur IS NULL
|
|
OR NOT oid_producteur = quote_ident(NEW.producteur)::regrole::oid
|
|
) ;
|
|
END IF ;
|
|
|
|
-- implémentation des permissions manquantes sur NEW.producteur
|
|
IF NOT pg_has_role(utilisateur, NEW.producteur, 'USAGE')
|
|
THEN
|
|
b_test := True ;
|
|
IF createur IS NULL OR b_superuser
|
|
THEN
|
|
RAISE EXCEPTION 'TA4. Opération interdite. Permissions insuffisantes pour le rôle %.', NEW.producteur
|
|
USING HINT = 'Votre rôle doit être membre de ' || NEW.producteur
|
|
|| ' ou disposer de l''attribut CREATEROLE pour réaliser cette opération.' ;
|
|
END IF ;
|
|
END IF ;
|
|
IF TG_OP = 'UPDATE'
|
|
THEN
|
|
IF OLD.creation AND NOT pg_has_role(utilisateur, a_producteur, 'USAGE')
|
|
AND NOT (NEW.producteur = OLD.producteur OR 'CLEAN producteur' = ANY(array_remove(NEW.ctrl, NULL)))
|
|
-- les permissions sur OLD.producteur ne sont contrôlées que si le producteur
|
|
-- a effectivement été modifié
|
|
THEN
|
|
b_test := True ;
|
|
IF createur IS NULL OR b_superuser
|
|
THEN
|
|
RAISE EXCEPTION 'TA5. Opération interdite. Permissions insuffisantes pour le rôle %.', a_producteur
|
|
USING HINT = 'Votre rôle doit être membre de ' || a_producteur
|
|
|| ' ou disposer de l''attribut CREATEROLE pour réaliser cette opération.' ;
|
|
END IF ;
|
|
END IF ;
|
|
END IF ;
|
|
IF b_test
|
|
THEN
|
|
EXECUTE 'SET ROLE ' || quote_ident(createur) ;
|
|
-- par commodité, on rend createur membre à la fois de NEW et (si besoin)
|
|
-- de OLD.producteur, même si l'utilisateur avait déjà accès à
|
|
-- l'un des deux par ailleurs :
|
|
IF NOT pg_has_role(createur, NEW.producteur, 'USAGE') AND NOT b_superuser
|
|
THEN
|
|
EXECUTE 'GRANT ' || quote_ident(NEW.producteur) || ' TO ' || quote_ident(createur) ;
|
|
RAISE NOTICE '... Permission accordée à %.', createur || ' sur le rôle ' || NEW.producteur ;
|
|
END IF ;
|
|
IF TG_OP = 'UPDATE'
|
|
THEN
|
|
IF NOT pg_has_role(createur, a_producteur, 'USAGE') AND NOT b_superuser
|
|
THEN
|
|
EXECUTE 'GRANT ' || quote_ident(a_producteur) || ' TO ' || quote_ident(createur) ;
|
|
RAISE NOTICE '... Permission accordée à %.', createur || ' sur le rôle ' || a_producteur ;
|
|
END IF ;
|
|
END IF ;
|
|
EXECUTE 'SET ROLE ' || quote_ident(utilisateur) ;
|
|
END IF ;
|
|
|
|
-- permission de g_admin sur le producteur, s'il y a encore lieu
|
|
-- à noter que, dans le cas où le producteur n'a pas été modifié, g_admin
|
|
-- devrait déjà avoir une permission sur NEW.producteur, sauf à ce qu'elle
|
|
-- lui ait été retirée manuellement entre temps. Les requêtes suivantes
|
|
-- génèreraient alors une erreur même dans le cas où la modification ne
|
|
-- porte que sur les rôles lecteur/éditeur - ce qui peut-être perçu comme
|
|
-- discutable.
|
|
IF NOT pg_has_role('g_admin', NEW.producteur, 'USAGE') AND NOT b_superuser
|
|
THEN
|
|
IF createur IS NOT NULL
|
|
THEN
|
|
EXECUTE 'SET ROLE ' || quote_ident(createur) ;
|
|
EXECUTE 'GRANT ' || quote_ident(NEW.producteur) || ' TO g_admin' ;
|
|
RAISE NOTICE '... Permission accordée à g_admin sur le rôle %.', NEW.producteur ;
|
|
EXECUTE 'SET ROLE ' || quote_ident(utilisateur) ;
|
|
ELSE
|
|
SELECT grantee INTO administrateur
|
|
FROM information_schema.applicable_roles
|
|
WHERE is_grantable = 'YES' AND role_name = NEW.producteur ;
|
|
IF FOUND
|
|
THEN
|
|
EXECUTE 'SET ROLE ' || quote_ident(administrateur) ;
|
|
EXECUTE 'GRANT ' || quote_ident(NEW.producteur) || ' TO g_admin' ;
|
|
RAISE NOTICE '... Permission accordée à g_admin sur le rôle %.', NEW.producteur ;
|
|
EXECUTE 'SET ROLE ' || quote_ident(utilisateur) ;
|
|
ELSE
|
|
RAISE EXCEPTION 'TA6. Opération interdite. Permissions insuffisantes pour le rôle %.', NEW.producteur
|
|
USING DETAIL = 'GRANT ' || quote_ident(NEW.producteur) || ' TO g_admin',
|
|
HINT = 'Votre rôle doit être membre de ' || NEW.producteur
|
|
|| ' avec admin option ou disposer de l''attribut CREATEROLE pour réaliser cette opération.' ;
|
|
END IF ;
|
|
END IF ;
|
|
END IF ;
|
|
END IF ;
|
|
|
|
------ PREPARATION DE L'EDITEUR ------
|
|
-- limitée ici à la création du rôle et l'implémentation
|
|
-- de son OID. On ne s'intéresse donc pas aux cas :
|
|
-- - où il y a pas d'éditeur ;
|
|
-- - d'un schéma qui n'a pas/plus vocation à exister ;
|
|
-- - d'un schéma pré-existant dont l'éditeur ne change pas
|
|
-- ou dont le libellé a seulement été nettoyé par le
|
|
-- trigger BEFORE.
|
|
-- ils sont donc exclus au préalable
|
|
b_test := False ;
|
|
IF NOT NEW.creation OR NEW.editeur IS NULL
|
|
OR 'CLEAN editeur' = ANY(array_remove(NEW.ctrl, NULL))
|
|
THEN
|
|
b_test := True ;
|
|
ELSIF TG_OP = 'UPDATE'
|
|
THEN
|
|
IF OLD.creation AND NEW.editeur = OLD.editeur
|
|
THEN
|
|
b_test := True ;
|
|
END IF ;
|
|
END IF ;
|
|
|
|
IF NOT b_test
|
|
THEN
|
|
IF NOT NEW.editeur IN (SELECT rolname FROM pg_catalog.pg_roles)
|
|
AND NOT NEW.editeur = 'public'
|
|
-- si l'éditeur désigné n'existe pas, on le crée
|
|
-- ou renvoie une erreur si les privilèges de l'utilisateur
|
|
-- sont insuffisants
|
|
THEN
|
|
IF createur IS NULL
|
|
THEN
|
|
RAISE EXCEPTION 'TA7. Opération interdite. Vous n''êtes pas habilité à créer le rôle %.', NEW.editeur
|
|
USING HINT = 'Être membre d''un rôle disposant des attributs CREATEROLE et INHERIT est nécessaire pour créer de nouveaux éditeurs.' ;
|
|
END IF ;
|
|
EXECUTE 'SET ROLE ' || quote_ident(createur) ;
|
|
EXECUTE 'CREATE ROLE ' || quote_ident(NEW.editeur) ;
|
|
RAISE NOTICE '... Le rôle de groupe % a été créé.', NEW.editeur ;
|
|
EXECUTE 'SET ROLE ' || quote_ident(utilisateur) ;
|
|
END IF ;
|
|
|
|
-- mise à jour du champ d'OID de l'éditeur
|
|
IF NEW.editeur = 'public'
|
|
THEN
|
|
UPDATE z_asgard.gestion_schema_etr
|
|
SET oid_editeur = 0,
|
|
ctrl = ARRAY['SELF', 'x7-A;#rzo']
|
|
WHERE nom_schema = NEW.nom_schema AND (
|
|
oid_editeur IS NULL
|
|
OR NOT oid_editeur = 0
|
|
) ;
|
|
ELSE
|
|
UPDATE z_asgard.gestion_schema_etr
|
|
SET oid_editeur = quote_ident(NEW.editeur)::regrole::oid,
|
|
ctrl = ARRAY['SELF', 'x7-A;#rzo']
|
|
WHERE nom_schema = NEW.nom_schema AND (
|
|
oid_editeur IS NULL
|
|
OR NOT oid_editeur = quote_ident(NEW.editeur)::regrole::oid
|
|
) ;
|
|
END IF ;
|
|
END IF ;
|
|
|
|
------ PREPARATION DU LECTEUR ------
|
|
-- limitée ici à la création du rôle et l'implémentation
|
|
-- de son OID. On ne s'intéresse donc pas aux cas :
|
|
-- - où il y a pas de lecteur ;
|
|
-- - d'un schéma qui n'a pas/plus vocation à exister ;
|
|
-- - d'un schéma pré-existant dont l'éditeur ne change pas
|
|
-- ou dont le libellé a seulement été nettoyé par le
|
|
-- trigger BEFORE.
|
|
-- ils sont donc exclus au préalable
|
|
b_test := False ;
|
|
IF NOT NEW.creation OR NEW.lecteur IS NULL
|
|
OR 'CLEAN lecteur' = ANY(array_remove(NEW.ctrl, NULL))
|
|
THEN
|
|
b_test := True ;
|
|
ELSIF TG_OP = 'UPDATE'
|
|
THEN
|
|
IF OLD.creation AND NEW.lecteur = OLD.lecteur
|
|
THEN
|
|
b_test := True ;
|
|
END IF ;
|
|
END IF ;
|
|
|
|
IF NOT b_test
|
|
THEN
|
|
IF NOT NEW.lecteur IN (SELECT rolname FROM pg_catalog.pg_roles)
|
|
AND NOT NEW.lecteur = 'public'
|
|
-- si le lecteur désigné n'existe pas, on le crée
|
|
-- ou renvoie une erreur si les privilèges de l'utilisateur
|
|
-- sont insuffisants
|
|
THEN
|
|
IF createur IS NULL
|
|
THEN
|
|
RAISE EXCEPTION 'TA8. Opération interdite. Vous n''êtes pas habilité à créer le rôle %.', NEW.lecteur
|
|
USING HINT = 'Être membre d''un rôle disposant des attributs CREATEROLE et INHERIT est nécessaire pour créer de nouveaux éditeurs.' ;
|
|
END IF ;
|
|
EXECUTE 'SET ROLE ' || quote_ident(createur) ;
|
|
EXECUTE 'CREATE ROLE ' || quote_ident(NEW.lecteur) ;
|
|
RAISE NOTICE '... Le rôle de groupe % a été créé.', NEW.lecteur ;
|
|
EXECUTE 'SET ROLE ' || quote_ident(utilisateur) ;
|
|
END IF ;
|
|
|
|
-- mise à jour du champ d'OID du lecteur
|
|
IF NEW.lecteur = 'public'
|
|
THEN
|
|
UPDATE z_asgard.gestion_schema_etr
|
|
SET oid_lecteur = 0,
|
|
ctrl = ARRAY['SELF', 'x7-A;#rzo']
|
|
WHERE nom_schema = NEW.nom_schema AND (
|
|
oid_lecteur IS NULL
|
|
OR NOT oid_lecteur = 0
|
|
) ;
|
|
ELSE
|
|
UPDATE z_asgard.gestion_schema_etr
|
|
SET oid_lecteur = quote_ident(NEW.lecteur)::regrole::oid,
|
|
ctrl = ARRAY['SELF', 'x7-A;#rzo']
|
|
WHERE nom_schema = NEW.nom_schema AND (
|
|
oid_lecteur IS NULL
|
|
OR NOT oid_lecteur = quote_ident(NEW.lecteur)::regrole::oid
|
|
) ;
|
|
END IF ;
|
|
END IF ;
|
|
|
|
------ CREATION DU SCHEMA ------
|
|
-- on exclut au préalable les cas qui ne
|
|
-- correspondent pas à des créations, ainsi que les
|
|
-- remontées de l'event trigger sur CREATE SCHEMA,
|
|
-- car le schéma existe alors déjà
|
|
b_test := False ;
|
|
IF NOT NEW.creation OR NEW.ctrl[1] = 'CREATE'
|
|
THEN
|
|
b_test := True ;
|
|
ELSIF TG_OP = 'UPDATE'
|
|
THEN
|
|
IF OLD.creation
|
|
THEN
|
|
b_test := True ;
|
|
END IF ;
|
|
END IF ;
|
|
|
|
IF NOT b_test
|
|
THEN
|
|
-- le schéma est créé s'il n'existe pas déjà (cas d'ajout
|
|
-- d'un schéma pré-existant qui n'était pas référencé dans
|
|
-- gestion_schema jusque-là), sinon on alerte juste
|
|
-- l'utilisateur
|
|
IF NOT NEW.nom_schema IN (SELECT nspname FROM pg_catalog.pg_namespace)
|
|
THEN
|
|
IF NOT has_database_privilege(current_database(), 'CREATE')
|
|
OR NOT pg_has_role(NEW.producteur, 'USAGE')
|
|
THEN
|
|
-- si le rôle courant n'a pas les privilèges nécessaires pour
|
|
-- créer le schéma, on tente avec le rôle createur [de rôles]
|
|
-- pré-identifié, dont on sait au moins qu'il aura les
|
|
-- permissions nécessaires sur le rôle producteur - mais pas
|
|
-- s'il est habilité à créer des schémas
|
|
IF createur IS NOT NULL
|
|
THEN
|
|
EXECUTE 'SET ROLE ' || quote_ident(createur) ;
|
|
END IF ;
|
|
IF NOT has_database_privilege(current_database(), 'CREATE')
|
|
OR NOT pg_has_role(NEW.producteur, 'USAGE')
|
|
THEN
|
|
RAISE EXCEPTION 'TA9. Opération interdite. Vous n''êtes pas habilité à créer le schéma %.', NEW.nom_schema
|
|
USING HINT = 'Être membre d''un rôle disposant du privilège CREATE sur la base de données est nécessaire pour créer des schémas.' ;
|
|
END IF ;
|
|
END IF ;
|
|
EXECUTE 'CREATE SCHEMA ' || quote_ident(NEW.nom_schema) || ' AUTHORIZATION ' || quote_ident(NEW.producteur) ;
|
|
EXECUTE 'SET ROLE ' || quote_ident(utilisateur) ;
|
|
RAISE NOTICE '... Le schéma % a été créé.', NEW.nom_schema ;
|
|
ELSE
|
|
RAISE NOTICE '(schéma % pré-existant)', NEW.nom_schema ;
|
|
END IF ;
|
|
-- récupération de l'OID du schéma
|
|
UPDATE z_asgard.gestion_schema_etr
|
|
SET oid_schema = quote_ident(NEW.nom_schema)::regnamespace::oid,
|
|
ctrl = ARRAY['SELF', 'x7-A;#rzo']
|
|
WHERE nom_schema = NEW.nom_schema AND (
|
|
oid_schema IS NULL
|
|
OR NOT oid_schema = quote_ident(NEW.nom_schema)::regnamespace::oid
|
|
) ;
|
|
END IF ;
|
|
|
|
------ APPLICATION DES DROITS DU PRODUCTEUR ------
|
|
-- comme précédemment pour la préparation du producteur,
|
|
-- on ne s'intéresse pas aux cas :
|
|
-- - d'un schéma qui n'a pas/plus vocation à exister
|
|
-- (creation vaut False) ;
|
|
-- - d'un schéma pré-existant dont le producteur ne change pas
|
|
-- ou dont le libellé a juste été nettoyé par le trigger
|
|
-- BEFORE ;
|
|
-- - d'une remontée de l'event trigger asgard_on_create_schema,
|
|
-- car le producteur sera déjà propriétaire du schéma
|
|
-- et de son éventuel contenu. Par contre on garde les INSERT,
|
|
-- pour les cas de référencements ;
|
|
-- - de z_asgard_admin (pour permettre sa saisie initiale
|
|
-- dans la table de gestion, étant entendu qu'il est
|
|
-- impossible au trigger sur gestion_schema de lancer
|
|
-- un ALTER TABLE OWNER TO sur cette même table).
|
|
-- ils sont donc exclus au préalable
|
|
b_test := False ;
|
|
IF NOT NEW.creation
|
|
OR 'CLEAN producteur' = ANY(array_remove(NEW.ctrl, NULL))
|
|
OR NEW.ctrl[1] = 'CREATE'
|
|
OR NEW.nom_schema = 'z_asgard_admin'
|
|
THEN
|
|
b_test := True ;
|
|
ELSIF TG_OP = 'UPDATE'
|
|
THEN
|
|
IF OLD.creation AND NEW.producteur = OLD.producteur
|
|
THEN
|
|
b_test := True ;
|
|
END IF ;
|
|
END IF ;
|
|
|
|
IF NOT b_test
|
|
THEN
|
|
-- si besoin, on bascule sur le rôle createur. À ce stade,
|
|
-- il est garanti que soit l'utilisateur courant soit
|
|
-- createur (pour le cas d'un utilisateur courant
|
|
-- NOINHERIT) aura les privilèges nécessaires
|
|
IF NOT pg_has_role(NEW.producteur, 'USAGE')
|
|
THEN
|
|
EXECUTE 'SET ROLE ' || quote_ident(createur) ;
|
|
ELSIF TG_OP = 'UPDATE'
|
|
THEN
|
|
IF NOT pg_has_role(a_producteur, 'USAGE')
|
|
THEN
|
|
EXECUTE 'SET ROLE ' || quote_ident(createur) ;
|
|
END IF ;
|
|
END IF ;
|
|
|
|
-- changements de propriétaires
|
|
IF (NEW.nom_schema, NEW.producteur)
|
|
IN (SELECT schema_name, schema_owner FROM information_schema.schemata)
|
|
THEN
|
|
-- si producteur est déjà propriétaire du schéma (cas d'une remontée de l'event trigger,
|
|
-- principalement), on ne change que les propriétaires des objets éventuels
|
|
IF quote_ident(NEW.nom_schema)::regnamespace::oid
|
|
IN (SELECT refobjid FROM pg_catalog.pg_depend WHERE deptype = 'n')
|
|
THEN
|
|
-- la commande n'est cependant lancée que s'il existe des dépendances de type
|
|
-- DEPENDENCY_NORMAL sur le schéma, ce qui est une condition nécessaire à
|
|
-- l'existence d'objets dans le schéma
|
|
RAISE NOTICE 'attribution de la propriété des objets au rôle producteur du schéma % :', NEW.nom_schema ;
|
|
SELECT z_asgard.asgard_admin_proprietaire(NEW.nom_schema, NEW.producteur, False)
|
|
INTO n ;
|
|
IF n = 0
|
|
THEN
|
|
RAISE NOTICE '> néant' ;
|
|
END IF ;
|
|
END IF ;
|
|
ELSE
|
|
-- sinon schéma + objets
|
|
RAISE NOTICE 'attribution de la propriété du schéma et des objets au rôle producteur du schéma % :', NEW.nom_schema ;
|
|
PERFORM z_asgard.asgard_admin_proprietaire(NEW.nom_schema, NEW.producteur) ;
|
|
END IF ;
|
|
|
|
EXECUTE 'SET ROLE ' || quote_ident(utilisateur) ;
|
|
END IF ;
|
|
|
|
------ APPLICATION DES DROITS DE L'EDITEUR ------
|
|
-- on ne s'intéresse pas aux cas :
|
|
-- - d'un schéma qui n'a pas/plus vocation à exister ;
|
|
-- - d'un schéma pré-existant dont l'éditeur ne change pas
|
|
-- (y compris pour rester vide) ou dont le libellé
|
|
-- a seulement été nettoyé par le trigger BEFORE.
|
|
-- ils sont donc exclus au préalable
|
|
b_test := False ;
|
|
IF NOT NEW.creation OR 'CLEAN editeur' = ANY(array_remove(NEW.ctrl, NULL))
|
|
THEN
|
|
b_test := True ;
|
|
ELSIF TG_OP = 'UPDATE'
|
|
THEN
|
|
IF OLD.creation
|
|
AND coalesce(NEW.editeur, '') = coalesce(OLD.editeur, '')
|
|
THEN
|
|
b_test := True ;
|
|
END IF ;
|
|
END IF ;
|
|
|
|
IF NOT b_test
|
|
THEN
|
|
-- si besoin, on bascule sur le rôle createur. À ce stade,
|
|
-- il est garanti que soit l'utilisateur courant soit
|
|
-- createur (pour le cas d'un utilisateur courant
|
|
-- NOINHERIT) aura les privilèges nécessaires
|
|
IF NOT pg_has_role(NEW.producteur, 'USAGE')
|
|
THEN
|
|
EXECUTE 'SET ROLE ' || quote_ident(createur) ;
|
|
END IF ;
|
|
|
|
IF TG_OP = 'UPDATE'
|
|
THEN
|
|
-- la validité de OLD.editeur n'ayant
|
|
-- pas été contrôlée par le trigger BEFORE,
|
|
-- on le fait maintenant
|
|
IF OLD.editeur = 'public'
|
|
THEN
|
|
a_editeur := 'public' ;
|
|
-- récupération des modifications manuelles des
|
|
-- droits de OLD.editeur/public, grâce à la fonction
|
|
-- asgard_synthese_public
|
|
SELECT array_agg(commande) INTO l_commande
|
|
FROM z_asgard.asgard_synthese_public(
|
|
quote_ident(NEW.nom_schema)::regnamespace
|
|
) ;
|
|
ELSE
|
|
SELECT rolname INTO a_editeur
|
|
FROM pg_catalog.pg_roles
|
|
WHERE pg_roles.oid = OLD.oid_editeur ;
|
|
IF FOUND
|
|
THEN
|
|
-- récupération des modifications manuelles des
|
|
-- droits de OLD.editeur, grâce à la fonction
|
|
-- asgard_synthese_role
|
|
SELECT array_agg(commande) INTO l_commande
|
|
FROM z_asgard.asgard_synthese_role(
|
|
quote_ident(NEW.nom_schema)::regnamespace,
|
|
quote_ident(a_editeur)::regrole
|
|
) ;
|
|
END IF ;
|
|
END IF ;
|
|
END IF ;
|
|
|
|
IF l_commande IS NOT NULL
|
|
-- transfert sur NEW.editeur des droits de
|
|
-- OLD.editeur, le cas échéant
|
|
THEN
|
|
IF NEW.editeur IS NOT NULL
|
|
THEN
|
|
RAISE NOTICE 'suppression et transfert vers le nouvel éditeur des privilèges de l''ancien éditeur du schéma % :', NEW.nom_schema ;
|
|
ELSE
|
|
RAISE NOTICE 'suppression des privilèges de l''ancien éditeur du schéma % :', NEW.nom_schema ;
|
|
END IF ;
|
|
FOREACH c IN ARRAY l_commande
|
|
LOOP
|
|
IF NEW.editeur IS NOT NULL
|
|
THEN
|
|
EXECUTE format(c, NEW.editeur) ;
|
|
RAISE NOTICE '> %', format(c, NEW.editeur) ;
|
|
END IF ;
|
|
IF c ~ '^GRANT'
|
|
THEN
|
|
SELECT z_asgard.asgard_grant_to_revoke(c) INTO c_reverse ;
|
|
EXECUTE format(c_reverse, a_editeur) ;
|
|
RAISE NOTICE '> %', format(c_reverse, a_editeur) ;
|
|
END IF ;
|
|
END LOOP ;
|
|
|
|
-- sinon, application des privilèges standards de l'éditeur
|
|
ELSIF NEW.editeur IS NOT NULL
|
|
THEN
|
|
RAISE NOTICE 'application des privilèges standards pour le rôle éditeur du schéma % :', NEW.nom_schema ;
|
|
|
|
EXECUTE 'GRANT USAGE ON SCHEMA ' || quote_ident(NEW.nom_schema) || ' TO ' || quote_ident(NEW.editeur) ;
|
|
RAISE NOTICE '> %', 'GRANT USAGE ON SCHEMA ' || quote_ident(NEW.nom_schema) || ' TO ' || quote_ident(NEW.editeur) ;
|
|
|
|
EXECUTE 'GRANT SELECT, UPDATE, DELETE, INSERT ON ALL TABLES IN SCHEMA ' || quote_ident(NEW.nom_schema) || ' TO ' || quote_ident(NEW.editeur) ;
|
|
RAISE NOTICE '> %', 'GRANT SELECT, UPDATE, DELETE, INSERT ON ALL TABLES IN SCHEMA ' || quote_ident(NEW.nom_schema) || ' TO ' || quote_ident(NEW.editeur) ;
|
|
|
|
EXECUTE 'GRANT SELECT, USAGE ON ALL SEQUENCES IN SCHEMA ' || quote_ident(NEW.nom_schema) || ' TO ' || quote_ident(NEW.editeur) ;
|
|
RAISE NOTICE '> %', 'GRANT SELECT, USAGE ON ALL SEQUENCES IN SCHEMA ' || quote_ident(NEW.nom_schema) || ' TO ' || quote_ident(NEW.editeur) ;
|
|
|
|
END IF ;
|
|
|
|
EXECUTE 'SET ROLE ' || quote_ident(utilisateur) ;
|
|
END IF ;
|
|
|
|
------ APPLICATION DES DROITS DU LECTEUR ------
|
|
-- on ne s'intéresse pas aux cas :
|
|
-- - d'un schéma qui n'a pas/plus vocation à exister ;
|
|
-- - d'un schéma pré-existant dont le lecteur ne change pas
|
|
-- (y compris pour rester vide) ou dont le libellé
|
|
-- a seulement été nettoyé par le trigger BEFORE.
|
|
-- ils sont donc exclus au préalable
|
|
b_test := False ;
|
|
l_commande := NULL ;
|
|
IF NOT NEW.creation OR 'CLEAN lecteur' = ANY(array_remove(NEW.ctrl, NULL))
|
|
THEN
|
|
b_test := True ;
|
|
ELSIF TG_OP = 'UPDATE'
|
|
THEN
|
|
IF OLD.creation
|
|
AND coalesce(NEW.lecteur, '') = coalesce(OLD.lecteur, '')
|
|
THEN
|
|
b_test := True ;
|
|
END IF ;
|
|
END IF ;
|
|
|
|
IF NOT b_test
|
|
THEN
|
|
-- si besoin, on bascule sur le rôle createur. À ce stade,
|
|
-- il est garanti que soit l'utilisateur courant soit
|
|
-- createur (pour le cas d'un utilisateur courant
|
|
-- NOINHERIT) aura les privilèges nécessaires
|
|
IF NOT pg_has_role(NEW.producteur, 'USAGE')
|
|
THEN
|
|
EXECUTE 'SET ROLE ' || quote_ident(createur) ;
|
|
END IF ;
|
|
|
|
IF TG_OP = 'UPDATE'
|
|
THEN
|
|
-- la validité de OLD.lecteur n'ayant
|
|
-- pas été contrôlée par le trigger BEFORE,
|
|
-- on le fait maintenant
|
|
IF OLD.lecteur = 'public'
|
|
THEN
|
|
a_lecteur := 'public' ;
|
|
-- récupération des modifications manuelles des
|
|
-- droits de OLD.lecteur/public, grâce à la fonction
|
|
-- asgard_synthese_public
|
|
SELECT array_agg(commande) INTO l_commande
|
|
FROM z_asgard.asgard_synthese_public(
|
|
quote_ident(NEW.nom_schema)::regnamespace
|
|
) ;
|
|
ELSE
|
|
SELECT rolname INTO a_lecteur
|
|
FROM pg_catalog.pg_roles
|
|
WHERE pg_roles.oid = OLD.oid_lecteur ;
|
|
IF FOUND
|
|
THEN
|
|
-- récupération des modifications manuelles des
|
|
-- droits de OLD.lecteur, grâce à la fonction
|
|
-- asgard_synthese_role
|
|
SELECT array_agg(commande) INTO l_commande
|
|
FROM z_asgard.asgard_synthese_role(
|
|
quote_ident(NEW.nom_schema)::regnamespace,
|
|
quote_ident(a_lecteur)::regrole
|
|
) ;
|
|
END IF ;
|
|
END IF ;
|
|
END IF ;
|
|
|
|
IF l_commande IS NOT NULL
|
|
-- transfert sur NEW.lecteur des droits de
|
|
-- OLD.lecteur, le cas échéant
|
|
THEN
|
|
IF NEW.lecteur IS NOT NULL
|
|
THEN
|
|
RAISE NOTICE 'suppression et transfert vers le nouveau lecteur des privilèges de l''ancien lecteur du schéma % :', NEW.nom_schema ;
|
|
ELSE
|
|
RAISE NOTICE 'suppression des privilèges de l''ancien lecteur du schéma % :', NEW.nom_schema ;
|
|
END IF ;
|
|
FOREACH c IN ARRAY l_commande
|
|
LOOP
|
|
IF NEW.lecteur IS NOT NULL
|
|
THEN
|
|
EXECUTE format(c, NEW.lecteur) ;
|
|
RAISE NOTICE '> %', format(c, NEW.lecteur) ;
|
|
END IF ;
|
|
IF c ~ '^GRANT'
|
|
THEN
|
|
SELECT z_asgard.asgard_grant_to_revoke(c) INTO c_reverse ;
|
|
EXECUTE format(c_reverse, a_lecteur) ;
|
|
RAISE NOTICE '> %', format(c_reverse, a_lecteur) ;
|
|
END IF ;
|
|
END LOOP ;
|
|
|
|
-- sinon, application des privilèges standards du lecteur
|
|
ELSIF NEW.lecteur IS NOT NULL
|
|
THEN
|
|
RAISE NOTICE 'application des privilèges standards pour le rôle lecteur du schéma % :', NEW.nom_schema ;
|
|
|
|
EXECUTE 'GRANT USAGE ON SCHEMA ' || quote_ident(NEW.nom_schema) || ' TO ' || quote_ident(NEW.lecteur) ;
|
|
RAISE NOTICE '> %', 'GRANT USAGE ON SCHEMA ' || quote_ident(NEW.nom_schema) || ' TO ' || quote_ident(NEW.lecteur) ;
|
|
|
|
EXECUTE 'GRANT SELECT ON ALL TABLES IN SCHEMA ' || quote_ident(NEW.nom_schema) || ' TO ' || quote_ident(NEW.lecteur) ;
|
|
RAISE NOTICE '> %', 'GRANT SELECT ON ALL TABLES IN SCHEMA ' || quote_ident(NEW.nom_schema) || ' TO ' || quote_ident(NEW.lecteur) ;
|
|
|
|
EXECUTE 'GRANT SELECT ON ALL SEQUENCES IN SCHEMA ' || quote_ident(NEW.nom_schema) || ' TO ' || quote_ident(NEW.lecteur) ;
|
|
RAISE NOTICE '> %', 'GRANT SELECT ON ALL SEQUENCES IN SCHEMA ' || quote_ident(NEW.nom_schema) || ' TO ' || quote_ident(NEW.lecteur) ;
|
|
|
|
END IF ;
|
|
|
|
EXECUTE 'SET ROLE ' || quote_ident(utilisateur) ;
|
|
END IF ;
|
|
|
|
RETURN NULL ;
|
|
|
|
EXCEPTION WHEN OTHERS THEN
|
|
GET STACKED DIAGNOSTICS e_mssg = MESSAGE_TEXT,
|
|
e_hint = PG_EXCEPTION_HINT,
|
|
e_detl = PG_EXCEPTION_DETAIL ;
|
|
RAISE EXCEPTION 'TA0 > %', e_mssg
|
|
USING DETAIL = e_detl,
|
|
HINT = e_hint ;
|
|
|
|
END
|
|
$BODY$ ;
|
|
|
|
ALTER FUNCTION z_asgard_admin.asgard_on_modify_gestion_schema_after()
|
|
OWNER TO g_admin ;
|
|
|
|
COMMENT ON FUNCTION z_asgard_admin.asgard_on_modify_gestion_schema_after() IS 'ASGARD. Fonction appelée par le trigger qui répercute physiquement les modifications de la table de gestion.' ;
|
|
|
|
|
|
-- Trigger: asgard_on_modify_gestion_schema_after
|
|
|
|
CREATE TRIGGER asgard_on_modify_gestion_schema_after
|
|
AFTER INSERT OR UPDATE
|
|
ON z_asgard_admin.gestion_schema
|
|
FOR EACH ROW
|
|
EXECUTE PROCEDURE z_asgard_admin.asgard_on_modify_gestion_schema_after();
|
|
|
|
COMMENT ON TRIGGER asgard_on_modify_gestion_schema_after ON z_asgard_admin.gestion_schema IS 'ASGARD. Trigger qui répercute physiquement les modifications de la table de gestion.' ;
|