-- v0 2020.05.07 AF -- v1 2020.05.15 LL -- v2 2020.05.19 LL -- v3 2020.05.25 LL -- v4 2020.05.27 LL -- v5 2020.05.28 LL -- v6 2020.05.30 LL -- v7 2020.06.05/19 LL (asgard 0.7.0) -- v8 2020.06.26/29 LL (asgard 0.7.1) -- v9 2020.08.11 LL (asgard 1.0.0) -- v10 2020.08.30 LL (asgard 1.0.0) /* - fonction d'import de la nomenclature : nomenclature explicite, import pour les schémas existants dans la base, fonctionnalité de mise à jour. - simplification de la structuration des remontées d'erreurs pour asgard_initialisation_gestion_schema, asgard_sortie_gestion_schema, asgard_nettoyage_roles, asgard_import_nomenclature et asgard_initialise_schema (ajout d'un contrôle d'erreurs). - ajout d'un contrôle des privilèges sur l'objet dans asgard_admin_proprietaire. - ajout d'une fonction de réinitialisation des droits sur l'ensemble des schémas référencés ; - sécurisation de la fonction asgard_initialise_schema, qui, lorsqu'elle est appliquées aux schémas d'ASGARD, n'efface plus les privilèges essentiels au fonctionnement d'ASGARD. */ -- MOT DE PASSE DE CONTRÔLE : 'x7-A;#rzo' -- peut être modifié sans risque par rechercher/remplacer --------------------------------------- ------ 4 - FONCTIONS UTILITAIRES ------ --------------------------------------- /* 4.1 - LISTES DES DROITS SUR LES OBJETS D'UN SCHEMA 4.2 - LISTE DES DROITS SUR UN OBJET 4.3 - MODIFICATION DU PROPRIETAIRE D'UN SCHEMA ET SON CONTENU 4.4 - TRANSFORMATION GRANT EN REVOKE 4.5 - INITIALISATION DE GESTION_SCHEMA 4.6 - DEREFERENCEMENT D'UN SCHEMA 4.7 - NETTOYAGE DES RÔLES 4.8 - REINITIALISATION DES PRIVILEGES SUR UN SCHEMA 4.9 - REINITIALISATION DES PRIVILEGES SUR UN OBJET 4.10 - DEPLACEMENT D'OBJET 4.11 - OCTROI D'UN RÔLE À TOUS LES RÔLES DE CONNEXION 4.12 - IMPORT DE LA NOMENCLATURE DANS GESTION_SCHEMA 4.13 - REAFFECTATION DES PRIVILEGES D'UN RÔLE 4.14 - REINITIALISATION DES PRIVILEGES SUR TOUS LES SCHEMAS */ ------ 4.1 - LISTES DES DROITS SUR LES OBJETS D'UN SCHEMA ------ -- FUNCTION: z_asgard.asgard_synthese_role(regnamespace, regrole) CREATE OR REPLACE FUNCTION z_asgard.asgard_synthese_role(n_schema regnamespace, n_role regrole) RETURNS TABLE(commande text) LANGUAGE plpgsql AS $_$ /* OBJET : Cette fonction renvoie une table contenant une liste de commandes GRANT et REVOKE permettant de recréer les droits de "role_1" sur les objets du schéma "schema" (et le schéma lui-même). ARGUMENTS : - "schema" est un nom de schéma valide, casté en regnamespace ; - "role_1" est un nom de rôle valide, casté en regrole. SORTIE : Une table avec un unique champ nommé "commande". */ DECLARE n_role_trans text := n_role::text ; BEGIN ------ SCHEMAS ------ -- privilèges attribués (hors propriétaire) : RETURN QUERY WITH t_acl AS ( SELECT unnest(nspacl)::text AS acl FROM pg_catalog.pg_namespace WHERE oid = n_schema::oid AND nspacl IS NOT NULL AND NOT n_role::oid = nspowner ) SELECT 'GRANT ' || privilege || ' ON SCHEMA ' || n_schema::text || ' TO %I' FROM t_acl, unnest(ARRAY['USAGE', 'CREATE'], ARRAY['U', 'C']) AS l (privilege, prvlg) WHERE acl ~ ('^' || n_role_trans || '[=].*' || prvlg || '.*[/]') ; -- privilèges révoqués du propriétaire : RETURN QUERY WITH t_acl AS ( SELECT oid, unnest(CASE WHEN nspacl::text[] = ARRAY[]::text[] OR NOT array_to_string(nspacl, ',') ~ ('^' || n_role_trans || '[=]') AND NOT array_to_string(nspacl, ',') ~ ('[,]' || n_role_trans || '[=]') THEN ARRAY[NULL]::text[] ELSE nspacl::text[] END) AS acl FROM pg_catalog.pg_namespace WHERE oid = n_schema::oid AND n_role::oid = nspowner AND nspacl IS NOT NULL ) SELECT 'REVOKE ' || privilege || ' ON SCHEMA ' || n_schema::text || ' FROM %I' FROM t_acl, unnest(ARRAY['USAGE', 'CREATE'], ARRAY['U', 'C']) AS l (privilege, prvlg) WHERE (acl ~ ('^' || n_role_trans || '[=]') AND NOT acl ~ ( '[=].*' || prvlg || '.*[/]')) OR acl IS NULL ; ------ TABLES ------ -- inclut les vues, vues matérialisées, tables étrangères et partitions -- privilèges attribués (hors propriétaire) : RETURN QUERY WITH t_acl AS ( SELECT oid, unnest(relacl)::text AS acl FROM pg_catalog.pg_class WHERE relnamespace = n_schema AND relkind IN ('r', 'v', 'm', 'f', 'p') AND relacl IS NOT NULL AND NOT n_role::oid = relowner ) SELECT 'GRANT ' || privilege || ' ON TABLE ' || oid::regclass::text || ' TO %I' FROM t_acl, unnest(ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], ARRAY['r', 'a', 'w', 'd', 'D', 'x', 't']) AS l (privilege, prvlg) WHERE acl ~ ('^' || n_role_trans || '[=].*' || prvlg || '.*[/]') ; -- privilèges révoqués du propriétaire : RETURN QUERY WITH t_acl AS ( SELECT oid, unnest(CASE WHEN relacl::text[] = ARRAY[]::text[] OR NOT array_to_string(relacl, ',') ~ ('^' || n_role_trans || '[=]') AND NOT array_to_string(relacl, ',') ~ ('[,]' || n_role_trans || '[=]') THEN ARRAY[NULL]::text[] ELSE relacl::text[] END) AS acl FROM pg_catalog.pg_class WHERE relnamespace = n_schema AND relacl IS NOT NULL AND relkind IN ('r', 'v', 'm', 'f', 'p') AND n_role::oid = relowner ) SELECT 'REVOKE ' || privilege || ' ON TABLE ' || oid::regclass::text || ' FROM %I' FROM t_acl, unnest(ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], ARRAY['r', 'a', 'w', 'd', 'D', 'x', 't']) AS l (privilege, prvlg) WHERE (acl ~ ('^' || n_role_trans || '[=]') AND NOT acl ~ ( '[=].*' || prvlg || '.*[/]')) OR acl IS NULL ; ------ SEQUENCES ------ -- privilèges attribués (hors propriétaire) : RETURN QUERY WITH t_acl AS ( SELECT oid, unnest(relacl)::text AS acl FROM pg_catalog.pg_class WHERE relnamespace = n_schema AND relkind = 'S' AND relacl IS NOT NULL AND NOT n_role::oid = relowner ) SELECT 'GRANT ' || privilege || ' ON SEQUENCE ' || oid::regclass::text || ' TO %I' FROM t_acl, unnest(ARRAY['SELECT', 'USAGE', 'UPDATE'], ARRAY['r', 'U', 'w']) AS l (privilege, prvlg) WHERE acl ~ ('^' || n_role_trans || '[=].*' || prvlg || '.*[/]') ; -- privilèges révoqués du propriétaire : RETURN QUERY WITH t_acl AS ( SELECT oid, unnest(CASE WHEN relacl::text[] = ARRAY[]::text[] OR NOT array_to_string(relacl, ',') ~ ('^' || n_role_trans || '[=]') AND NOT array_to_string(relacl, ',') ~ ('[,]' || n_role_trans || '[=]') THEN ARRAY[NULL]::text[] ELSE relacl::text[] END) AS acl FROM pg_catalog.pg_class WHERE relnamespace = n_schema AND relacl IS NOT NULL AND relkind = 'S' AND n_role::oid = relowner ) SELECT 'REVOKE ' || privilege || ' ON SEQUENCE ' || oid::regclass::text || ' FROM %I' FROM t_acl, unnest(ARRAY['SELECT', 'USAGE', 'UPDATE'], ARRAY['r', 'U', 'w']) AS l (privilege, prvlg) WHERE (acl ~ ('^' || n_role_trans || '[=]') AND NOT acl ~ ( '[=].*' || prvlg || '.*[/]')) OR acl IS NULL ; ------ COLONNES ------ -- privilèges attribués : RETURN QUERY WITH t_acl AS ( SELECT attname, attrelid, unnest(attacl)::text AS acl FROM pg_catalog.pg_class JOIN pg_catalog.pg_attribute ON pg_class.oid = pg_attribute.attrelid WHERE relnamespace = n_schema AND attacl IS NOT NULL ) SELECT 'GRANT ' || privilege || ' (' || attname::text || ') ON TABLE ' || attrelid::regclass::text || ' TO %I' FROM t_acl, unnest(ARRAY['SELECT', 'INSERT', 'UPDATE', 'REFERENCES'], ARRAY['r', 'a', 'w', 'x']) AS l (privilege, prvlg) WHERE acl ~ ('^' || n_role_trans || '[=].*' || prvlg || '.*[/]') ; ------ FONCTIONS ------ -- inclut les fonctions d'agrégation -- privilèges attribués (hors propriétaire) : RETURN QUERY WITH t_acl AS ( SELECT oid, unnest(proacl)::text AS acl FROM pg_catalog.pg_proc WHERE pronamespace = n_schema AND proacl IS NOT NULL AND NOT n_role::oid = proowner ) SELECT 'GRANT ' || privilege || ' ON FUNCTION ' || oid::regprocedure::text || ' TO %I' FROM t_acl, unnest(ARRAY['EXECUTE'], ARRAY['X']) AS l (privilege, prvlg) WHERE acl ~ ('^' || n_role_trans || '[=].*' || prvlg || '.*[/]') ; -- privilèges révoqués du propriétaire : RETURN QUERY WITH t_acl AS ( SELECT oid, unnest(CASE WHEN proacl::text[] = ARRAY[]::text[] OR NOT array_to_string(proacl, ',') ~ ('^' || n_role_trans || '[=]') AND NOT array_to_string(proacl, ',') ~ ('[,]' || n_role_trans || '[=]') THEN ARRAY[NULL]::text[] ELSE proacl::text[] END) AS acl FROM pg_catalog.pg_proc WHERE pronamespace = n_schema AND n_role::oid = proowner AND proacl IS NOT NULL ) SELECT 'REVOKE ' || privilege || ' ON FUNCTION ' || oid::regprocedure::text || ' FROM %I' FROM t_acl, unnest(ARRAY['EXECUTE'], ARRAY['X']) AS l (privilege, prvlg) WHERE (acl ~ ('^' || n_role_trans || '[=]') AND NOT acl ~ ( '[=].*' || prvlg || '.*[/]')) OR acl IS NULL ; ------ TYPES ------ -- inclut les domaines -- privilèges attribués (hors propriétaire) : RETURN QUERY WITH t_acl AS ( SELECT oid, unnest(typacl)::text AS acl FROM pg_catalog.pg_type WHERE typnamespace = n_schema AND typacl IS NOT NULL AND NOT n_role::oid = typowner ) SELECT 'GRANT ' || privilege || ' ON TYPE ' || oid::regtype::text || ' TO %I' FROM t_acl, unnest(ARRAY['USAGE'], ARRAY['U']) AS l (privilege, prvlg) WHERE acl ~ ('^' || n_role_trans || '[=].*' || prvlg || '.*[/]') ; -- privilèges révoqués du propriétaire : RETURN QUERY WITH t_acl AS ( SELECT oid, unnest(CASE WHEN typacl::text[] = ARRAY[]::text[] OR NOT array_to_string(typacl, ',') ~ ('^' || n_role_trans || '[=]') AND NOT array_to_string(typacl, ',') ~ ('[,]' || n_role_trans || '[=]') THEN ARRAY[NULL]::text[] ELSE typacl::text[] END) AS acl FROM pg_catalog.pg_type WHERE typnamespace = n_schema AND n_role::oid = typowner AND typacl IS NOT NULL ) SELECT 'REVOKE ' || privilege || ' ON TYPE ' || oid::regtype::text || ' FROM %I' FROM t_acl, unnest(ARRAY['USAGE'], ARRAY['U']) AS l (privilege, prvlg) WHERE (acl ~ ('^' || n_role_trans || '[=]') AND NOT acl ~ ( '[=].*' || prvlg || '.*[/]')) OR acl IS NULL ; END $_$; ALTER FUNCTION z_asgard.asgard_synthese_role(regnamespace, regrole) OWNER TO g_admin_ext ; COMMENT ON FUNCTION z_asgard.asgard_synthese_role(regnamespace, regrole) IS 'ASGARD. Fonction qui liste les commandes permettant de reproduire les droits d''un rôle sur les objets d''un schéma.' ; -- FUNCTION: z_asgard.asgard_synthese_public(regnamespace) CREATE OR REPLACE FUNCTION z_asgard.asgard_synthese_public(n_schema regnamespace) RETURNS TABLE(commande text) LANGUAGE plpgsql AS $_$ /* OBJET : Cette fonction renvoie une table contenant une liste de commandes GRANT et REVOKE permettant de recréer les droits de public sur les objets du schéma "schema" (et le schéma lui-même). REMARQUE : La fonction ne s'intéresse pas aux objets de type fonction (dont agrégats) et type (dont domaines), sur lesquels public reçoit des droits par défaut qu'il n'est pas judicieux de reproduire sur un autre rôle, ni de révoquer lors d'un changement de lecteur/éditeur. Si des privilèges par défaut ont été révoqués pour public, la révocation restera valable pour les futurs lecteur/éditeurs puisqu'il n'y a pas d'attribution de privilèges supplémentaires pour les lecteurs/éditeurs sur ces objets. ARGUMENT : "schema" est un nom de schéma valide, casté en regnamespace. SORTIE : Une table avec un unique champ nommé "commande". */ BEGIN ------ SCHEMAS ------ RETURN QUERY WITH t_acl AS ( SELECT unnest(nspacl)::text AS acl FROM pg_catalog.pg_namespace WHERE oid = n_schema::oid AND nspacl IS NOT NULL ) SELECT 'GRANT ' || privilege || ' ON SCHEMA ' || n_schema::text || ' TO %I' FROM t_acl, unnest(ARRAY['USAGE', 'CREATE'], ARRAY['U', 'C']) AS l (privilege, prvlg) WHERE acl ~ ('^[=].*' || prvlg || '.*[/]') ; ------ TABLES ------ -- inclut les vues, vues matérialisées, tables étrangères et partitions RETURN QUERY WITH t_acl AS ( SELECT oid, unnest(relacl)::text AS acl FROM pg_catalog.pg_class WHERE relnamespace = n_schema AND relkind IN ('r', 'v', 'm', 'f', 'p') AND relacl IS NOT NULL ) SELECT 'GRANT ' || privilege || ' ON TABLE ' || oid::regclass::text || ' TO %I' FROM t_acl, unnest(ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], ARRAY['r', 'a', 'w', 'd', 'D', 'x', 't']) AS l (privilege, prvlg) WHERE acl ~ ('^[=].*' || prvlg || '.*[/]') ; ------ SEQUENCES ------ RETURN QUERY WITH t_acl AS ( SELECT oid, unnest(relacl)::text AS acl FROM pg_catalog.pg_class WHERE relnamespace = n_schema AND relkind = 'S' AND relacl IS NOT NULL ) SELECT 'GRANT ' || privilege || ' ON SEQUENCE ' || oid::regclass::text || ' TO %I' FROM t_acl, unnest(ARRAY['SELECT', 'USAGE', 'UPDATE'], ARRAY['r', 'U', 'w']) AS l (privilege, prvlg) WHERE acl ~ ('^[=].*' || prvlg || '.*[/]') ; ------ COLONNES ------ RETURN QUERY WITH t_acl AS ( SELECT attname, attrelid, unnest(attacl)::text AS acl FROM pg_catalog.pg_class JOIN pg_catalog.pg_attribute ON pg_class.oid = pg_attribute.attrelid WHERE relnamespace = n_schema AND attacl IS NOT NULL ) SELECT 'GRANT ' || privilege || ' (' || attname::text || ') ON TABLE ' || attrelid::regclass::text || ' TO %I' FROM t_acl, unnest(ARRAY['SELECT', 'INSERT', 'UPDATE', 'REFERENCES'], ARRAY['r', 'a', 'w', 'x']) AS l (privilege, prvlg) WHERE acl ~ ('^[=].*' || prvlg || '.*[/]') ; END $_$; ALTER FUNCTION z_asgard.asgard_synthese_public(regnamespace) OWNER TO g_admin_ext ; COMMENT ON FUNCTION z_asgard.asgard_synthese_public(regnamespace) IS 'ASGARD. Fonction qui liste les commandes permettant de reproduire les droits de public sur les objets d''un schéma.' ; ------ 4.2 - LISTE DES DROITS SUR UN OBJET ------ -- FUNCTION: z_asgard.asgard_synthese_role_obj(oid, text, regrole) CREATE OR REPLACE FUNCTION z_asgard.asgard_synthese_role_obj(obj_oid oid, obj_type text, n_role regrole) RETURNS TABLE(commande text) LANGUAGE plpgsql AS $_$ /* OBJET : Cette fonction renvoie une table contenant une liste de commandes GRANT et REVOKE permettant de recréer les droits de "role_1" sur un objet de type table, table étrangère, partition de table, vue, vue matérialisée, séquence, fonction (dont fonctions d'agrégations), type (dont domaines). ARGUMENTS : - "obj_oid" est l'identifiant interne de l'objet ; - "obj_type" est le type de l'objet au format text ('table', 'view', 'materialized view', 'sequence', 'function', 'type', 'domain', 'foreign table', 'partitioned table', 'aggregate') ; - "role_1" est un nom de rôle valide, casté en regrole. SORTIE : Une table avec un unique champ nommé "commande". */ DECLARE n_role_trans text := n_role::text ; BEGIN ------ TABLE, VUE, VUE MATERIALISEE ------ IF obj_type IN ('table', 'view', 'materialized view', 'foreign table', 'partitioned table') THEN -- privilèges attribués (si n_role n'est pas le propriétaire de l'objet) : RETURN QUERY WITH t_acl AS ( SELECT oid, unnest(relacl)::text AS acl FROM pg_catalog.pg_class WHERE oid = obj_oid AND relacl IS NOT NULL AND NOT n_role::oid = relowner ) SELECT 'GRANT ' || privilege || ' ON TABLE ' || oid::regclass::text || ' TO %I' FROM t_acl, unnest(ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], ARRAY['r', 'a', 'w', 'd', 'D', 'x', 't']) AS l (privilege, prvlg) WHERE acl ~ ('^' || n_role_trans || '[=].*' || prvlg || '.*[/]') ; -- privilèges révoqués du propriétaire (si n_role est le propriétaire de l'objet) : RETURN QUERY WITH t_acl AS ( SELECT oid, unnest(CASE WHEN relacl::text[] = ARRAY[]::text[] OR NOT array_to_string(relacl, ',') ~ ('^' || n_role_trans || '[=]') AND NOT array_to_string(relacl, ',') ~ ('[,]' || n_role_trans || '[=]') THEN ARRAY[NULL]::text[] ELSE relacl::text[] END) AS acl FROM pg_catalog.pg_class WHERE oid = obj_oid AND relacl IS NOT NULL AND n_role::oid = relowner ) SELECT 'REVOKE ' || privilege || ' ON TABLE ' || oid::regclass::text || ' FROM %I' FROM t_acl, unnest(ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], ARRAY['r', 'a', 'w', 'd', 'D', 'x', 't']) AS l (privilege, prvlg) WHERE (acl ~ ('^' || n_role_trans || '[=]') AND NOT acl ~ ( '[=].*' || prvlg || '.*[/]')) OR acl IS NULL ; ------ COLONNES ------ -- privilèges attribués : RETURN QUERY WITH t_acl AS ( SELECT attname, attrelid, unnest(attacl)::text AS acl FROM pg_catalog.pg_attribute WHERE pg_attribute.attrelid = obj_oid AND attacl IS NOT NULL ) SELECT 'GRANT ' || privilege || ' (' || attname::text || ') ON TABLE ' || attrelid::regclass::text || ' TO %I' FROM t_acl, unnest(ARRAY['SELECT', 'INSERT', 'UPDATE', 'REFERENCES'], ARRAY['r', 'a', 'w', 'x']) AS l (privilege, prvlg) WHERE acl ~ ('^' || n_role_trans || '[=].*' || prvlg || '.*[/]') ; ------ SEQUENCES ------ ELSIF obj_type = 'sequence' THEN -- privilèges attribués (si n_role n'est pas le propriétaire de l'objet) : RETURN QUERY WITH t_acl AS ( SELECT oid, unnest(relacl)::text AS acl FROM pg_catalog.pg_class WHERE oid = obj_oid AND relacl IS NOT NULL AND NOT n_role::oid = relowner ) SELECT 'GRANT ' || privilege || ' ON SEQUENCE ' || oid::regclass::text || ' TO %I' FROM t_acl, unnest(ARRAY['SELECT', 'USAGE', 'UPDATE'], ARRAY['r', 'U', 'w']) AS l (privilege, prvlg) WHERE acl ~ ('^' || n_role_trans || '[=].*' || prvlg || '.*[/]') ; -- privilèges révoqués du propriétaire (si n_role est le propriétaire de l'objet) : RETURN QUERY WITH t_acl AS ( SELECT oid, unnest(CASE WHEN relacl::text[] = ARRAY[]::text[] OR NOT array_to_string(relacl, ',') ~ ('^' || n_role_trans || '[=]') AND NOT array_to_string(relacl, ',') ~ ('[,]' || n_role_trans || '[=]') THEN ARRAY[NULL]::text[] ELSE relacl::text[] END) AS acl FROM pg_catalog.pg_class WHERE oid = obj_oid AND relacl IS NOT NULL AND n_role::oid = relowner ) SELECT 'REVOKE ' || privilege || ' ON SEQUENCE ' || oid::regclass::text || ' FROM %I' FROM t_acl, unnest(ARRAY['SELECT', 'USAGE', 'UPDATE'], ARRAY['r', 'U', 'w']) AS l (privilege, prvlg) WHERE (acl ~ ('^' || n_role_trans || '[=]') AND NOT acl ~ ( '[=].*' || prvlg || '.*[/]')) OR acl IS NULL ; ------ FONCTIONS ------ -- inclut les fonctions d'agrégation ELSIF obj_type IN ('function', 'aggregate') THEN -- privilèges attribués (si n_role n'est pas le propriétaire de l'objet) : RETURN QUERY WITH t_acl AS ( SELECT oid, unnest(proacl)::text AS acl FROM pg_catalog.pg_proc WHERE oid = obj_oid AND proacl IS NOT NULL AND NOT n_role::oid = proowner ) SELECT 'GRANT ' || privilege || ' ON FUNCTION ' || oid::regprocedure::text || ' TO %I' FROM t_acl, unnest(ARRAY['EXECUTE'], ARRAY['X']) AS l (privilege, prvlg) WHERE acl ~ ('^' || n_role_trans || '[=].*' || prvlg || '.*[/]') ; -- privilèges révoqués du propriétaire (si n_role est le propriétaire de l'objet) : RETURN QUERY WITH t_acl AS ( SELECT oid, unnest(CASE WHEN proacl::text[] = ARRAY[]::text[] OR NOT array_to_string(proacl, ',') ~ ('^' || n_role_trans || '[=]') AND NOT array_to_string(proacl, ',') ~ ('[,]' || n_role_trans || '[=]') THEN ARRAY[NULL]::text[] ELSE proacl::text[] END) AS acl FROM pg_catalog.pg_proc WHERE oid = obj_oid AND n_role::oid = proowner AND proacl IS NOT NULL ) SELECT 'REVOKE ' || privilege || ' ON FUNCTION ' || oid::regprocedure::text || ' FROM %I' FROM t_acl, unnest(ARRAY['EXECUTE'], ARRAY['X']) AS l (privilege, prvlg) WHERE (acl ~ ('^' || n_role_trans || '[=]') AND NOT acl ~ ( '[=].*' || prvlg || '.*[/]')) OR acl IS NULL ; ------ TYPES ------ -- inclut les domaines ELSIF obj_type IN ('type', 'domain') THEN -- privilèges attribués (si n_role n'est pas le propriétaire de l'objet) : RETURN QUERY WITH t_acl AS ( SELECT oid, unnest(typacl)::text AS acl FROM pg_catalog.pg_type WHERE oid = obj_oid AND typacl IS NOT NULL AND NOT n_role::oid = typowner ) SELECT 'GRANT ' || privilege || ' ON TYPE ' || oid::regtype::text || ' TO %I' FROM t_acl, unnest(ARRAY['USAGE'], ARRAY['U']) AS l (privilege, prvlg) WHERE acl ~ ('^' || n_role_trans || '[=].*' || prvlg || '.*[/]') ; -- privilèges révoqués du propriétaire (si n_role est le propriétaire de l'objet) : RETURN QUERY WITH t_acl AS ( SELECT oid, unnest(CASE WHEN typacl::text[] = ARRAY[]::text[] OR NOT array_to_string(typacl, ',') ~ ('^' || n_role_trans || '[=]') AND NOT array_to_string(typacl, ',') ~ ('[,]' || n_role_trans || '[=]') THEN ARRAY[NULL]::text[] ELSE typacl::text[] END) AS acl FROM pg_catalog.pg_type WHERE oid = obj_oid AND n_role::oid = typowner AND typacl IS NOT NULL ) SELECT 'REVOKE ' || privilege || ' ON TYPE ' || oid::regtype::text || ' FROM %I' FROM t_acl, unnest(ARRAY['USAGE'], ARRAY['U']) AS l (privilege, prvlg) WHERE (acl ~ ('^' || n_role_trans || '[=]') AND NOT acl ~ ( '[=].*' || prvlg || '.*[/]')) OR acl IS NULL ; ELSE RAISE EXCEPTION 'FSS0. Le type d''objet % n''est pas pris en charge', obj_type ; END IF ; END $_$; ALTER FUNCTION z_asgard.asgard_synthese_role_obj(oid, text, regrole) OWNER TO g_admin_ext ; COMMENT ON FUNCTION z_asgard.asgard_synthese_role_obj(oid, text, regrole) IS 'ASGARD. Fonction qui liste les commandes permettant de reproduire les droits d''un rôle sur un objet.' ; -- FUNCTION: z_asgard.asgard_synthese_public_obj(oid, text) CREATE OR REPLACE FUNCTION z_asgard.asgard_synthese_public_obj(obj_oid oid, obj_type text) RETURNS TABLE(commande text) LANGUAGE plpgsql AS $_$ /* OBJET : Cette fonction renvoie une table contenant une liste de commandes GRANT et REVOKE permettant de recréer les droits de public sur un objet de type table, table étrangère, partition de table, vue, vue matérialisée ou séquence. REMARQUE : La fonction ne s'intéresse pas aux objets de type fonction (dont agrégats) et type (dont domaines), sur lesquels public reçoit des droits par défaut qu'il n'est pas judicieux de reproduire sur un autre rôle, ni de révoquer lors d'un changement de lecteur/éditeur. Si des privilèges par défaut ont été révoqués pour public, la révocation restera valable pour les futurs lecteur/éditeurs puisqu'il n'y a pas d'attribution de privilèges supplémentaires pour les lecteurs/éditeurs sur ces objets. ARGUMENTS : - "obj_oid" est l'identifiant interne de l'objet ; - "obj_type" est le type de l'objet au format text ('table', 'view', 'materialized view', 'sequence', 'foreign table', 'partitioned table'). SORTIE : Une table avec un unique champ nommé "commande". */ BEGIN ------ TABLE, VUE, VUE MATERIALISEE ------ IF obj_type IN ('table', 'view', 'materialized view', 'foreign table', 'partitioned table') THEN -- privilèges attribués (si n_role n'est pas le propriétaire de l'objet) : RETURN QUERY WITH t_acl AS ( SELECT oid, unnest(relacl)::text AS acl FROM pg_catalog.pg_class WHERE oid = obj_oid AND relacl IS NOT NULL ) SELECT 'GRANT ' || privilege || ' ON TABLE ' || oid::regclass::text || ' TO %I' FROM t_acl, unnest(ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], ARRAY['r', 'a', 'w', 'd', 'D', 'x', 't']) AS l (privilege, prvlg) WHERE acl ~ ('^[=].*' || prvlg || '.*[/]') ; ------ COLONNES ------ -- privilèges attribués : RETURN QUERY WITH t_acl AS ( SELECT attname, attrelid, unnest(attacl)::text AS acl FROM pg_catalog.pg_attribute WHERE pg_attribute.attrelid = obj_oid AND attacl IS NOT NULL ) SELECT 'GRANT ' || privilege || ' (' || attname::text || ') ON TABLE ' || attrelid::regclass::text || ' TO %I' FROM t_acl, unnest(ARRAY['SELECT', 'INSERT', 'UPDATE', 'REFERENCES'], ARRAY['r', 'a', 'w', 'x']) AS l (privilege, prvlg) WHERE acl ~ ('^[=].*' || prvlg || '.*[/]') ; ------ SEQUENCES ------ ELSIF obj_type = 'sequence' THEN -- privilèges attribués (si n_role n'est pas le propriétaire de l'objet) : RETURN QUERY WITH t_acl AS ( SELECT oid, unnest(relacl)::text AS acl FROM pg_catalog.pg_class WHERE oid = obj_oid AND relacl IS NOT NULL ) SELECT 'GRANT ' || privilege || ' ON SEQUENCE ' || oid::regclass::text || ' TO %I' FROM t_acl, unnest(ARRAY['SELECT', 'USAGE', 'UPDATE'], ARRAY['r', 'U', 'w']) AS l (privilege, prvlg) WHERE acl ~ ('^[=].*' || prvlg || '.*[/]') ; ELSE RAISE EXCEPTION 'FSS0. Le type d''objet % n''est pas pris en charge', obj_type ; END IF ; END $_$; ALTER FUNCTION z_asgard.asgard_synthese_public_obj(oid, text) OWNER TO g_admin_ext ; COMMENT ON FUNCTION z_asgard.asgard_synthese_public_obj(oid, text) IS 'ASGARD. Fonction qui liste les commandes permettant de reproduire les droits de public sur un objet.' ; ------ 4.3 - MODIFICATION DU PROPRIETAIRE D'UN SCHEMA ET SON CONTENU ------ -- FUNCTION: z_asgard.asgard_admin_proprietaire(text, text, boolean) CREATE OR REPLACE FUNCTION z_asgard.asgard_admin_proprietaire( n_schema text, n_owner text, b_setschema boolean DEFAULT True ) RETURNS int LANGUAGE plpgsql AS $_$ /* OBJET : Gestion des droits. Cette fonction permet d''attribuer un schéma et tous les objets qu'il contient à un [nouveau] propriétaire. AVERTISSEMENT : Les objets de type operator class, operator family et extended planner statistic ne sont pas pris en charge pour l'heure. ARGUMENTS : - "n_schema" est une chaîne de caractères correspondant au nom du schéma à considérer ; - "n_owner" est une chaîne de caractères correspondant au nom du rôle (rôle de groupe ou rôle de connexion) qui doit être propriétaire des objets ; - "b_setschema" est un paramètre booléen optionnel (vrai par défaut) qui indique si la fonction doit changer le propriétaire du schéma ou seulement des objets qu'il contient. RESULTAT : la fonction renvoie un entier correspondant au nombre d''objets effectivement traités. Les commandes lancées sont notifiées au fur et à mesure. */ DECLARE item record ; k int := 0 ; o_owner oid ; s_owner text ; BEGIN ------ TESTS PREALABLES ------ SELECT nspowner::regrole::text INTO s_owner FROM pg_catalog.pg_namespace WHERE nspname = n_schema ; -- non existance du schémas IF NOT FOUND THEN RAISE EXCEPTION 'FAP1. Le schéma % n''existe pas.', n_schema ; END IF ; -- absence de permission sur le propriétaire courant du schéma IF NOT pg_has_role(s_owner::regrole::oid, 'USAGE') THEN RAISE EXCEPTION 'FAP5. Vous n''êtes pas habilité à modifier le propriétaire du schéma %.', n_schema USING DETAIL = 'Propriétaire courant : ' || s_owner || '.' ; END IF ; -- le propriétaire désigné n'existe pas IF NOT n_owner IN (SELECT rolname::text FROM pg_catalog.pg_roles) THEN RAISE EXCEPTION 'FAP2. Le rôle % n''existe pas.', n_owner ; -- absence de permission sur le propriétaire désigné ELSIF NOT pg_has_role(n_owner, 'USAGE') THEN RAISE EXCEPTION 'FAP6. Vous n''avez pas la permission d''utiliser le rôle %.', n_owner ; ELSE o_owner := quote_ident(n_owner)::regrole::oid ; END IF ; -- le propriétaire désigné n'est pas le propriétaire courant et la fonction -- a été lancée avec la variante qui ne traite pas le schéma IF NOT b_setschema AND NOT quote_ident(n_owner) = s_owner THEN RAISE EXCEPTION 'FAP3. Le rôle % n''est pas propriétaire du schéma.', n_owner USING HINT = 'Lancez asgard_admin_proprietaire(' || quote_literal(n_schema) || ', ' || quote_literal(n_owner) || ') pour changer également le propriétaire du schéma.' ; END IF ; ------ PROPRIÉTAIRE DU SCHEMA ------ IF b_setschema THEN EXECUTE 'ALTER SCHEMA ' || quote_ident(n_schema) || ' OWNER TO ' || quote_ident(n_owner) ; RAISE NOTICE '> %', 'ALTER SCHEMA ' || quote_ident(n_schema) || ' OWNER TO ' || quote_ident(n_owner) ; k := k + 1 ; END IF ; ------ PROPRIETAIRES DES OBJETS ------ -- uniquement ceux qui n'appartiennent pas déjà -- au rôle identifié FOR item IN -- tables, tables étrangères, vues, vues matérialisées, -- partitions, séquences : SELECT relname::text AS n_objet, relowner AS obj_owner, relkind IN ('r', 'f', 'p', 'm') AS b, -- servira à assurer que les tables -- soient listées avant les objets qui -- en dépendent 'ALTER ' || kind_lg || ' ' || pg_class.oid::regclass || ' OWNER TO ' || quote_ident(n_owner) AS commande FROM pg_catalog.pg_class, unnest(ARRAY['r', 'p', 'v', 'm', 'f', 'S'], ARRAY['TABLE', 'TABLE', 'VIEW', 'MATERIALIZED VIEW', 'FOREIGN TABLE', 'SEQUENCE']) AS l (kind_crt, kind_lg) WHERE relnamespace = quote_ident(n_schema)::regnamespace AND relkind IN ('S', 'r', 'p', 'v', 'm', 'f') AND kind_crt = relkind AND NOT relowner = o_owner UNION -- fonctions et agrégats : SELECT proname::text AS n_objet, proowner AS obj_owner, False AS b, 'ALTER FUNCTION ' || pg_proc.oid::regprocedure || ' OWNER TO ' || quote_ident(n_owner) AS commande FROM pg_catalog.pg_proc WHERE pronamespace = quote_ident(n_schema)::regnamespace AND NOT proowner = o_owner -- à noter que les agrégats (proisagg vaut True) ont -- leur propre commande ALTER AGGREGATE OWNER TO, mais -- ALTER FUNCTION OWNER TO fonctionne également, on ne -- fait donc pas de distinction pour l'heure UNION -- types et domaines : SELECT typname::text AS n_objet, typowner AS obj_owner, False AS b, 'ALTER ' || kind_lg || ' ' || typnamespace::regnamespace::text || '.' || quote_ident(typname) || ' OWNER TO ' || quote_ident(n_owner) AS commande FROM unnest(ARRAY['true', 'false'], ARRAY['DOMAIN', 'TYPE']) AS l (kind_crt, kind_lg), pg_catalog.pg_type LEFT JOIN pg_catalog.pg_class ON typrelid = pg_class.oid WHERE typnamespace = quote_ident(n_schema)::regnamespace AND kind_crt::boolean = (typtype = 'd') AND NOT (typelem > 0 AND typname ~ '^_') -- exclusion des types array générés automatiquement -- leur propriétaire est lié à celui du type source -- et ne peut être modifié directement AND (NOT typtype = 'c' OR relkind = 'c') -- idem pour les types "ligne de table", dont -- le propriétaire est lié à celui de la table AND NOT typowner = o_owner UNION -- conversions : SELECT conname::text AS n_objet, conowner AS obj_owner, False AS b, 'ALTER CONVERSION ' || connamespace::regnamespace::text || '.' || quote_ident(conname) || ' OWNER TO ' || quote_ident(n_owner) AS commande FROM pg_catalog.pg_conversion WHERE connamespace = quote_ident(n_schema)::regnamespace AND NOT conowner = o_owner UNION -- opérateurs : SELECT oprname::text AS n_objet, oprowner AS obj_owner, False AS b, 'ALTER OPERATOR ' || pg_operator.oid::regoperator || ' OWNER TO ' || quote_ident(n_owner) AS commande FROM pg_catalog.pg_operator WHERE oprnamespace = quote_ident(n_schema)::regnamespace AND NOT oprowner = o_owner UNION -- collations : SELECT collname::text AS n_objet, collowner AS obj_owner, False AS b, 'ALTER COLLATION ' || collnamespace::regnamespace::text || '.' || quote_ident(collname) || ' OWNER TO ' || quote_ident(n_owner) AS commande FROM pg_catalog.pg_collation WHERE collnamespace = quote_ident(n_schema)::regnamespace AND NOT collowner = o_owner UNION -- text search dictionary : SELECT dictname::text AS n_objet, dictowner AS obj_owner, False AS b, 'ALTER TEXT SEARCH DICTIONARY ' || pg_ts_dict.oid::regdictionary || ' OWNER TO ' || quote_ident(n_owner) AS commande FROM pg_catalog.pg_ts_dict WHERE dictnamespace = quote_ident(n_schema)::regnamespace AND NOT dictowner = o_owner UNION -- text search configuration : SELECT cfgname::text AS n_objet, cfgowner AS obj_owner, False AS b, 'ALTER TEXT SEARCH CONFIGURATION ' || pg_ts_config.oid::regconfig || ' OWNER TO ' || quote_ident(n_owner) AS commande FROM pg_catalog.pg_ts_config WHERE cfgnamespace = quote_ident(n_schema)::regnamespace AND NOT cfgowner = o_owner ORDER BY b DESC LOOP IF pg_has_role(item.obj_owner, 'USAGE') THEN EXECUTE item.commande ; RAISE NOTICE '> %', item.commande ; k := k + 1 ; ELSE RAISE EXCEPTION 'FAP4. Vous n''êtes pas habilité à modifier le propriétaire de l''objet %.', item.n_objet USING DETAIL = 'Propriétaire courant : ' || item.obj_owner::regrole::text || '.' ; END IF ; END LOOP ; ------ RESULTAT ------ RETURN k ; END $_$ ; ALTER FUNCTION z_asgard.asgard_admin_proprietaire(text, text, boolean) OWNER TO g_admin_ext ; COMMENT ON FUNCTION z_asgard.asgard_admin_proprietaire(text, text, boolean) IS 'ASGARD. Fonction qui modifie le propriétaire d''un schéma et de tous les objets qu''il contient.' ; ------ 4.4 - TRANSFORMATION GRANT EN REVOKE ------ -- FUNCTION: z_asgard.asgard_grant_to_revoke(text) CREATE OR REPLACE FUNCTION z_asgard.asgard_grant_to_revoke(c_grant text) RETURNS text LANGUAGE plpgsql AS $_$ /* OBJET : Cette fonction transforme une commande de type GRANT en son équivalent REVOKE, ou l'inverse. AVERTISSEMENT : La fonction ne reconnaîtra que les mots clés écrits en majuscules. ARGUMENT : une commande de type GRANT/REVOKE présumée valide (chaîne de caractères). SORTIE : une commande de type REVOKE/GRANT (chaîne de caractères). */ DECLARE c_revoke text ; BEGIN IF c_grant ~ '^GRANT' THEN c_revoke := regexp_replace(c_grant, '^GRANT', 'REVOKE') ; c_revoke := regexp_replace(c_revoke, '[[:space:]]TO[[:space:]]', ' FROM ') ; ELSIF c_grant ~ '^REVOKE' THEN c_revoke := regexp_replace(c_grant, '^REVOKE', 'GRANT') ; c_revoke := regexp_replace(c_revoke, '[[:space:]]FROM[[:space:]]', ' TO ') ; ELSE RAISE EXCEPTION 'FGR1. Commande GRANT/REVOKE invalide.' ; END IF ; RETURN c_revoke ; END $_$; ALTER FUNCTION z_asgard.asgard_grant_to_revoke(text) OWNER TO g_admin_ext ; COMMENT ON FUNCTION z_asgard.asgard_grant_to_revoke(text) IS 'ASGARD. Fonction qui transforme une commande GRANT en commande REVOKE.' ; ------ 4.5 - INITIALISATION DE GESTION_SCHEMA ------ -- FUNCTION: z_asgard_admin.asgard_initialisation_gestion_schema(text[], boolean) CREATE OR REPLACE FUNCTION z_asgard_admin.asgard_initialisation_gestion_schema( exceptions text[] default NULL::text[], b_gs boolean default False ) RETURNS text LANGUAGE plpgsql AS $_$ /* OBJET : Cette fonction intègre à la table de gestion des droits gestion_schema l'ensemble des schémas existants, hors schémas système et ceux qui sont (optionnellement) listés en argument. ARGUMENTS : - exceptions (optionnel) : un tableau text[] contenant les noms des schémas à omettre, le cas échéant ; - b_gs (optionnel) : un booléen indiquant si, dans l'hypothèse où un schéma serait déjà référencé - nécessairement comme non créé - dans la table de gestion, c'est le propriétaire du schéma qui doit devenir le "producteur" du schéma (False) ou le producteur pré-renseigné dans la table de gestion qui doit devenir le propriétaire du schéma (True). False par défaut. SORTIE : '__ FIN INTIALISATION.' si la requête s'est exécutée normalement. */ DECLARE item record ; e_mssg text ; e_detl text ; e_hint text ; b_creation boolean ; BEGIN FOR item IN SELECT nspname, nspowner FROM pg_catalog.pg_namespace WHERE NOT nspname ~ ANY(ARRAY['^pg_toast', '^pg_temp', '^pg_catalog$', '^public$', '^information_schema$', '^topology$']) AND (exceptions IS NULL OR NOT nspname = ANY(exceptions)) LOOP SELECT creation INTO b_creation FROM z_asgard.gestion_schema_usr WHERE item.nspname::text = nom_schema ; IF b_creation IS NULL -- schéma non référencé dans gestion_schema THEN INSERT INTO z_asgard.gestion_schema_usr (nom_schema, producteur, creation) VALUES (item.nspname::text, replace(item.nspowner::regrole::text, '"', ''), true) ; RAISE NOTICE '... Schéma % enregistré dans la table de gestion.', item.nspname::text ; ELSIF NOT b_creation -- schéma pré-référencé dans gestion_schema THEN IF NOT b_gs THEN UPDATE z_asgard.gestion_schema_usr SET creation = true, producteur = replace(item.nspowner::regrole::text, '"', '') WHERE item.nspname::text = nom_schema ; ELSE UPDATE z_asgard.gestion_schema_usr SET creation = true WHERE item.nspname::text = nom_schema ; END IF ; RAISE NOTICE '... Schéma % marqué comme créé dans la table de gestion.', item.nspname::text ; END IF ; END LOOP ; RETURN '__ FIN INITALISATION.' ; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS e_mssg = MESSAGE_TEXT, e_hint = PG_EXCEPTION_HINT, e_detl = PG_EXCEPTION_DETAIL ; RAISE EXCEPTION 'FIG0 > %', e_mssg USING DETAIL = e_detl, HINT = e_hint ; END $_$; ALTER FUNCTION z_asgard_admin.asgard_initialisation_gestion_schema(text[], boolean) OWNER TO g_admin ; COMMENT ON FUNCTION z_asgard_admin.asgard_initialisation_gestion_schema(text[], boolean) IS 'ASGARD. Fonction qui initialise la table de gestion à partir des schémas existants.' ; ------ 4.6 - DEREFERENCEMENT D'UN SCHEMA ------ -- FUNCTION: z_asgard_admin.asgard_sortie_gestion_schema(text) CREATE OR REPLACE FUNCTION z_asgard_admin.asgard_sortie_gestion_schema(n_schema text) RETURNS text LANGUAGE plpgsql AS $_$ /* OBJET : Cette fonction permet de supprimer de la table de gestion un schéma existant (qui échappera alors aux mécanismes de gestion des droits), en outrepassant les règles qui veulent que seules les lignes avec creation valant false puisse être ciblées par des DELETE et que creation ne puisse être mis à false si le schéma existe. ARGUMENTS : - n_schema : nom d'un schéma présumé référencé dans le champ nom_schema de la table de gestion (sinon la fonction n'aura pas d'effet). SORTIE : '__ DEREFERENCEMENT REUSSI.' si la requête s'est exécutée normalement. */ DECLARE e_mssg text ; e_detl text ; e_hint text ; BEGIN UPDATE z_asgard.gestion_schema_etr SET ctrl = ARRAY['EXIT', 'x7-A;#rzo'] WHERE nom_schema = n_schema ; DELETE FROM z_asgard.gestion_schema_etr WHERE nom_schema = n_schema ; RETURN '__ DEREFERENCEMENT REUSSI.' ; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS e_mssg = MESSAGE_TEXT, e_hint = PG_EXCEPTION_HINT, e_detl = PG_EXCEPTION_DETAIL ; RAISE EXCEPTION 'FSG0 > %', e_mssg USING DETAIL = e_detl, HINT = e_hint ; END $_$; ALTER FUNCTION z_asgard_admin.asgard_sortie_gestion_schema(text) OWNER TO g_admin ; COMMENT ON FUNCTION z_asgard_admin.asgard_sortie_gestion_schema(text) IS 'ASGARD. Fonction qui déréférence un schéma existant de la table de gestion.' ; ------ 4.7 - NETTOYAGE DES RÔLES ------ -- FUNCTION: z_asgard.asgard_nettoyage_roles() CREATE OR REPLACE FUNCTION z_asgard.asgard_nettoyage_roles() RETURNS text LANGUAGE plpgsql AS $_$ /* OBJET : Cette fonction active la mise à jour des noms des rôles désignés dans la table de gestion comme producteur, éditeur et lecteur, pour prendre en compte les changements de nom ou suppression qui auraient pu avoir eu lieu. ARGUMENTS : néant. SORTIE : '__ NETTOYAGE REUSSI.' si la requête s'est exécutée normalement. */ DECLARE e_mssg text ; e_detl text ; e_hint text ; BEGIN UPDATE z_asgard.gestion_schema_usr SET producteur = producteur, editeur = editeur, lecteur = lecteur ; RETURN '__ NETTOYAGE REUSSI.' ; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS e_mssg = MESSAGE_TEXT, e_hint = PG_EXCEPTION_HINT, e_detl = PG_EXCEPTION_DETAIL ; RAISE EXCEPTION 'FNR0 > %', e_mssg USING DETAIL = e_detl, HINT = e_hint ; END $_$; ALTER FUNCTION z_asgard.asgard_nettoyage_roles() OWNER TO g_admin_ext ; COMMENT ON FUNCTION z_asgard.asgard_nettoyage_roles() IS 'ASGARD. Fonction qui met à jour les noms des rôles référencés dans la table de gestion.' ; ------ 4.8 - REINITIALISATION DES PRIVILEGES SUR UN SCHEMA ------ -- FUNCTION: z_asgard.asgard_initialise_schema(text, boolean, boolean) CREATE OR REPLACE FUNCTION z_asgard.asgard_initialise_schema( n_schema text, b_preserve boolean DEFAULT False, b_gs boolean default False ) RETURNS text LANGUAGE plpgsql AS $_$ /* OBJET : Cette fonction permet de réinitialiser les droits sur un schéma selon les privilèges standards associés aux rôles désignés dans la table de gestion. Si elle est appliquée à un schéma existant non référencé dans la table de gestion, elle l'ajoute avec son propriétaire courant. Elle échoue si le schéma n'existe pas. ARGUMENTS : - n_schema : nom d'un schéma présumé existant ; - b_preserve (optionnel) : un paramètre booléen. Pour un schéma encore non référencé (ou pré-référencé comme non-créé) dans la table de gestion une valeur True signifie que les privilèges des rôles lecteur et éditeur doivent être ajoutés par dessus les droits actuels. Avec la valeur par défaut False, les privilèges sont réinitialisés. Ce paramètre est ignoré pour un schéma déjà référencé comme créé (et les privilèges sont réinitialisés) ; - b_gs (optionnel) : un booléen indiquant si, dans l'hypothèse où un schéma serait déjà référencé - nécessairement comme non créé - dans la table de gestion, c'est le propriétaire du schéma qui doit devenir le "producteur" (False) ou le producteur de la table de gestion qui doit devenir le propriétaire du schéma (True). False par défaut. Ce paramètre est ignoré pour un schéma déjà créé. SORTIE : '__ REINITIALISATION REUSSIE.' (ou '__INITIALISATION REUSSIE.' pour un schéma non référencé comme créé avec b_preserve = True) si la requête s'est exécutée normalement. */ DECLARE roles record ; r record ; c record ; n_owner text ; k int := 0 ; n int ; e_mssg text ; e_detl text ; e_hint text ; BEGIN ------ TESTS PREALABLES ------ -- schéma système IF n_schema ~ ANY(ARRAY['^pg_toast', '^pg_temp', '^pg_catalog$', '^public$', '^information_schema$', '^topology$']) THEN RAISE EXCEPTION 'FIS1. Opération interdite. Le schéma % est un schéma système.', n_schema ; END IF ; -- existence du schéma SELECT replace(nspowner::regrole::text, '"', '') INTO n_owner FROM pg_catalog.pg_namespace WHERE n_schema = nspname::text ; IF NOT FOUND THEN RAISE EXCEPTION 'FIS2. Echec. Le schéma % n''existe pas.', n_schema ; END IF ; -- permission sur le propriétaire IF NOT pg_has_role(n_owner, 'USAGE') THEN RAISE EXCEPTION 'FIS3. Echec. Vous ne disposez pas des permissions nécessaires sur le schéma % pour réaliser cette opération.', n_schema USING HINT = 'Il vous faut être membre du rôle propriétaire ' || n_owner || '.' ; END IF ; ------ RECUPERATION DES ROLES ------ SELECT r1.rolname AS producteur, CASE WHEN editeur = 'public' THEN 'public' ELSE r2.rolname END AS editeur, CASE WHEN lecteur = 'public' THEN 'public' ELSE r3.rolname END AS lecteur, creation INTO roles FROM z_asgard.gestion_schema_etr LEFT JOIN pg_catalog.pg_roles AS r1 ON r1.oid = oid_producteur LEFT JOIN pg_catalog.pg_roles AS r2 ON r2.oid = oid_editeur LEFT JOIN pg_catalog.pg_roles AS r3 ON r3.oid = oid_lecteur WHERE nom_schema = n_schema ; ------ SCHEMA NON REFERENCE ------ -- ajouté à gestion_schema -- le reste est pris en charge par le trigger -- on_modify_gestion_schema_after IF NOT FOUND THEN INSERT INTO z_asgard.gestion_schema_usr (nom_schema, producteur, creation) VALUES (n_schema, n_owner, true) ; RAISE NOTICE '... Le schéma % a été enregistré dans la table de gestion.', n_schema ; IF b_preserve THEN RETURN '__ INITIALISATION REUSSIE.' ; END IF ; ------- SCHEMA PRE-REFERENCE ------ -- présent dans gestion_schema avec creation valant -- False. ELSIF NOT roles.creation THEN IF NOT b_gs THEN UPDATE z_asgard.gestion_schema_usr SET creation = true, producteur = n_owner WHERE n_schema = nom_schema ; ELSE UPDATE z_asgard.gestion_schema_usr SET creation = true WHERE n_schema = nom_schema ; END IF ; RAISE NOTICE '... Le schéma % a été marqué comme créé dans la table de gestion.', item.nspname::text ; IF b_preserve THEN RETURN '__ INITIALISATION REUSSIE.' ; END IF ; ------ REMISE A PLAT DES PROPRIETAIRES ------ -- uniquement pour les schémas qui étaient déjà -- référencés dans gestion_schema (pour les autres, pris en charge -- par le trigger on_modify_gestion_schema_after) -- schéma dont le propriétaire ne serait pas le producteur ELSIF NOT roles.producteur = n_owner THEN -- permission sur le producteur IF NOT pg_has_role(roles.producteur, 'USAGE') THEN RAISE EXCEPTION 'FIS4. Echec. Vous ne disposez pas des permissions nécessaires sur le schéma % pour réaliser cette opération.', n_schema USING HINT = 'Il vous faut être membre du rôle producteur ' || roles.producteur || '.' ; END IF ; -- propriétaire du schéma + contenu RAISE NOTICE '(ré)attribution de la propriété du schéma et des objets au rôle producteur du schéma :' ; PERFORM z_asgard.asgard_admin_proprietaire(n_schema, roles.producteur) ; -- schema dont le propriétaire est le producteur ELSE -- reprise uniquement des propriétaires du contenu RAISE NOTICE '(ré)attribution de la propriété des objets au rôle producteur du schéma :' ; SELECT z_asgard.asgard_admin_proprietaire(n_schema, roles.producteur, False) INTO n ; IF n = 0 THEN RAISE NOTICE '> néant' ; END IF ; END IF ; ------ DESTRUCTION DES PRIVILEGES ACTUELS ------ -- hors privilèges par défaut (définis par ALTER DEFAULT PRIVILEGE) -- et hors révocations des privilèges par défaut de public sur -- les types et les fonctions -- pour le propriétaire, ces commandes ont pour effet -- de remettre les privilèges par défaut supprimés -- public RAISE NOTICE 'remise à zéro des privilèges manuels du pseudo-rôle public :' ; FOR c IN (SELECT * FROM z_asgard.asgard_synthese_public( quote_ident(n_schema)::regnamespace)) LOOP EXECUTE format(z_asgard.asgard_grant_to_revoke(c.commande), 'public') ; RAISE NOTICE '> %', format(z_asgard.asgard_grant_to_revoke(c.commande), 'public') ; END LOOP ; IF NOT FOUND THEN RAISE NOTICE '> néant' ; END IF ; -- autres rôles RAISE NOTICE 'remise à zéro des privilèges des autres rôles (pour le producteur, les éventuels privilèges manquants sont réattribués) :' ; FOR r IN (SELECT rolname FROM pg_roles) LOOP FOR c IN (SELECT * FROM z_asgard.asgard_synthese_role( quote_ident(n_schema)::regnamespace, quote_ident(r.rolname)::regrole)) LOOP EXECUTE format(z_asgard.asgard_grant_to_revoke(c.commande), r.rolname) ; RAISE NOTICE '> %', format(z_asgard.asgard_grant_to_revoke(c.commande), r.rolname) ; k := k + 1 ; END LOOP ; END LOOP ; IF NOT FOUND OR k = 0 THEN RAISE NOTICE '> néant' ; END IF ; ------ RECREATION DES PRIVILEGES DE L'EDITEUR ------ IF roles.editeur IS NOT NULL THEN RAISE NOTICE 'application des privilèges standards pour le rôle éditeur du schéma :' ; EXECUTE 'GRANT USAGE ON SCHEMA ' || quote_ident(n_schema) || ' TO ' || quote_ident(roles.editeur) ; RAISE NOTICE '> %', 'GRANT USAGE ON SCHEMA ' || quote_ident(n_schema) || ' TO ' || quote_ident(roles.editeur) ; EXECUTE 'GRANT SELECT, UPDATE, DELETE, INSERT ON ALL TABLES IN SCHEMA ' || quote_ident(n_schema) || ' TO ' || quote_ident(roles.editeur) ; RAISE NOTICE '> %', 'GRANT SELECT, UPDATE, DELETE, INSERT ON ALL TABLES IN SCHEMA ' || quote_ident(n_schema) || ' TO ' || quote_ident(roles.editeur) ; EXECUTE 'GRANT SELECT, USAGE ON ALL SEQUENCES IN SCHEMA ' || quote_ident(n_schema) || ' TO ' || quote_ident(roles.editeur) ; RAISE NOTICE '> %', 'GRANT SELECT, USAGE ON ALL SEQUENCES IN SCHEMA ' || quote_ident(n_schema) || ' TO ' || quote_ident(roles.editeur) ; END IF ; ------ RECREATION DES PRIVILEGES DU LECTEUR ------ IF roles.lecteur IS NOT NULL THEN RAISE NOTICE 'application des privilèges standards pour le rôle lecteur du schéma :' ; EXECUTE 'GRANT USAGE ON SCHEMA ' || quote_ident(n_schema) || ' TO ' || quote_ident(roles.lecteur) ; RAISE NOTICE '> %', 'GRANT USAGE ON SCHEMA ' || quote_ident(n_schema) || ' TO ' || quote_ident(roles.lecteur) ; EXECUTE 'GRANT SELECT ON ALL TABLES IN SCHEMA ' || quote_ident(n_schema) || ' TO ' || quote_ident(roles.lecteur) ; RAISE NOTICE '> %', 'GRANT SELECT ON ALL TABLES IN SCHEMA ' || quote_ident(n_schema) || ' TO ' || quote_ident(roles.lecteur) ; EXECUTE 'GRANT SELECT ON ALL SEQUENCES IN SCHEMA ' || quote_ident(n_schema) || ' TO ' || quote_ident(roles.lecteur) ; RAISE NOTICE '> %', 'GRANT SELECT ON ALL SEQUENCES IN SCHEMA ' || quote_ident(n_schema) || ' TO ' || quote_ident(roles.lecteur) ; END IF ; ------ RECREATION DES PRIVILEGES SUR LES SCHEMAS D'ASGARD ------ IF n_schema = 'z_asgard' AND (roles.lecteur IS NULL OR NOT roles.lecteur = 'g_consult') THEN -- rétablissement des droits de g_consult RAISE NOTICE 'rétablissement des privilèges attendus pour g_consult :' ; GRANT USAGE ON SCHEMA z_asgard TO g_consult ; RAISE NOTICE '> GRANT USAGE ON SCHEMA z_asgard TO g_consult' ; GRANT SELECT ON TABLE z_asgard.gestion_schema_usr TO g_consult ; RAISE NOTICE '> GRANT SELECT ON TABLE z_asgard.gestion_schema_usr TO g_consult' ; GRANT SELECT ON TABLE z_asgard.gestion_schema_etr TO g_consult ; RAISE NOTICE '> GRANT SELECT ON TABLE z_asgard.gestion_schema_etr TO g_consult' ; GRANT SELECT ON TABLE z_asgard.qgis_menubuilder_metadata TO g_consult ; RAISE NOTICE '> GRANT SELECT ON TABLE z_asgard.qgis_menubuilder_metadata TO g_consult' ; ELSIF n_schema = 'z_asgard_admin' THEN -- rétablissement des droits de g_admin_ext RAISE NOTICE 'rétablissement des privilèges attendus pour g_admin_ext :' ; GRANT USAGE ON SCHEMA z_asgard_admin TO g_admin_ext ; RAISE NOTICE '> GRANT USAGE ON SCHEMA z_asgard_admin TO g_admin_ext' ; GRANT INSERT, SELECT, UPDATE, DELETE ON TABLE z_asgard_admin.gestion_schema TO g_admin_ext ; RAISE NOTICE '> GRANT INSERT, SELECT, UPDATE, DELETE ON TABLE z_asgard_admin.gestion_schema TO g_admin_ext' ; GRANT SELECT ON TABLE z_asgard_admin.asgard_parametre TO g_admin_ext ; RAISE NOTICE '> GRANT SELECT ON TABLE z_asgard_admin.asgard_parametre TO g_admin_ext' ; END IF ; RETURN '__ REINITIALISATION REUSSIE.' ; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS e_mssg = MESSAGE_TEXT, e_hint = PG_EXCEPTION_HINT, e_detl = PG_EXCEPTION_DETAIL ; RAISE EXCEPTION 'FIS0 > %', e_mssg USING DETAIL = e_detl, HINT = e_hint ; END $_$; ALTER FUNCTION z_asgard.asgard_initialise_schema(text, boolean, boolean) OWNER TO g_admin_ext ; COMMENT ON FUNCTION z_asgard.asgard_initialise_schema(text, boolean, boolean) IS 'ASGARD. Fonction qui réinitialise les privilèges sur un schéma (et l''ajoute à la table de gestion s''il n''y est pas déjà).' ; ------ 4.9 - REINITIALISATION DES PRIVILEGES SUR UN OBJET ------ -- FUNCTION: z_asgard.asgard_initialise_obj(text, text, text) CREATE OR REPLACE FUNCTION z_asgard.asgard_initialise_obj( obj_schema text, obj_nom text, obj_typ text ) RETURNS text LANGUAGE plpgsql AS $_$ /* OBJET : Cette fonction permet de réinitialiser les droits sur un objet selon les privilèges standards associés aux rôles désignés dans la table de gestion pour son schéma. ARGUMENTS : - "obj_schema" est le nom du schéma contenant l'objet, au format texte et sans guillemets ; - "obj_nom" est le nom de l'objet, au format texte et sans guillemets ; - "obj_typ" est le type de l'objet au format text ('table', 'partitioned table' (assimilé à 'table'), 'view', 'materialized view', 'foreign table', 'sequence', 'function', 'aggregate', 'type', 'domain'). SORTIE : '__ REINITIALISATION REUSSIE.' si la requête s'est exécutée normalement. */ DECLARE class_info record ; roles record ; obj record ; r record ; c record ; l text ; k int := 0 ; BEGIN -- pour la suite, on assimile les partitions à des tables IF obj_typ = 'partitioned table' THEN obj_typ := 'table' ; END IF ; ------ TESTS PREALABLES ------ -- schéma système IF obj_schema ~ ANY(ARRAY['^pg_toast', '^pg_temp', '^pg_catalog$', '^public$', '^information_schema$', '^topology$']) THEN RAISE EXCEPTION 'FIO1. Opération interdite. Le schéma % est un schéma système.', obj_schema ; END IF ; -- schéma non référencé IF NOT obj_schema IN (SELECT nom_schema FROM z_asgard.gestion_schema_usr WHERE creation) THEN RAISE EXCEPTION 'FIO2. Echec. Le schéma % n''est pas référencé dans la table de gestion (ou marqué comme non créé).', obj_schema ; END IF ; -- type invalide + récupération des informations sur le catalogue contenant l'objet SELECT xtyp, xclass, xprefix || 'name' AS xname, xprefix || 'owner' AS xowner, xprefix || 'namespace' AS xschema INTO class_info FROM unnest(ARRAY['table', 'foreign table', 'view', 'materialized view', 'sequence', 'type', 'domain', 'function', 'aggregate'], ARRAY['pg_class', 'pg_class', 'pg_class', 'pg_class', 'pg_class', 'pg_type', 'pg_type', 'pg_proc', 'pg_proc'], ARRAY['rel', 'rel', 'rel', 'rel', 'rel', 'typ', 'typ', 'pro', 'pro']) AS typ (xtyp, xclass, xprefix) WHERE typ.xtyp = obj_typ ; IF NOT FOUND THEN RAISE EXCEPTION 'FIO3. Echec. Le type % n''existe pas ou n''est pas pris en charge.', obj_typ USING HINT = 'Types acceptés : ''table'', ''partitioned table'' (assimilé à ''table''), ''view'', ''materialized view'', ''foreign table'', ''sequence'', ''function'', ''aggregate'', ''type'', ''domain''.' ; END IF ; -- objet inexistant + récupération du propriétaire EXECUTE 'SELECT ' || class_info.xowner || '::regrole::text AS prop, ' || class_info.xclass || '.oid FROM pg_catalog.' || class_info.xclass || ' WHERE ' || class_info.xname || ' = ' || quote_literal(obj_nom) || ' AND ' || class_info.xschema || '::regnamespace::text = quote_ident(' || quote_literal(obj_schema) || ')' INTO obj ; IF obj.prop IS NULL THEN RAISE EXCEPTION 'FIO4. Echec. L''objet % n''existe pas.', obj_nom ; END IF ; ------ RECUPERATION DES ROLES ------ SELECT r1.rolname AS producteur, CASE WHEN editeur = 'public' THEN 'public' ELSE r2.rolname END AS editeur, CASE WHEN lecteur = 'public' THEN 'public' ELSE r3.rolname END AS lecteur, creation INTO roles FROM z_asgard.gestion_schema_etr LEFT JOIN pg_catalog.pg_roles AS r1 ON r1.oid = oid_producteur LEFT JOIN pg_catalog.pg_roles AS r2 ON r2.oid = oid_editeur LEFT JOIN pg_catalog.pg_roles AS r3 ON r3.oid = oid_lecteur WHERE nom_schema = obj_schema ; -- permission sur le producteur IF NOT pg_has_role(roles.producteur, 'USAGE') THEN RAISE EXCEPTION 'FIO5. Echec. Vous ne disposez pas des permissions nécessaires sur le schéma % pour réaliser cette opération.', obj_schema USING HINT = 'Il vous faut être membre du rôle producteur ' || roles.producteur || '.' ; END IF ; ------ REMISE A PLAT DU PROPRIETAIRE ------ IF NOT obj.prop = roles.producteur THEN -- permission sur le propriétaire de l'objet IF NOT pg_has_role(obj.prop, 'USAGE') THEN RAISE EXCEPTION 'FIO6. Echec. Vous ne disposez pas des permissions nécessaires sur l''objet % pour réaliser cette opération.', obj_nom USING HINT = 'Il vous faut être membre du rôle propriétaire de l''objet (' || obj.prop || ').' ; END IF ; RAISE NOTICE 'réattribution de la propriété de % au rôle producteur du schéma :', obj_nom ; l := 'ALTER ' || obj_typ || ' ' || quote_ident(obj_schema) || '.' || quote_ident(obj_nom)|| ' OWNER TO ' || quote_ident(roles.producteur) ; EXECUTE l ; RAISE NOTICE '> %', l ; END IF ; ------ DESTRUCTION DES PRIVILEGES ACTUELS ------ -- hors privilèges par défaut (définis par ALTER DEFAULT PRIVILEGE) -- et hors révocations des privilèges par défaut de public sur -- les types et les fonctions -- pour le propriétaire, ces commandes ont pour effet -- de remettre les privilèges par défaut supprimés -- public RAISE NOTICE 'remise à zéro des privilèges manuels du pseudo-rôle public :' ; FOR c IN (SELECT * FROM z_asgard.asgard_synthese_public_obj(obj.oid, obj_typ)) LOOP EXECUTE format(z_asgard.asgard_grant_to_revoke(c.commande), 'public') ; RAISE NOTICE '> %', format(z_asgard.asgard_grant_to_revoke(c.commande), 'public') ; END LOOP ; IF NOT FOUND THEN RAISE NOTICE '> néant' ; END IF ; -- autres rôles RAISE NOTICE 'remise à zéro des privilèges des autres rôles (pour le producteur, les éventuels privilèges manquants sont réattribués) :' ; FOR r IN (SELECT rolname FROM pg_roles) LOOP FOR c IN (SELECT * FROM z_asgard.asgard_synthese_role_obj( obj.oid, obj_typ, quote_ident(r.rolname)::regrole)) LOOP EXECUTE format(z_asgard.asgard_grant_to_revoke(c.commande), r.rolname) ; RAISE NOTICE '> %', format(z_asgard.asgard_grant_to_revoke(c.commande), r.rolname) ; k := k + 1 ; END LOOP ; END LOOP ; IF NOT FOUND OR k = 0 THEN RAISE NOTICE '> néant' ; END IF ; ------ RECREATION DES PRIVILEGES DE L'EDITEUR ------ IF roles.editeur IS NOT NULL THEN -- sur les tables : IF obj_typ IN ('table', 'view', 'materialized view', 'foreign table') THEN RAISE NOTICE 'application des privilèges standards pour le rôle éditeur du schéma :' ; l := 'GRANT SELECT, UPDATE, DELETE, INSERT ON TABLE ' || quote_ident(obj_schema) || '.' || quote_ident(obj_nom) || ' TO ' || quote_ident(roles.editeur) ; EXECUTE l ; RAISE NOTICE '> %', l ; -- sur les séquences : ELSIF obj_typ IN ('sequence') THEN RAISE NOTICE 'application des privilèges standards pour le rôle éditeur du schéma :' ; l := 'GRANT SELECT, USAGE ON SEQUENCE ' || quote_ident(obj_schema) || '.' || quote_ident(obj_nom) || ' TO ' || quote_ident(roles.editeur) ; EXECUTE l ; RAISE NOTICE '> %', l ; END IF ; END IF ; ------ RECREATION DES PRIVILEGES DU LECTEUR ------ IF roles.lecteur IS NOT NULL THEN -- sur les tables : IF obj_typ IN ('table', 'view', 'materialized view', 'foreign table') THEN RAISE NOTICE 'application des privilèges standards pour le rôle lecteur du schéma :' ; l := 'GRANT SELECT ON TABLE ' || quote_ident(obj_schema) || '.' || quote_ident(obj_nom) || ' TO ' || quote_ident(roles.lecteur) ; EXECUTE l ; RAISE NOTICE '> %', l ; -- sur les séquences : ELSIF obj_typ IN ('sequence') THEN RAISE NOTICE 'application des privilèges standards pour le rôle lecteur du schéma :' ; l := 'GRANT SELECT ON SEQUENCE ' || quote_ident(obj_schema) || '.' || quote_ident(obj_nom) || ' TO ' || quote_ident(roles.lecteur) ; EXECUTE l ; RAISE NOTICE '> %', l ; END IF ; END IF ; RETURN '__ REINITIALISATION REUSSIE.' ; END $_$; ALTER FUNCTION z_asgard.asgard_initialise_obj(text, text, text) OWNER TO g_admin_ext ; COMMENT ON FUNCTION z_asgard.asgard_initialise_obj(text, text, text) IS 'ASGARD. Fonction qui réinitialise les privilèges sur un objet.' ; ------ 4.10 - DEPLACEMENT D'OBJET ------ -- FUNCTION: z_asgard.asgard_deplace_obj(text, text, text, text, int) CREATE OR REPLACE FUNCTION z_asgard.asgard_deplace_obj( obj_schema text, obj_nom text, obj_typ text, schema_cible text, variante int DEFAULT 1 ) RETURNS text LANGUAGE plpgsql AS $_$ /* OBJET : Cette fonction permet de déplacer un objet vers un nouveau schéma en spécifiant la gestion voulue sur les droits de l'objet : transfert ou réinitialisation des privilèges. Dans le cas d'une table avec un ou plusieurs champs de type serial, elle prend aussi en charge les privilèges sur les séquences associées. ARGUMENTS : - "obj_schema" est le nom du schéma contenant l'objet, au format texte et sans guillemets ; - "obj_nom" est le nom de l'objet, au format texte et sans guillemets ; - "obj_typ" est le type de l'objet au format text ('table', 'partitioned table' (assimilé à 'table'), 'view', 'materialized view', 'foreign table', 'sequence', 'function', 'aggregate', 'type', 'domain') ; - "schema_cible" est le nom du schéma où doit être déplacé l'objet, au format texte et sans guillemets ; - "variante" [optionnel] est un entier qui définit le comportement attendu par l'utilisateur vis à vis des privilèges : - 1 (valeur par défaut) | TRANSFERT COMPLET + CONSERVATION : les privilèges des rôles producteur, éditeur et lecteur de l'ancien schéma sont transférés sur ceux du nouveau. Si un éditeur ou lecteur a été désigné pour le nouveau schéma mais qu'aucun n'était défini pour l'ancien, le rôle reçoit les privilèges standards pour sa fonction. Le cas échéant, les privilèges des autres rôles sont conservés ; - 2 | REINITIALISATION COMPLETE : les nouveaux producteur, éditeur et lecteur reçoivent les privilèges standard. Les privilèges des autres rôles sont supprimés ; - 3 | TRANSFERT COMPLET + NETTOYAGE : les privilèges des rôles producteur, éditeur et lecteur de l'ancien schéma sont transférés sur ceux du nouveau. Si un éditeur ou lecteur a été désigné pour le nouveau schéma mais qu'aucun n'était défini pour l'ancien, le rôle reçoit les privilèges standards pour sa fonction. Les privilèges des autres rôles sont supprimés ; - 4 | TRANSFERT PRODUCTEUR + CONSERVATION : les privilèges de l'ancien producteur sont transférés sur le nouveau. Les privilèges des autres rôles sont conservés tels quels. C'est le comportement d'une commande ALTER [...] SET SCHEMA (interceptée par l'event trigger asgard_on_alter_objet) ; - 5 | TRANSFERT PRODUCTEUR + REINITIALISATION : les privilèges de l'ancien producteur sont transférés sur le nouveau. Les nouveaux éditeur et lecteur reçoivent les privilèges standards. Les privilèges des autres rôles sont supprimés ; - 6 | REINITIALISATION PARTIELLE : les nouveaux producteur, éditeur et lecteur reçoivent les privilèges standard. Les privilèges des autres rôles sont conservés. SORTIE : '__ DEPLACEMENT REUSSI.' si la requête s'est exécutée normalement. */ DECLARE class_info record ; roles record ; roles_cible record ; obj record ; r record ; c record ; l text ; c_lecteur text[] ; c_editeur text[] ; c_producteur text[] ; c_n_lecteur text[] ; c_n_editeur text[] ; c_autres text[] ; seq_liste oid[] ; a text[] ; s record ; o oid ; BEGIN -- pour la suite, on assimile les partitions à des tables IF obj_typ = 'partitioned table' THEN obj_typ := 'table' ; END IF ; ------ TESTS PREALABLES ------ -- schéma système IF obj_schema ~ ANY(ARRAY['^pg_toast', '^pg_temp', '^pg_catalog$', '^public$', '^information_schema$', '^topology$']) THEN RAISE EXCEPTION 'FDO1. Opération interdite. Le schéma % est un schéma système.', obj_schema ; END IF ; -- schéma de départ non référencé IF NOT obj_schema IN (SELECT nom_schema FROM z_asgard.gestion_schema_usr WHERE creation) THEN RAISE EXCEPTION 'FDO2. Echec. Le schéma % n''est pas référencé dans la table de gestion (ou marqué comme non créé).', obj_schema ; END IF ; -- schéma cible non référencé IF NOT schema_cible IN (SELECT nom_schema FROM z_asgard.gestion_schema_usr WHERE creation) THEN RAISE EXCEPTION 'FDO3. Echec. Le schéma cible % n''est pas référencé dans la table de gestion (ou marqué comme non créé).', schema_cible ; END IF ; -- type invalide + récupération des informations sur le catalogue contenant l'objet SELECT xtyp, xclass, xprefix || 'name' AS xname, xprefix || 'owner' AS xowner, xprefix || 'namespace' AS xschema INTO class_info FROM unnest(ARRAY['table', 'foreign table', 'view', 'materialized view', 'sequence', 'type', 'domain', 'function', 'aggregate'], ARRAY['pg_class', 'pg_class', 'pg_class', 'pg_class', 'pg_class', 'pg_type', 'pg_type', 'pg_proc', 'pg_proc'], ARRAY['rel', 'rel', 'rel', 'rel', 'rel', 'typ', 'typ', 'pro', 'pro']) AS typ (xtyp, xclass, xprefix) WHERE typ.xtyp = obj_typ ; IF NOT FOUND THEN RAISE EXCEPTION 'FDO4. Echec. Le type % n''existe pas ou n''est pas pris en charge.', obj_typ USING HINT = 'Types acceptés : ''table'', ''partitioned table'' (assimilé à ''table''), ''view'', ''materialized view'', ''foreign table'', ''sequence'', ''function'', ''aggregate'', ''type'', ''domain''.' ; END IF ; -- objet inexistant + récupération du propriétaire EXECUTE 'SELECT ' || class_info.xowner || '::regrole::text AS prop, ' || class_info.xclass || '.oid FROM pg_catalog.' || class_info.xclass || ' WHERE ' || class_info.xname || ' = ' || quote_literal(obj_nom) || ' AND ' || class_info.xschema || '::regnamespace::text = quote_ident(' || quote_literal(obj_schema) || ')' INTO obj ; IF obj.prop IS NULL THEN RAISE EXCEPTION 'FDO5. Echec. L''objet % n''existe pas.', obj_nom ; END IF ; ------ RECUPERATION DES ROLES ------ -- schéma de départ : SELECT r1.rolname AS producteur, CASE WHEN editeur = 'public' THEN 'public' ELSE r2.rolname END AS editeur, CASE WHEN lecteur = 'public' THEN 'public' ELSE r3.rolname END AS lecteur, creation INTO roles FROM z_asgard.gestion_schema_etr LEFT JOIN pg_catalog.pg_roles AS r1 ON r1.oid = oid_producteur LEFT JOIN pg_catalog.pg_roles AS r2 ON r2.oid = oid_editeur LEFT JOIN pg_catalog.pg_roles AS r3 ON r3.oid = oid_lecteur WHERE nom_schema = obj_schema ; -- schéma cible : SELECT r1.rolname AS producteur, CASE WHEN editeur = 'public' THEN 'public' ELSE r2.rolname END AS editeur, CASE WHEN lecteur = 'public' THEN 'public' ELSE r3.rolname END AS lecteur, creation INTO roles_cible FROM z_asgard.gestion_schema_etr LEFT JOIN pg_catalog.pg_roles AS r1 ON r1.oid = oid_producteur LEFT JOIN pg_catalog.pg_roles AS r2 ON r2.oid = oid_editeur LEFT JOIN pg_catalog.pg_roles AS r3 ON r3.oid = oid_lecteur WHERE nom_schema = schema_cible ; -- permission sur le producteur du schéma cible IF NOT pg_has_role(roles_cible.producteur, 'USAGE') THEN RAISE EXCEPTION 'FDO6. Echec. Vous ne disposez pas des permissions nécessaires sur le schéma cible % pour réaliser cette opération.', schema_cible USING HINT = 'Il vous faut être membre du rôle producteur ' || roles_cible.producteur || '.' ; END IF ; -- permission sur le propriétaire de l'objet IF NOT pg_has_role(obj.prop, 'USAGE') THEN RAISE EXCEPTION 'FDO7. Echec. Vous ne disposez pas des permissions nécessaires sur l''objet % pour réaliser cette opération.', obj_nom USING HINT = 'Il vous faut être membre du rôle propriétaire de l''objet (' || obj.prop || ').' ; END IF ; ------ MEMORISATION DES PRIVILEGES ACTUELS ------ -- ancien producteur : SELECT array_agg(commande) INTO c_producteur FROM z_asgard.asgard_synthese_role_obj( obj.oid, obj_typ, quote_ident(roles.producteur)::regrole) ; -- ancien éditeur : IF roles.editeur = 'public' THEN SELECT array_agg(commande) INTO c_editeur FROM z_asgard.asgard_synthese_public_obj(obj.oid, obj_typ) ; ELSIF roles.editeur IS NOT NULL THEN SELECT array_agg(commande) INTO c_editeur FROM z_asgard.asgard_synthese_role_obj( obj.oid, obj_typ, quote_ident(roles.editeur)::regrole) ; END IF ; -- ancien lecteur : IF roles.lecteur = 'public' THEN SELECT array_agg(commande) INTO c_lecteur FROM z_asgard.asgard_synthese_public_obj(obj.oid, obj_typ) ; ELSIF roles.lecteur IS NOT NULL THEN SELECT array_agg(commande) INTO c_lecteur FROM z_asgard.asgard_synthese_role_obj( obj.oid, obj_typ, quote_ident(roles.lecteur)::regrole) ; END IF ; -- nouvel éditeur : IF roles_cible.editeur = 'public' THEN SELECT array_agg(commande) INTO c_n_editeur FROM z_asgard.asgard_synthese_public_obj(obj.oid, obj_typ) ; ELSIF roles_cible.editeur IS NOT NULL THEN SELECT array_agg(commande) INTO c_n_editeur FROM z_asgard.asgard_synthese_role_obj( obj.oid, obj_typ, quote_ident(roles_cible.editeur)::regrole) ; END IF ; -- nouveau lecteur : IF roles_cible.lecteur = 'public' THEN SELECT array_agg(commande) INTO c_n_lecteur FROM z_asgard.asgard_synthese_public_obj(obj.oid, obj_typ) ; ELSIF roles_cible.lecteur IS NOT NULL THEN SELECT array_agg(commande) INTO c_n_lecteur FROM z_asgard.asgard_synthese_role_obj( obj.oid, obj_typ, quote_ident(roles_cible.lecteur)::regrole) ; END IF ; -- autres rôles : -- pour ces commandes, contrairement aux précédentes, le rôle -- est inséré dès maintenant (avec "format") -- public IF NOT 'public' = ANY (array_remove(ARRAY[roles.producteur, roles.lecteur, roles.editeur, roles_cible.producteur, roles_cible.lecteur, roles_cible.editeur], NULL)) THEN SELECT array_agg(format(commande, 'public')) INTO c_autres FROM z_asgard.asgard_synthese_public_obj(obj.oid, obj_typ) ; END IF ; -- et le reste FOR r IN (SELECT rolname FROM pg_roles WHERE NOT rolname = ANY (array_remove(ARRAY[roles.producteur, roles.lecteur, roles.editeur, roles_cible.producteur, roles_cible.lecteur, roles_cible.editeur], NULL))) LOOP SELECT array_agg(format(commande, r.rolname::text)) INTO a FROM z_asgard.asgard_synthese_role_obj( obj.oid, obj_typ, quote_ident(r.rolname)::regrole) ; IF FOUND THEN c_autres := array_cat(c_autres, a) ; a := NULL ; END IF ; END LOOP ; ------ PRIVILEGES SUR LES SEQUENCES ASSOCIEES ------ IF obj_typ = 'table' THEN -- dans le cas d'une table, on recherche les séquences -- utilisées par ses éventuels champs de type serial. -- elles sont repérées par le fait qu'il existe -- une dépendance de type "DEPENDENCY_AUTO" entre -- la séquence et un champ de la table FOR s IN ( SELECT pg_class.oid FROM pg_catalog.pg_depend LEFT JOIN pg_catalog.pg_class ON pg_class.oid = pg_depend.objid WHERE pg_depend.classid = 'pg_catalog.pg_class'::regclass::oid AND pg_depend.refclassid = 'pg_catalog.pg_class'::regclass::oid AND pg_depend.refobjid = obj.oid AND pg_depend.refobjsubid > 0 AND pg_depend.deptype = 'a' AND pg_class.relkind = 'S' ) LOOP -- liste des séquences seq_liste := array_append(seq_liste, s.oid) ; -- récupération des privilèges -- ancien producteur : SELECT array_agg(commande) INTO a FROM z_asgard.asgard_synthese_role_obj( s.oid, 'sequence', quote_ident(roles.producteur)::regrole) ; IF FOUND THEN c_producteur := array_cat(c_producteur, a) ; a := NULL ; END IF ; -- ancien éditeur : IF roles.editeur = 'public' THEN SELECT array_agg(commande) INTO a FROM z_asgard.asgard_synthese_public_obj(s.oid, 'sequence'::text) ; ELSIF roles.editeur IS NOT NULL THEN SELECT array_agg(commande) INTO a FROM z_asgard.asgard_synthese_role_obj( s.oid, 'sequence'::text, quote_ident(roles.editeur)::regrole) ; END IF ; IF a IS NOT NULL THEN c_editeur := array_cat(c_editeur, a) ; a := NULL ; END IF ; -- ancien lecteur : IF roles.lecteur = 'public' THEN SELECT array_agg(commande) INTO a FROM z_asgard.asgard_synthese_public_obj(s.oid, 'sequence'::text) ; ELSIF roles.lecteur IS NOT NULL THEN SELECT array_agg(commande) INTO a FROM z_asgard.asgard_synthese_role_obj( s.oid, 'sequence'::text, quote_ident(roles.lecteur)::regrole) ; END IF ; IF a IS NOT NULL THEN c_lecteur := array_cat(c_lecteur, a) ; a := NULL ; END IF ; -- nouvel éditeur : IF roles_cible.editeur = 'public' THEN SELECT array_agg(commande) INTO a FROM z_asgard.asgard_synthese_public_obj(s.oid, 'sequence'::text) ; ELSIF roles_cible.editeur IS NOT NULL THEN SELECT array_agg(commande) INTO a FROM z_asgard.asgard_synthese_role_obj( s.oid, 'sequence'::text, quote_ident(roles_cible.editeur)::regrole) ; END IF ; IF a IS NOT NULL THEN c_n_editeur := array_cat(c_n_editeur, a) ; a := NULL ; END IF ; -- nouveau lecteur : IF roles_cible.lecteur = 'public' THEN SELECT array_agg(commande) INTO a FROM z_asgard.asgard_synthese_public_obj(s.oid, 'sequence'::text) ; ELSIF roles_cible.lecteur IS NOT NULL THEN SELECT array_agg(commande) INTO a FROM z_asgard.asgard_synthese_role_obj( s.oid, 'sequence'::text, quote_ident(roles_cible.lecteur)::regrole) ; END IF ; IF a IS NOT NULL THEN c_n_lecteur := array_cat(c_n_lecteur, a) ; a := NULL ; END IF ; -- autres rôles : -- public IF NOT 'public' = ANY (array_remove(ARRAY[roles.producteur, roles.lecteur, roles.editeur, roles_cible.producteur, roles_cible.lecteur, roles_cible.editeur], NULL)) THEN SELECT array_agg(format(commande, 'public')) INTO a FROM z_asgard.asgard_synthese_public_obj(s.oid, 'sequence'::text) ; IF FOUND THEN c_autres := array_cat(c_autres, a) ; a := NULL ; END IF ; END IF ; -- et le reste FOR r IN (SELECT rolname FROM pg_roles WHERE NOT rolname = ANY (array_remove(ARRAY[roles.producteur, roles.lecteur, roles.editeur, roles_cible.producteur, roles_cible.lecteur, roles_cible.editeur], NULL))) LOOP SELECT array_agg(format(commande, r.rolname::text)) INTO a FROM z_asgard.asgard_synthese_role_obj( s.oid, 'sequence'::text, quote_ident(r.rolname)::regrole) ; IF FOUND THEN c_autres := array_cat(c_autres, a) ; a := NULL ; END IF ; END LOOP ; END LOOP ; END IF ; ------ DEPLACEMENT DE L'OBJET ------ EXECUTE 'ALTER ' || obj_typ || ' ' || quote_ident(obj_schema) || '.' || quote_ident(obj_nom)|| ' SET SCHEMA ' || quote_ident(schema_cible) ; RAISE NOTICE '... Objet déplacé dans le schéma %.', schema_cible ; ------ PRIVILEGES DU PRODUCTEUR ------ -- par défaut, ils ont été transférés -- lors du changement de propriétaire, il -- n'y a donc qu'à réinitialiser pour les -- variantes 2 et 6 -- objet, réinitialisation pour 2 et 6 IF variante IN (2, 6) AND (c_producteur IS NOT NULL) THEN RAISE NOTICE 'réinitialisation des privilèges du nouveau producteur, % :', roles_cible.producteur ; FOREACH l IN ARRAY c_producteur LOOP l := z_asgard.asgard_grant_to_revoke(replace(l, quote_ident(obj_schema) || '.', quote_ident(schema_cible) || '.')) ; EXECUTE format(l, roles_cible.producteur) ; RAISE NOTICE '> %', format(l, roles_cible.producteur) ; END LOOP ; END IF ; ------- PRIVILEGES EDITEUR ------ -- révocation des privilèges du nouvel éditeur IF roles_cible.editeur IS NOT NULL AND (roles.editeur IS NULL OR NOT roles.editeur = roles_cible.editeur) AND NOT roles.producteur = roles_cible.editeur AND NOT variante = 4 AND c_n_editeur IS NOT NULL THEN RAISE NOTICE 'suppression des privilèges pré-existants du nouvel éditeur, % :', roles_cible.editeur ; FOREACH l IN ARRAY c_n_editeur LOOP l := z_asgard.asgard_grant_to_revoke(replace(l, quote_ident(obj_schema) || '.', quote_ident(schema_cible) || '.')) ; EXECUTE format(l, roles_cible.editeur) ; RAISE NOTICE '> %', format(l, roles_cible.editeur) ; END LOOP ; END IF ; -- révocation des privilèges de l'ancien éditeur IF roles.editeur IS NOT NULL AND NOT roles.editeur = roles_cible.producteur AND (roles_cible.editeur IS NULL OR NOT roles.editeur = roles_cible.editeur OR NOT variante IN (1,3)) AND NOT variante = 4 AND c_editeur IS NOT NULL THEN RAISE NOTICE 'suppression des privilèges de l''ancien éditeur, % :', roles.editeur ; FOREACH l IN ARRAY c_editeur LOOP l := z_asgard.asgard_grant_to_revoke(replace(l, quote_ident(obj_schema) || '.', quote_ident(schema_cible) || '.')) ; EXECUTE format(l, roles.editeur) ; RAISE NOTICE '> %', format(l, roles.editeur) ; END LOOP ; END IF ; -- reproduction sur le nouvel éditeur pour les variantes 1 et 3 IF roles.editeur IS NOT NULL AND roles_cible.editeur IS NOT NULL AND variante IN (1, 3) AND c_editeur IS NOT NULL AND NOT roles.editeur = roles_cible.editeur THEN RAISE NOTICE 'transfert des privilèges de l''ancien éditeur vers le nouvel éditeur, % :', roles_cible.editeur ; FOREACH l IN ARRAY c_editeur LOOP l := replace(l, quote_ident(obj_schema) || '.', quote_ident(schema_cible) || '.') ; EXECUTE format(l, roles_cible.editeur) ; RAISE NOTICE '> %', format(l, roles_cible.editeur) ; END LOOP ; END IF ; -- attribution des privilèges standard au nouvel éditeur -- pour les variantes 2, 5, 6 -- ou s'il n'y avait pas de lecteur sur l'ancien schéma IF roles_cible.editeur IS NOT NULL AND (variante IN (2, 5, 6) OR roles.editeur IS NULL) AND NOT variante = 4 THEN -- sur les tables : IF obj_typ IN ('table', 'view', 'materialized view', 'foreign table') THEN RAISE NOTICE 'application des privilèges standards pour le rôle éditeur du schéma :' ; l := 'GRANT SELECT, UPDATE, DELETE, INSERT ON TABLE ' || quote_ident(schema_cible) || '.' || quote_ident(obj_nom) || ' TO ' || quote_ident(roles_cible.editeur) ; EXECUTE l ; RAISE NOTICE '> %', l ; -- sur les séquences libres : ELSIF obj_typ IN ('sequence') THEN RAISE NOTICE 'application des privilèges standards pour le rôle éditeur du schéma :' ; l := 'GRANT SELECT, USAGE ON SEQUENCE ' || quote_ident(schema_cible) || '.' || quote_ident(obj_nom) || ' TO ' || quote_ident(roles_cible.editeur) ; EXECUTE l ; RAISE NOTICE '> %', l ; END IF ; -- sur les séquences des champs serial : IF seq_liste IS NOT NULL THEN FOREACH o IN ARRAY seq_liste LOOP l := 'GRANT SELECT, USAGE ON SEQUENCE ' || o::regclass::text || ' TO ' || quote_ident(roles_cible.editeur) ; EXECUTE l ; RAISE NOTICE '> %', l ; END LOOP ; END IF ; END IF ; ------- PRIVILEGES LECTEUR ------ -- révocation des privilèges du nouveau lecteur IF roles_cible.lecteur IS NOT NULL AND (roles.lecteur IS NULL OR NOT roles.lecteur = roles_cible.lecteur) AND NOT roles.producteur = roles_cible.lecteur AND (roles.editeur IS NULL OR NOT roles.editeur = roles_cible.lecteur) AND NOT variante = 4 AND c_n_lecteur IS NOT NULL THEN RAISE NOTICE 'suppression des privilèges pré-existants du nouveau lecteur, % :', roles_cible.lecteur ; FOREACH l IN ARRAY c_n_lecteur LOOP l := z_asgard.asgard_grant_to_revoke(replace(l, quote_ident(obj_schema) || '.', quote_ident(schema_cible) || '.')) ; EXECUTE format(l, roles_cible.lecteur) ; RAISE NOTICE '> %', format(l, roles_cible.lecteur) ; END LOOP ; END IF ; -- révocation des privilèges de l'ancien lecteur IF roles.lecteur IS NOT NULL AND NOT roles.lecteur = roles_cible.producteur AND (roles_cible.editeur IS NULL OR NOT roles.lecteur = roles_cible.editeur) AND (roles_cible.lecteur IS NULL OR NOT roles.lecteur = roles_cible.lecteur OR NOT variante IN (1,3)) AND NOT variante = 4 AND c_lecteur IS NOT NULL THEN RAISE NOTICE 'suppression des privilèges de l''ancien lecteur, % :', roles.lecteur ; FOREACH l IN ARRAY c_lecteur LOOP l := z_asgard.asgard_grant_to_revoke(replace(l, quote_ident(obj_schema) || '.', quote_ident(schema_cible) || '.')) ; EXECUTE format(l, roles.lecteur) ; RAISE NOTICE '> %', format(l, roles.lecteur) ; END LOOP ; END IF ; -- reproduction sur le nouveau lecteur pour les variantes 1 et 3 IF roles.lecteur IS NOT NULL AND roles_cible.lecteur IS NOT NULL AND variante IN (1, 3) AND c_lecteur IS NOT NULL AND NOT roles.lecteur = roles_cible.lecteur THEN RAISE NOTICE 'transfert des privilèges de l''ancien lecteur vers le nouveau lecteur, % :', roles_cible.lecteur ; FOREACH l IN ARRAY c_lecteur LOOP l := replace(l, quote_ident(obj_schema) || '.', quote_ident(schema_cible) || '.') ; EXECUTE format(l, roles_cible.lecteur) ; RAISE NOTICE '> %', format(l, roles_cible.lecteur) ; END LOOP ; END IF ; -- attribution des privilèges standard au nouveau lecteur -- pour les variantes 2, 5, 6 -- ou s'il n'y avait pas de lecteur sur l'ancien schéma IF roles_cible.lecteur IS NOT NULL AND (variante IN (2, 5, 6) OR roles.lecteur IS NULL) AND NOT variante = 4 THEN -- sur les tables : IF obj_typ IN ('table', 'view', 'materialized view', 'foreign table') THEN RAISE NOTICE 'application des privilèges standards pour le rôle lecteur du schéma :' ; l := 'GRANT SELECT ON TABLE ' || quote_ident(schema_cible) || '.' || quote_ident(obj_nom) || ' TO ' || quote_ident(roles_cible.lecteur) ; EXECUTE l ; RAISE NOTICE '> %', l ; -- sur les séquences libres : ELSIF obj_typ IN ('sequence') THEN RAISE NOTICE 'application des privilèges standards pour le rôle lecteur du schéma :' ; l := 'GRANT SELECT ON SEQUENCE ' || quote_ident(schema_cible) || '.' || quote_ident(obj_nom) || ' TO ' || quote_ident(roles_cible.lecteur) ; EXECUTE l ; RAISE NOTICE '> %', l ; END IF ; -- sur les séquences des champs serial : IF seq_liste IS NOT NULL THEN FOREACH o IN ARRAY seq_liste LOOP l := 'GRANT SELECT ON SEQUENCE ' || o::regclass::text || ' TO ' || quote_ident(roles_cible.lecteur) ; EXECUTE l ; RAISE NOTICE '> %', l ; END LOOP ; END IF ; END IF ; ------ AUTRES ROLES ------ -- pour les variantes 2, 3, 5, remise à zéro IF variante IN (2, 3, 5) AND c_autres IS NOT NULL THEN RAISE NOTICE 'remise à zéro des privilèges des autres rôles :' ; FOREACH l IN ARRAY c_autres LOOP l := z_asgard.asgard_grant_to_revoke(replace(l, quote_ident(obj_schema) || '.', quote_ident(schema_cible) || '.')) ; EXECUTE l ; RAISE NOTICE '> %', l ; END LOOP ; END IF ; RETURN '__ DEPLACEMENT REUSSI.' ; END $_$; ALTER FUNCTION z_asgard.asgard_deplace_obj(text, text, text, text, int) OWNER TO g_admin_ext ; COMMENT ON FUNCTION z_asgard.asgard_deplace_obj(text, text, text, text, int) IS 'ASGARD. Fonction qui prend en charge le déplacement d''un objet dans un nouveau schéma, avec une gestion propre des privilèges.' ; ------ 4.11 - OCTROI D'UN RÔLE À TOUS LES RÔLES DE CONNEXION ------ -- FUNCTION: z_asgard_admin.asgard_all_login_grant_role(text, boolean) CREATE OR REPLACE FUNCTION z_asgard_admin.asgard_all_login_grant_role(n_role text, b boolean DEFAULT True) RETURNS int LANGUAGE plpgsql AS $_$ /* OBJET : Cette fonction confère à tous les rôles de connexion du serveur l'appartenance au rôle donné en argument. ARGUMENTS : - n_role : une chaîne de caractères présumée correspondre à un nom de rôle valide ; - b : [optionnel] un booléen. Si b vaut False et qu'un rôle de connexion est déjà membre du rôle considéré par héritage, la fonction ne fait rien. Si b vaut True (défaut), la fonction ne passera un rôle de connexion que s'il est lui-même membre du rôle considéré. SORTIE : un entier correspondant au nombre de rôles pour lesquels la permission a été accordée. */ DECLARE roles record ; attributeur text ; utilisateur text := current_user ; c text ; n int := 0 ; BEGIN ------ TESTS PREALABLES ----- -- existance du rôle IF NOT n_role IN (SELECT rolname FROM pg_catalog.pg_roles) THEN RAISE EXCEPTION 'FLG1. Echec. Le rôle % n''existe pas', n_role ; END IF ; -- on cherche un rôle dont l'utilisateur est -- membre et qui, soit a l'attribut CREATEROLE -- soit a ADMIN OPTION sur le rôle SELECT rolname INTO attributeur FROM pg_roles WHERE pg_has_role(rolname, 'MEMBER') AND rolcreaterole ORDER BY rolname = current_user DESC ; IF NOT FOUND THEN SELECT grantee INTO attributeur FROM information_schema.applicable_roles WHERE is_grantable = 'YES' AND role_name = n_role ; IF NOT FOUND THEN RAISE EXCEPTION 'FLG2. Opération interdite. Permissions insuffisantes pour le rôle %.', n_role USING HINT = 'Votre rôle doit être membre de ' || n_role || ' avec admin option ou disposer de l''attribut CREATEROLE pour réaliser cette opération.' ; END IF ; END IF ; EXECUTE 'SET ROLE ' || quote_ident(attributeur) ; IF b THEN FOR roles IN SELECT rolname FROM pg_roles LEFT JOIN pg_auth_members ON member = pg_roles.oid AND roleid = n_role::regrole::oid WHERE rolcanlogin AND member IS NULL AND NOT rolsuper LOOP c := 'GRANT ' || quote_ident(n_role) || ' TO ' || quote_ident(roles.rolname) ; EXECUTE c ; RAISE NOTICE '> %', c ; n := n + 1 ; END LOOP ; ELSE FOR roles IN SELECT rolname FROM pg_roles WHERE rolcanlogin AND NOT pg_has_role(rolname, n_role, 'MEMBER') LOOP c := 'GRANT ' || quote_ident(n_role) || ' TO ' || quote_ident(roles.rolname) ; EXECUTE c ; RAISE NOTICE '> %', c ; n := n + 1 ; END LOOP ; END IF ; EXECUTE 'SET ROLE ' || quote_ident(utilisateur) ; RETURN n ; END $_$; ALTER FUNCTION z_asgard_admin.asgard_all_login_grant_role(text, boolean) OWNER TO g_admin ; COMMENT ON FUNCTION z_asgard_admin.asgard_all_login_grant_role(text, boolean) IS 'ASGARD. Fonction qui confère à tous les rôles de connexion du serveur l''appartenance au rôle donné en argument.' ; ------ 4.12 - IMPORT DE LA NOMENCLATURE DANS GESTION_SCHEMA ------ -- FUNCTION: z_asgard_admin.asgard_import_nomenclature(text[]) CREATE OR REPLACE FUNCTION z_asgard_admin.asgard_import_nomenclature( domaines text[] default NULL::text[] ) RETURNS text LANGUAGE plpgsql AS $_$ /* OBJET : Fonction qui importe dans la table de gestion les schémas manquants de la nomenclature nationale - ou de certains domaines de la nomenclature nationale listés en argument - toujours avec creation valant False, même si le schéma existe (mais n'a pas été référencé). Des messages informent l'opérateur des schémas effectivement ajoutés. Lorsque le schéma est déjà référencé dans la table de gestion, réappliquer la fonction a pour effet de mettre à jour les champs relatifs à la nomenclature. ARGUMENT : domaines (optionnel) : un tableau text[] contenant les noms des domaines à importer, soit le "niveau 1"/niv1 ou niv1_abr des schémas. Si non renseigné, toute la nomenclature est importée (hors schémas déjà référencés). SORTIE : '__ FIN IMPORT NOMENCLATURE.' si la requête s'est exécutée normalement. */ DECLARE item record ; e_mssg text ; e_detl text ; e_hint text ; BEGIN FOR item IN SELECT * FROM ( VALUES ('c', true, 'Données génériques', 'donnees_generique', 'Découpage électoral', 'decoupage_electoral', 'c_don_gen_decoupage_electoral', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Données génériques', 'donnees_generique', 'Démographie', 'demographie', 'c_don_gen_demographie', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Données génériques', 'donnees_generique', 'Habillage des cartes', 'habillage', 'c_don_gen_habillage', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Données génériques', 'donnees_generique', 'Intercommunalité', 'intercommunalite', 'c_don_gen_intercommunalite', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Données génériques', 'donnees_generique', 'Milieu physique', 'milieu_physique', 'c_don_gen_milieu_physique', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Eau', 'eau', 'Alimentation en eau potable', 'aep', 'c_eau_aep', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Eau', 'eau', 'Assainissement', 'assainissement', 'c_eau_assainissement', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Eau', 'eau', 'Masses d’eau', 'masse_eau', 'c_eau_masse_eau', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Eau', 'eau', 'Ouvrages', 'ouvrage', 'c_eau_ouvrage', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Eau', 'eau', 'Pêche', 'peche', 'c_eau_peche', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Eau', 'eau', 'Surveillance', 'surveillance', 'c_eau_surveillance', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Agriculture', 'agriculture', 'Environnement', 'agri_environnement', 'c_agri_environnement', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Agriculture', 'agriculture', 'Agro-alimentaire', 'agro_alimentaire', 'c_agri_agroalimentaire', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Agriculture', 'agriculture', 'Exploitation & élevage', 'exploitation_elevage', 'c_agri_exploi_elevage', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Agriculture', 'agriculture', 'Parcellaire agricole', 'parcellaire_agricole', 'c_agri_parcellaire_agricole', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Agriculture', 'agriculture', 'Santé animale', 'sante_animale', 'c_agri_sante_animale', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Agriculture', 'agriculture', 'Santé végétale', 'sante_vegetale', 'c_agri_sante_vegetale', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Risques', 'risque', 'Séismes', 'seisme', 'c_risque_seisme', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Agriculture', 'agriculture', 'Zonages agricoles', 'zonages_agricoles', 'c_agri_zonages_agricoles', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Air & climat', 'air_climat', 'Changement climatique', 'changement_climatique', 'c_air_clim_changement', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Air & climat', 'air_climat', 'Météorologie', 'meteo', 'c_air_clim_meteo', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Air & climat', 'air_climat', 'Qualité de l’air & pollution', 'qualité_pollution', 'c_air_clim_qual_polu', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Aménagement & urbanisme', 'amenagement_urbanisme', 'Assiettes des servitudes', 'assiette_servitude', 'c_amgt_urb_servitude', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Aménagement & urbanisme', 'amenagement_urbanisme', 'Politique européenne', 'politique_europeenne', 'c_amgt_urb_pol_euro', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Aménagement & urbanisme', 'amenagement_urbanisme', 'Zonages d’aménagement', 'zonages_amenagement', 'c_amgt_urb_zon_amgt', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Aménagement & urbanisme', 'amenagement_urbanisme', 'Zonages d’études', 'zonages_etudes', 'c_amgt_urb_zon_etudes', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Aménagement & urbanisme', 'amenagement_urbanisme', 'Zonages de planification', 'zonages_planification', 'c_amgt_urb_zon_plan', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Culture, société & services', 'culture_societe_service', 'Enseignement', 'enseignement', 'c_cult_soc_ser_enseignement', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Culture, société & services', 'culture_societe_service', 'Équipements sportifs et culturels', 'equipement_sportif_culturel', 'c_cult_soc_ser_equip_sport_cult', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Culture, société & services', 'culture_societe_service', 'Autres établissements', 'erp_autre', 'c_cult_soc_ser_erp_autre', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Culture, société & services', 'culture_societe_service', 'Patrimoine culturel', 'patrimoine_culturel', 'c_cult_soc_ser_patrim_cult', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Culture, société & services', 'culture_societe_service', 'Santé & social', 'sante_social', 'c_cult_soc_ser_sante_social', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Culture, société & services', 'culture_societe_service', 'Tourisme', 'tourisme', 'c_cult_soc_ser_tourisme', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Données génériques', 'donnees_generique', 'Action publique', 'action_publique', 'c_don_gen_action_publique', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Données génériques', 'donnees_generique', 'Découpage administratif', 'administratif', 'c_don_gen_administratif', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Eau', 'eau', 'Travaux & entretien', 'travail_action', 'c_eau_travail_action', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Eau', 'eau', 'Autres utilisations', 'utilisation_autre', 'c_eau_utilisation_autre', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Eau', 'eau', 'Zonages eau', 'zonages_eau', 'c_eau_zonages', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Foncier & sol', 'foncier_sol', 'Foncier agricole', 'foncier_agricole', 'c_fon_sol_agricole', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Foncier & sol', 'foncier_sol', 'Mutations foncières', 'mutation_fonciere', 'c_fon_sol_mutation', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Foncier & sol', 'foncier_sol', 'Occupation du sol', 'occupation_sol', 'c_fon_sol_occupation', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Foncier & sol', 'foncier_sol', 'Propriétés foncières', 'propriete_fonciere', 'c_fon_sol_propriete', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Forêt', 'foret', 'Description', 'description', 'c_foret_description', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Forêt', 'foret', 'Défense de la forêt contre les incendies', 'dfci', 'c_foret_dfci', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Forêt', 'foret', 'Gestion', 'gestion', 'c_foret_gestion', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Forêt', 'foret', 'Règlement', 'reglement', 'c_foret_reglement', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Forêt', 'foret', 'Transformation', 'transformation', 'c_foret_transformation', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Habitat & politique de la ville', 'habitat_politique_de_la_ville', 'Accession à la propriété', 'accession_propriete', 'c_hab_vil_access_propriete', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Habitat & politique de la ville', 'habitat_politique_de_la_ville', 'Besoin en logements', 'besoin_en_logement', 'c_hab_vil_besoin_logt', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Habitat & politique de la ville', 'habitat_politique_de_la_ville', 'Construction', 'construction', 'c_hab_vil_construction', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Habitat & politique de la ville', 'habitat_politique_de_la_ville', 'Habitat indigne', 'habitat_indigne', 'c_hab_vil_habitat_indigne', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Habitat & politique de la ville', 'habitat_politique_de_la_ville', 'Occupation des logements', 'occupation_logements', 'c_hab_vil_occupation_logt', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Habitat & politique de la ville', 'habitat_politique_de_la_ville', 'Parc locatif social', 'parc_locatif_social', 'c_hab_vil_parc_loc_social', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Habitat & politique de la ville', 'habitat_politique_de_la_ville', 'Parc de logements', 'parc_logements', 'c_hab_vil_parc_logt', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Habitat & politique de la ville', 'habitat_politique_de_la_ville', 'Politique', 'politique', 'c_hab_vil_politique', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Habitat & politique de la ville', 'habitat_politique_de_la_ville', 'Rénovation', 'renovation', 'c_hab_vil_renovation', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Mer & littoral', 'mer_littoral', 'Autres activités', 'autres_activites', 'c_mer_litt_autres_activites', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Mer & littoral', 'mer_littoral', 'Chasse maritime', 'chasse_maritime', 'c_mer_litt_chasse_maritime', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Mer & littoral', 'mer_littoral', 'Culture marine', 'culture_marine', 'c_mer_litt_culture_marine', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Mer & littoral', 'mer_littoral', 'Écologie du littoral', 'ecologie_littoral', 'c_mer_litt_ecol_littoral', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Mer & littoral', 'mer_littoral', 'Limites administratives spéciales', 'lim_admin_speciale', 'c_mer_litt_lim_admin_spe', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Mer & littoral', 'mer_littoral', 'Lutte anti-pollution', 'lutte_anti_pollution', 'c_mer_litt_lutte_anti_pollu', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Mer & littoral', 'mer_littoral', 'Navigation maritime', 'navigation_maritime', 'c_mer_litt_nav_maritime', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Mer & littoral', 'mer_littoral', 'Pêche maritime', 'peche_maritime', 'c_mer_litt_peche_maritime', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Mer & littoral', 'mer_littoral', 'Topographie', 'topographie', 'c_mer_litt_topographie', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Nature, paysage & biodiversité', 'nature_paysage_biodiversite', 'Chasse', 'chasse', 'c_nat_pays_bio_chasse', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Nature, paysage & biodiversité', 'nature_paysage_biodiversite', 'Inventaires nature & biodiversité', 'inventaire_nature_biodiversite', 'c_nat_pays_bio_invent_nat_bio', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Nature, paysage & biodiversité', 'nature_paysage_biodiversite', 'Inventaires paysages', 'inventaire_paysage', 'c_nat_pays_bio_invent_pays', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Nature, paysage & biodiversité', 'nature_paysage_biodiversite', 'Zonages nature', 'zonage_nature', 'c_nat_pays_bio_zonage_nat', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Nature, paysage & biodiversité', 'nature_paysage_biodiversite', 'Zonages paysages', 'zonage_paysage', 'c_nat_pays_bio_zonage_pays', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Nuisances', 'nuisance', 'Bruit', 'bruit', 'c_nuis_bruit', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Nuisances', 'nuisance', 'Déchets', 'dechet', 'c_nuis_dechet', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Nuisances', 'nuisance', 'Nuisances électromagnétiques', 'nuisance_electromagnetique', 'c_nuis_electromag', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Nuisances', 'nuisance', 'Pollution des sols', 'pollution_sol', 'c_nuis_pollu_sol', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Réseaux & énergie', 'reseau_energie_divers', 'Aménagement numérique du territoire', 'amenagement_numerique_territoire', 'c_res_energ_amgt_num_terri', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Réseaux & énergie', 'reseau_energie_divers', 'Autre', 'autre', 'c_res_energ_autre', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Réseaux & énergie', 'reseau_energie_divers', 'Électricité', 'electricite', 'c_res_energ_electricite', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Réseaux & énergie', 'reseau_energie_divers', 'Hydrocarbures', 'hydrocarbure', 'c_res_energ_hydrocarbure', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Réseaux & énergie', 'reseau_energie_divers', 'Télécommunications', 'telecommunication', 'c_res_energ_telecom', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Risques', 'risque', 'Avalanche', 'avalanche', 'c_risque_avalanche', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Risques', 'risque', 'Éruptions volcaniques', 'eruption_volcanique', 'c_risque_eruption_volcanique', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Risques', 'risque', 'Gestion des risques', 'gestion_risque', 'c_risque_gestion_risque', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Risques', 'risque', 'Inondations', 'inondation', 'c_risque_inondation', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Risques', 'risque', 'Mouvements de terrain', 'mouvement_terrain', 'c_risque_mouvement_terrain', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Risques', 'risque', 'Radon', 'radon', 'c_risque_radon', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Risques', 'risque', 'Risques miniers', 'risque_minier', 'c_risque_minier', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Risques', 'risque', 'Risques technologiques', 'risque_technologique', 'c_risque_techno', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Risques', 'risque', 'Zonages risques naturels', 'zonages_risque_naturel', 'c_risque_zonages_naturel', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Risques', 'risque', 'Zonages risques technologiques', 'zonages_risque_technologique', 'c_risque_zonages_techno', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Sites industriels & production', 'site_industriel_production', 'Mines, carrières & granulats', 'mine_carriere_granulats', 'c_indus_prod_mine_carriere_granul', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Sites industriels & production', 'site_industriel_production', 'Sites éoliens', 'site_eolien', 'c_indus_prod_eolien', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Sites industriels & production', 'site_industriel_production', 'Sites industriels', 'site_industriel', 'c_indus_prod_industriel', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Sites industriels & production', 'site_industriel_production', 'Sites de production d’énergie', 'site_production_energie', 'c_indus_prod_prod_energ', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Socio-économie', 'socio_economie', ' ', ' ', 'c_socio_eco_', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Déplacements', 'transport_deplacement', 'Sécurité routière', 'securite_routiere', 'c_tr_depl_securite_routiere', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Déplacements', 'transport_deplacement', 'Transport collectif', 'tr_collectif', 'c_tr_depl_collectif', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Déplacements', 'transport_deplacement', 'Transport exceptionnel', 'tr_exceptionnel', 'c_tr_depl_exceptionnel', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Déplacements', 'transport_deplacement', 'Transport de marchandises', 'tr_marchandise', 'c_tr_depl_marchandise', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Déplacements', 'transport_deplacement', 'Transport de matières dangereuses', 'tr_matiere_dangereuse', 'c_tr_depl_mat_dangereuse', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Déplacements', 'transport_deplacement', 'Trafic', 'trafic', 'c_tr_depl_trafic', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Infrastructures de transport', 'transport_infrastructure', 'Aérien', 'aerien', 'c_tr_infra_aerien', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Infrastructures de transport', 'transport_infrastructure', 'Circulation douce', 'circulation_douce', 'c_tr_infra_circulation_douce', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Infrastructures de transport', 'transport_infrastructure', 'Ferroviaire', 'ferroviaire', 'c_tr_infra_ferroviaire', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Infrastructures de transport', 'transport_infrastructure', 'Fluvial', 'fluvial', 'c_tr_infra_fluvial', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Infrastructures de transport', 'transport_infrastructure', 'Maritime', 'maritime', 'c_tr_infra_maritime', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Infrastructures de transport', 'transport_infrastructure', 'Plateformes multimodales', 'plateforme_multimodale', 'c_tr_infra_plateforme_multimod', false, 'g_admin', NULL, 'g_consult'), ('c', true, 'Infrastructures de transport', 'transport_infrastructure', 'Routier', 'routier', 'c_tr_infra_routier', false, 'g_admin', NULL, 'g_consult') ) AS t (bloc, nomenclature, niv1, niv1_abr, niv2, niv2_abr, nom_schema, creation, producteur, editeur, lecteur) WHERE domaines IS NULL OR niv1 = ANY(domaines) OR niv1_abr = ANY(domaines) LOOP -- si le schéma n'était pas déjà référencé, il est ajouté -- (toujours comme non créé, même s'il existe par ailleurs dans la base) IF NOT item.nom_schema IN (SELECT gestion_schema_usr.nom_schema FROM z_asgard.gestion_schema_usr) THEN INSERT INTO z_asgard.gestion_schema_usr (bloc, nomenclature, niv1, niv1_abr, niv2, niv2_abr, nom_schema, creation, producteur, editeur, lecteur) VALUES (item.bloc, item.nomenclature, item.niv1, item.niv1_abr, item.niv2, item.niv2_abr, item.nom_schema, item.creation, item.producteur, item.editeur, item.lecteur) ; RAISE NOTICE 'Le schéma % a été ajouté à la table de gestion.', item.nom_schema ; -- sinon les champs de la nomenclature sont simplement mis à jour, le cas échéant ELSIF item.nom_schema IN (SELECT gestion_schema_usr.nom_schema FROM z_asgard.gestion_schema_usr) THEN UPDATE z_asgard.gestion_schema_usr SET nomenclature = item.nomenclature, niv1 = item.niv1, niv1_abr = item.niv1_abr, niv2 = item.niv2, niv2_abr = item.niv2_abr WHERE gestion_schema_usr.nom_schema = item.nom_schema AND (NOT nomenclature = item.nomenclature OR NOT coalesce(gestion_schema_usr.niv1, '') = coalesce(item.niv1, '') OR NOT coalesce(gestion_schema_usr.niv1_abr, '') = coalesce(item.niv1_abr, '') OR NOT coalesce(gestion_schema_usr.niv2, '') = coalesce(item.niv2, '') OR NOT coalesce(gestion_schema_usr.niv2_abr, '') = coalesce(item.niv2_abr, '')) ; IF FOUND THEN RAISE NOTICE 'Les champs de la nomenclature ont été mis à jour pour le schéma %.', item.nom_schema ; END IF ; END IF ; END LOOP ; RETURN '__ FIN IMPORT NOMENCLATURE.' ; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS e_mssg = MESSAGE_TEXT, e_hint = PG_EXCEPTION_HINT, e_detl = PG_EXCEPTION_DETAIL ; RAISE EXCEPTION 'FIN0 > %', e_mssg USING DETAIL = e_detl, HINT = e_hint ; END $_$; ALTER FUNCTION z_asgard_admin.asgard_import_nomenclature(text[]) OWNER TO g_admin ; COMMENT ON FUNCTION z_asgard_admin.asgard_import_nomenclature(text[]) IS 'ASGARD. Fonction qui importe dans la table de gestion les schémas manquants de la nomenclature nationale - ou de certains domaines de la nomenclature nationale listés en argument.' ; ------ 4.13 - REAFFECTATION DES PRIVILEGES D'UN RÔLE ------ -- FUNCTION: z_asgard_admin.asgard_reaffecte_role(text, text, boolean, boolean) CREATE OR REPLACE FUNCTION z_asgard_admin.asgard_reaffecte_role( n_role text, n_role_cible text DEFAULT NULL, b_hors_asgard boolean DEFAULT False, b_privileges boolean DEFAULT True) RETURNS text LANGUAGE plpgsql AS $_$ /* OBJET : Cette fonction transfère tous les privilèges d'un rôle à un autre, et en premier lieu ses fonctions de producteur, éditeur et lecteur. Si aucun rôle cible n'est spécifié, les privilèges sont simplement supprimés et g_admin devient producteur des schémas, le cas échéant. ARGUMENTS : - n_role : une chaîne de caractères présumée correspondre à un nom de rôle valide ; - n_role_cible : une chaîne de caractères présumée correspondre à un nom de rôle valide ; - b_hors_asgard : un booléen, valeur par défaut False. Si ce paramètre vaut True, la propriété et les privilèges sur les objets des schémas non gérés par ASGARD ou hors schémas (par ex la base), sont pris en compte. La propriété des objets reviendra à g_admin si aucun rôle cible n'est spécifié ; - b_privileges : un booléen, valeur par défaut True. Indique si, dans l'hypothèse où le rôle cible est spécifié, celui-ci doit recevoir les privilèges et propriétés du rôle (True) ou seulement ses propriétés (False). SORTIE : '__ REAFFECTATION REUSSIE' si la fonction s'est exécutée sans erreur. */ DECLARE item record ; n_producteur_cible text := coalesce(n_role_cible, 'g_admin') ; c record ; k int ; BEGIN ------ TESTS PREALABLES ----- -- existance du rôle IF NOT n_role IN (SELECT rolname FROM pg_catalog.pg_roles) THEN RAISE EXCEPTION 'FRR1. Echec. Le rôle % n''existe pas', n_role ; END IF ; -- existance du rôle cible IF n_role_cible IS NOT NULL AND NOT n_role_cible IN (SELECT rolname FROM pg_catalog.pg_roles) THEN RAISE EXCEPTION 'FRR2. Echec. Le rôle % n''existe pas', n_role_cible ; END IF ; IF NOT b_privileges THEN n_role_cible := NULL ; END IF ; ------ FONCTION DE PRODUCTEUR ------ FOR item IN (SELECT * FROM z_asgard.gestion_schema_usr WHERE producteur = n_role) LOOP IF item.editeur = n_producteur_cible THEN UPDATE z_asgard.gestion_schema_usr SET editeur = NULL WHERE nom_schema = item.nom_schema ; RAISE NOTICE '... L''éditeur du schéma % a été supprimé.', item.nom_schema ; END IF ; IF item.lecteur = n_producteur_cible THEN UPDATE z_asgard.gestion_schema_usr SET lecteur = NULL WHERE nom_schema = item.nom_schema ; RAISE NOTICE '... Le lecteur du schéma % a été supprimé.', item.nom_schema ; END IF ; UPDATE z_asgard.gestion_schema_usr SET producteur = n_role_cible WHERE nom_schema = item.nom_schema ; RAISE NOTICE '... Le producteur du schéma % a été redéfini.', item.nom_schema ; END LOOP ; ------ FONCTION D'EDITEUR ------ -- seulement si le rôle cible n'est pas déjà producteur du schéma FOR item IN (SELECT * FROM z_asgard.gestion_schema_usr WHERE editeur = n_role) LOOP IF item.producteur = n_role_cible THEN RAISE NOTICE 'Le rôle cible est actuellement producteur du schéma %.', item.nom_schema ; UPDATE z_asgard.gestion_schema_usr SET editeur = NULL WHERE nom_schema = item.nom_schema ; RAISE NOTICE '... L''éditeur du schéma % a été supprimé.', item.nom_schema ; ELSE IF item.lecteur = n_role_cible THEN UPDATE z_asgard.gestion_schema_usr SET lecteur = NULL WHERE nom_schema = item.nom_schema ; RAISE NOTICE '... Le lecteur du schéma % a été supprimé.', item.nom_schema ; END IF ; UPDATE z_asgard.gestion_schema_usr SET editeur = n_role_cible WHERE nom_schema = item.nom_schema ; RAISE NOTICE '... L''éditeur du schéma % a été redéfini.', item.nom_schema ; END IF ; END LOOP ; ------ FONCTION DE LECTEUR ------ -- seulement si le rôle cible n'est pas déjà producteur ou éditeur du schéma FOR item IN (SELECT * FROM z_asgard.gestion_schema_usr WHERE lecteur = n_role) LOOP IF item.producteur = n_role_cible THEN RAISE NOTICE 'Le rôle cible est actuellement producteur du schéma %.', item.nom_schema ; UPDATE z_asgard.gestion_schema_usr SET lecteur = NULL WHERE nom_schema = item.nom_schema ; RAISE NOTICE '... Le lecteur du schéma % a été supprimé.', item.nom_schema ; ELSIF item.editeur = n_role_cible THEN RAISE NOTICE 'Le rôle cible est actuellement éditeur du schéma %.', item.nom_schema ; UPDATE z_asgard.gestion_schema_usr SET lecteur = NULL WHERE nom_schema = item.nom_schema ; RAISE NOTICE '... Le lecteur du schéma % a été supprimé.', item.nom_schema ; ELSE UPDATE z_asgard.gestion_schema_usr SET lecteur = n_role_cible WHERE nom_schema = item.nom_schema ; RAISE NOTICE '... Le lecteur du schéma % a été redéfini.', item.nom_schema ; END IF ; END LOOP ; ------ PROPRIETES HORS ASGARD ------ IF b_hors_asgard THEN EXECUTE 'REASSIGN OWNED BY ' || quote_ident(n_role) || ' TO ' || quote_ident(n_producteur_cible) ; RAISE NOTICE '> %', 'REASSIGN OWNED BY ' || quote_ident(n_role) || ' TO ' || quote_ident(n_producteur_cible) ; RAISE NOTICE '... Le cas échéant, la propriété des objets hors schémas référencés par ASGARD a été réaffectée.' ; END IF ; ------ PRIVILEGES RESIDUELS SUR LES SCHEMAS D'ASGARD ------- k := 0 ; FOR item IN (SELECT * FROM z_asgard.gestion_schema_usr WHERE creation) LOOP FOR c IN (SELECT * FROM z_asgard.asgard_synthese_role( quote_ident(item.nom_schema)::regnamespace, quote_ident(n_role)::regrole)) LOOP EXECUTE format(z_asgard.asgard_grant_to_revoke(c.commande), n_role) ; RAISE NOTICE '> %', format(z_asgard.asgard_grant_to_revoke(c.commande), n_role) ; IF n_role_cible IS NOT NULL THEN EXECUTE format(c.commande, n_role_cible) ; RAISE NOTICE '> %', format(c.commande, n_role_cible) ; END IF ; k := k + 1 ; END LOOP ; END LOOP ; IF k > 0 THEN IF n_role_cible IS NULL THEN RAISE NOTICE '... Les privilèges résiduels du rôle % sur les schémas référencés par ASGARD ont été révoqués.', n_role ; ELSE RAISE NOTICE '... Les privilèges résiduels du rôle % sur les schémas référencés par ASGARD ont été réaffectés.', n_role ; END IF ; END IF ; ------ PRIVILEGES RESIDUELS SUR LES SCHEMAS HORS ASGARD ------ IF b_hors_asgard THEN k := 0 ; FOR item IN (SELECT * FROM pg_catalog.pg_namespace LEFT JOIN z_asgard.gestion_schema_usr ON nspname::text = nom_schema AND creation WHERE nom_schema IS NULL) LOOP FOR c IN (SELECT * FROM z_asgard.asgard_synthese_role( quote_ident(item.nspname::text)::regnamespace, quote_ident(n_role)::regrole)) LOOP EXECUTE format(z_asgard.asgard_grant_to_revoke(c.commande), n_role) ; RAISE NOTICE '> %', format(z_asgard.asgard_grant_to_revoke(c.commande), n_role) ; IF n_role_cible IS NOT NULL THEN EXECUTE format(c.commande, n_role_cible) ; RAISE NOTICE '> %', format(c.commande, n_role_cible) ; END IF ; k := k + 1 ; END LOOP ; END LOOP ; IF k > 0 THEN IF n_role_cible IS NULL THEN RAISE NOTICE '... Les privilèges résiduels du rôle % sur les schémas non référencés par ASGARD ont été révoqués.', n_role ; ELSE RAISE NOTICE '... Les privilèges résiduels du rôle % sur les schémas non référencés par ASGARD ont été réaffectés.', n_role ; END IF ; END IF ; END IF ; ------- OBJETS HORS SCHEMAS ------ IF b_hors_asgard THEN k := 0 ; FOR c IN ( WITH t_acl AS ( -- bases de données SELECT 'DATABASE'::text AS type_obj, datname::text AS n_obj, unnest(datacl)::text AS acl FROM pg_catalog.pg_database WHERE datacl IS NOT NULL UNION -- tablespaces SELECT 'TABLESPACE'::text AS type_obj, spcname::text AS n_obj, unnest(spcacl)::text AS acl FROM pg_catalog.pg_tablespace WHERE spcacl IS NOT NULL UNION -- foreign data wrappers SELECT 'FOREIGN DATA WRAPPER'::text AS type_obj, fdwname::text AS n_obj, unnest(fdwacl)::text AS acl FROM pg_catalog.pg_foreign_data_wrapper WHERE fdwacl IS NOT NULL UNION -- foreign servers SELECT 'FOREIGN SERVER'::text AS type_obj, srvname::text AS n_obj, unnest(srvacl)::text AS acl FROM pg_catalog.pg_foreign_server WHERE srvacl IS NOT NULL UNION -- langages SELECT 'LANGUAGE'::text AS type_obj, lanname::text AS n_obj, unnest(lanacl)::text AS acl FROM pg_catalog.pg_language WHERE lanacl IS NOT NULL UNION -- large objects SELECT 'LARGE OBJECT'::text AS type_obj, pg_largeobject_metadata.oid::text AS n_obj, unnest(lomacl)::text AS acl FROM pg_catalog.pg_largeobject_metadata WHERE lomacl IS NOT NULL ) SELECT 'GRANT ' || privilege || ' ON ' || type_obj || ' ' || quote_ident(n_obj) || ' TO %I' AS commande FROM t_acl, unnest(ARRAY['CREATE', 'CONNECT', 'TEMPORARY', 'USAGE', 'SELECT', 'UPDATE'], ARRAY['C', 'c', 'T', 'U', 'r', 'w']) AS l (privilege, prvlg) WHERE acl ~ ('^' || quote_ident(n_role)::regrole::text || '[=].*' || prvlg || '.*[/]') ) LOOP EXECUTE format(z_asgard.asgard_grant_to_revoke(c.commande), n_role) ; RAISE NOTICE '> %', format(z_asgard.asgard_grant_to_revoke(c.commande), n_role) ; IF n_role_cible IS NOT NULL THEN EXECUTE format(c.commande, n_role_cible) ; RAISE NOTICE '> %', format(c.commande, n_role_cible) ; END IF ; k := k + 1 ; END LOOP ; IF k > 0 THEN IF n_role_cible IS NULL THEN RAISE NOTICE '... Les privilèges résiduels du rôle % sur les objets hors schémas ont été révoqués.', n_role ; ELSE RAISE NOTICE '... Les privilèges résiduels du rôle % sur les objets hors schémas ont été réaffectés.', n_role ; END IF ; END IF ; END IF ; RETURN '__ REAFFECTATION REUSSIE' ; END $_$; ALTER FUNCTION z_asgard_admin.asgard_reaffecte_role(text, text, boolean, boolean) OWNER TO g_admin ; COMMENT ON FUNCTION z_asgard_admin.asgard_reaffecte_role(text, text, boolean, boolean) IS 'ASGARD. Fonction qui réaffecte les privilèges et propriétés d''un rôle à un autre.' ; ------ 4.14 - REINITIALISATION DES PRIVILEGES SUR TOUS LES SCHEMAS ------ -- FUNCTION: z_asgard_admin.asgard_initialise_all_schemas(integer) CREATE OR REPLACE FUNCTION z_asgard_admin.asgard_initialise_all_schemas(variante integer DEFAULT 0) RETURNS varchar[] LANGUAGE plpgsql AS $_$ /* OBJET : Cette fonction réinitialise les privilèges sur tous les schémas référencés par ASGARD en une seule commande. Pour les schémas d'ASGARD, même s'ils n'ont pas été référencés, les droits nécessaires au bon fonctionnement du système seront rétablis. ARGUMENTS : un entier optionnel - 0 par défaut. Si 1, la fonction ne fera que s'assurer que tous les objets appartiennent au propriétaire du schéma. Si 2, la fonction ne s'exécutera que sur les schémas d'ASGARD. SORTIE : NULL si la requête s'est exécutée normalement, sinon la liste des schémas qui n'ont pas pu être traités. Se reporter dans ce cas à l'onglet des messages pour le détail des erreurs. */ DECLARE s record ; l varchar[] ; b boolean ; k integer ; e_mssg text ; e_detl text ; e_hint text ; utilisateur text := current_user::text ; v_prop oid ; t text ; BEGIN ------ CONTROLES PREALABLES ------ -- la fonction est dans z_asgard_admin, donc seuls les membres de -- g_admin devraient pouvoir y accéder, mais au cas où : IF NOT pg_has_role('g_admin', 'USAGE') THEN RAISE EXCEPTION 'FAS1. Opération interdite. Vous devez être membre de g_admin pour exécuter cette fonction.' ; END IF ; IF NOT utilisateur IN (SELECT rolname FROM pg_catalog.pg_roles WHERE rolsuper) THEN SET ROLE g_admin ; END IF ; -- permission manquante du propriétaire de la vue gestion_schema_usr -- (en principe g_admin_ext) sur le schéma z_asgard_admin ou la table -- gestion_schema : SELECT relowner INTO v_prop FROM pg_catalog.pg_class WHERE relname = 'gestion_schema_usr' AND relnamespace = 'z_asgard'::regnamespace::oid ; IF NOT FOUND THEN RAISE EXCEPTION 'FAS2. Echec. La vue gestion_schema_usr est introuvable.' ; END IF ; IF NOT has_schema_privilege(v_prop, 'z_asgard_admin', 'USAGE') OR NOT has_table_privilege(v_prop, 'z_asgard_admin.gestion_schema', 'SELECT') THEN RAISE NOTICE '(temporaire) droits a minima pour le propriétaire de la vue gestion_schema_usr :' ; IF NOT has_schema_privilege(v_prop, 'z_asgard_admin', 'USAGE') THEN t := 'GRANT USAGE ON SCHEMA z_asgard_admin TO ' || v_prop::regrole::text ; EXECUTE t ; RAISE NOTICE '> %', t ; END IF ; IF NOT has_table_privilege(v_prop, 'z_asgard_admin.gestion_schema', 'SELECT') THEN t := 'GRANT SELECT ON TABLE z_asgard_admin.gestion_schema TO ' || v_prop::regrole::text ; EXECUTE t ; RAISE NOTICE '> %', t ; END IF ; RAISE NOTICE '---------------------------------' ; END IF ; ------ NETTOYAGE ------ FOR s IN ( SELECT 2 AS n, nom_schema, producteur FROM z_asgard.gestion_schema_usr WHERE creation AND NOT nom_schema IN ('z_asgard', 'z_asgard_admin') UNION VALUES (1, 'z_asgard', 'g_admin_ext'), (0, 'z_asgard_admin', 'g_admin') ORDER BY n, nom_schema ) LOOP b := True ; IF s.n < 2 OR variante < 2 THEN ------ CONTROLE DES PRIVILEGES DE G_ADMIN SUR LE PRODUCTEUR ------ -- si g_admin n'est pas membre du producteur, alors on l'en rend -- membre, sous réserve que ce ne soit pas un super-utilisateur ou -- un rôle de connexion (ce dernier cas n'étant pas supposé arriver, -- sauf désactivation temporaire de triggers ou à avoir donné -- LOGIN au rôle après l'avoir désigné comme producteur). IF NOT pg_has_role(s.producteur, 'USAGE') THEN -- propriétaire super-utilisateur IF s.producteur IN (SELECT rolname FROM pg_catalog.pg_roles WHERE rolsuper) THEN RAISE NOTICE '... ECHEC. Schéma % non traité.', s.nom_schema USING DETAIL = 'Seul un super-utilisateur est habilité à intervenir sur ce schéma. Producteur : ' || s.producteur || '.', HINT = 'Veuillez relancer la fonction en tant que super-utilisateur.' ; b := False ; l := array_append(l, s.nom_schema) ; -- propriétaire rôle de connexion ELSIF s.producteur IN (SELECT rolname FROM pg_catalog.pg_roles WHERE rolcanlogin) THEN RAISE NOTICE '... ECHEC. Schéma % non traité.', s.nom_schema USING DETAIL = 'Le producteur du schéma est un rôle de connexion. Producteur : ' || s.producteur || '.', HINT = 'Veuillez relancer la fonction en tant que super-utilisateur ou après avoir désigné un rôle de groupe comme producteur.' ; b := False ; l := array_append(l, s.nom_schema) ; -- rôle de groupe lambda sur lequel g_admin n'a pas de permission -- on la lui donne et on continue ELSE EXECUTE 'GRANT ' || quote_ident(s.producteur) || ' TO g_admin' ; RAISE NOTICE '... Permission accordée à g_admin sur le rôle %.', s.producteur ; END IF ; END IF ; IF b THEN BEGIN IF variante = 1 THEN -- lancement de la fonction de nettoyage des propriétaires IF quote_ident(s.producteur) IN (SELECT nspowner::regrole::text FROM pg_catalog.pg_namespace WHERE nspname = s.nom_schema) THEN -- version objets seuls si le propriétaire du schéma est bon RAISE NOTICE '(ré)attribution de la propriété des objets au rôle producteur du schéma :' ; SELECT z_asgard.asgard_admin_proprietaire(s.nom_schema, s.producteur, False) INTO k ; IF k = 0 THEN RAISE NOTICE '> néant' ; END IF ; ELSE -- version schéma + objets sinon RAISE NOTICE '(ré)attribution de la propriété du schéma et des objets au rôle producteur du schéma :' ; PERFORM z_asgard.asgard_admin_proprietaire(s.nom_schema, s.producteur) ; END IF ; ELSE -- lancement de la fonction de réinitialisation des droits PERFORM z_asgard.asgard_initialise_schema(s.nom_schema) ; END IF ; RAISE NOTICE '... Le schéma % a été traité', s.nom_schema ; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS e_mssg = MESSAGE_TEXT, e_hint = PG_EXCEPTION_HINT, e_detl = PG_EXCEPTION_DETAIL ; RAISE NOTICE '... ECHEC. Schéma % non traité.', s.nom_schema ; RAISE NOTICE 'FAS0 > %', e_mssg USING DETAIL = e_detl, HINT = e_hint ; l := array_append(l, s.nom_schema) ; END ; END IF ; RAISE NOTICE '---------------------------------' ; END IF ; END LOOP ; EXECUTE 'SET ROLE ' || quote_ident(utilisateur) ; ------ RESULTAT ------ RETURN l ; END $_$; ALTER FUNCTION z_asgard_admin.asgard_initialise_all_schemas(integer) OWNER TO g_admin ; COMMENT ON FUNCTION z_asgard_admin.asgard_initialise_all_schemas(integer) IS 'ASGARD. Fonction qui réinitialise les droits sur l''ensemble des schémas référencés.' ;