|
| 1 | +-- complain if script is sourced in psql, rather than via CREATE EXTENSION |
| 2 | +\echo Use "ALTER EXTENSION pg_permissions UPDATE" to load this file. \quit |
| 3 | + |
| 4 | +/* rename some view columns to match the other views */ |
| 5 | + |
| 6 | +DROP VIEW all_permissions; |
| 7 | +DROP VIEW schema_permissions; |
| 8 | +DROP VIEW database_permissions; |
| 9 | + |
| 10 | +CREATE VIEW schema_permissions AS |
| 11 | +SELECT obj_type 'SCHEMA' AS object_type, |
| 12 | + r.rolname AS role_name, |
| 13 | + n.nspname AS schema_name, |
| 14 | + NULL::text AS object_name, |
| 15 | + NULL::name AS column_name, |
| 16 | + p.perm::perm_type AS permission, |
| 17 | + has_schema_privilege(r.oid, n.oid, p.perm) AS granted |
| 18 | +FROM pg_catalog.pg_namespace AS n |
| 19 | + CROSS JOIN pg_catalog.pg_roles AS r |
| 20 | + CROSS JOIN (VALUES ('USAGE'), ('CREATE')) AS p(perm) |
| 21 | +WHERE n.nspname <> 'information_schema' |
| 22 | + AND n.nspname NOT LIKE 'pg_%' |
| 23 | + AND NOT r.rolsuper; |
| 24 | + |
| 25 | +GRANT SELECT ON schema_permissions TO PUBLIC; |
| 26 | + |
| 27 | +CREATE VIEW database_permissions AS |
| 28 | + WITH list AS (SELECT unnest AS perm |
| 29 | + FROM unnest ('{"CREATE", "CONNECT", "TEMPORARY"}'::text[])) |
| 30 | +SELECT obj_type 'DATABASE' AS object_type, |
| 31 | + r.rolname AS role_name, |
| 32 | + NULL::name AS schema_name, |
| 33 | + NULL::text AS object_name, |
| 34 | + NULL::name AS column_name, |
| 35 | + p.perm::perm_type AS permission, |
| 36 | + has_database_privilege(r.oid, d.oid, p.perm) AS granted |
| 37 | +FROM pg_catalog.pg_database AS d |
| 38 | + CROSS JOIN pg_catalog.pg_roles AS r |
| 39 | + CROSS JOIN (VALUES ('CREATE'), ('CONNECT'), ('TEMPORARY')) AS p(perm) |
| 40 | +WHERE d.datname = current_database() |
| 41 | + AND NOT r.rolsuper; |
| 42 | + |
| 43 | +GRANT SELECT ON database_permissions TO PUBLIC; |
| 44 | + |
| 45 | +CREATE VIEW all_permissions AS |
| 46 | +SELECT * FROM table_permissions |
| 47 | +UNION ALL |
| 48 | +SELECT * FROM view_permissions |
| 49 | +UNION ALL |
| 50 | +SELECT * FROM column_permissions |
| 51 | +UNION ALL |
| 52 | +SELECT * FROM sequence_permissions |
| 53 | +UNION ALL |
| 54 | +SELECT * FROM function_permissions |
| 55 | +UNION ALL |
| 56 | +SELECT * FROM schema_permissions |
| 57 | +UNION ALL |
| 58 | +SELECT * FROM database_permissions; |
| 59 | + |
| 60 | +GRANT SELECT ON all_permissions TO PUBLIC; |
| 61 | + |
| 62 | +/* update trigers for the views */ |
| 63 | + |
| 64 | +CREATE FUNCTION permissions_trigger_func() |
| 65 | +RETURNS TRIGGER |
| 66 | +LANGUAGE plpgsql |
| 67 | +AS $$ |
| 68 | +DECLARE |
| 69 | + db_name text; |
| 70 | + cmd text; |
| 71 | +BEGIN |
| 72 | + IF NEW.object_type <> OLD.object_type OR |
| 73 | + NEW.role_name <> OLD.role_name OR |
| 74 | + NEW.schema_name <> OLD.schema_name OR |
| 75 | + NEW.object_name <> OLD.object_name OR |
| 76 | + NEW.column_name <> OLD.column_name OR |
| 77 | + NEW.permission <> OLD.permission |
| 78 | + THEN |
| 79 | + RAISE 'Only the "granted" column may be updated'; |
| 80 | + END IF; |
| 81 | + |
| 82 | + -- Is there anything to do at all? |
| 83 | + IF NEW.granted = OLD.granted |
| 84 | + THEN |
| 85 | + RETURN NEW; |
| 86 | + END IF; |
| 87 | + |
| 88 | + IF OLD.object_type IN ('TABLE', 'VIEW') |
| 89 | + THEN |
| 90 | + IF NOT OLD.granted |
| 91 | + THEN |
| 92 | + cmd := format('GRANT %s ON %s.%s TO %s', |
| 93 | + OLD.permission, OLD.schema_name, |
| 94 | + OLD.object_name, OLD.role_name); |
| 95 | + ELSE |
| 96 | + cmd := format('REVOKE %s ON %s.%s FROM %s', |
| 97 | + OLD.permission, OLD.schema_name, |
| 98 | + OLD.object_name, OLD.role_name); |
| 99 | + END IF; |
| 100 | + ELSIF OLD.object_type = 'COLUMN' |
| 101 | + THEN |
| 102 | + IF NOT OLD.granted |
| 103 | + THEN |
| 104 | + cmd := format('GRANT %s(%s) ON %s.%s TO %s', |
| 105 | + OLD.permission, OLD.column_name, |
| 106 | + OLD.schema_name, OLD.object_name, |
| 107 | + OLD.role_name); |
| 108 | + ELSE |
| 109 | + cmd := format('REVOKE %s(%s) ON %s.%s FROM %s', |
| 110 | + OLD.permission, OLD.column_name, |
| 111 | + OLD.schema_name, OLD.object_name, |
| 112 | + OLD.role_name); |
| 113 | + END IF; |
| 114 | + ELSIF OLD.object_type = 'SEQUENCE' |
| 115 | + THEN |
| 116 | + IF NOT OLD.granted |
| 117 | + THEN |
| 118 | + cmd := format('GRANT %s ON SEQUENCE %s.%s TO %s', |
| 119 | + OLD.permission, OLD.schema_name, |
| 120 | + OLD.object_name, OLD.role_name); |
| 121 | + ELSE |
| 122 | + cmd := format('REVOKE %s ON SEQUENCE %s.%s FROM %s', |
| 123 | + OLD.permission, OLD.schema_name, |
| 124 | + OLD.object_name, OLD.role_name); |
| 125 | + END IF; |
| 126 | + ELSIF OLD.object_type = 'FUNCTION' |
| 127 | + THEN |
| 128 | + IF NOT OLD.granted |
| 129 | + THEN |
| 130 | + cmd := format('GRANT %s ON FUNCTION %s.%s TO %s', |
| 131 | + OLD.permission, OLD.schema_name, |
| 132 | + OLD.object_name, OLD.role_name); |
| 133 | + ELSE |
| 134 | + cmd := format('REVOKE %s ON FUNCTION %s.%s FROM %s', |
| 135 | + OLD.permission, OLD.schema_name, |
| 136 | + OLD.object_name, OLD.role_name); |
| 137 | + END IF; |
| 138 | + ELSIF OLD.object_type = 'SCHEMA' |
| 139 | + THEN |
| 140 | + IF NOT OLD.granted |
| 141 | + THEN |
| 142 | + cmd := format('GRANT %s ON SCHEMA %s TO %s', |
| 143 | + OLD.permission, OLD.schema_name, |
| 144 | + OLD.role_name); |
| 145 | + ELSE |
| 146 | + cmd := format('REVOKE %s ON SCHEMA %s FROM %s', |
| 147 | + OLD.permission, OLD.schema_name, |
| 148 | + OLD.role_name); |
| 149 | + END IF; |
| 150 | + ELSIF OLD.object_type = 'DATABASE' |
| 151 | + THEN |
| 152 | + db_name := pg_catalog.current_database(); |
| 153 | + |
| 154 | + IF NOT OLD.granted |
| 155 | + THEN |
| 156 | + cmd := format('GRANT %s ON DATABASE %s TO %s', |
| 157 | + OLD.permission, db_name, OLD.role_name); |
| 158 | + ELSE |
| 159 | + cmd := format('REVOKE %s ON DATABASE %s FROM %s', |
| 160 | + OLD.permission, db_name, OLD.role_name); |
| 161 | + END IF; |
| 162 | + ELSE |
| 163 | + RAISE 'Unrecognized object type: %', |
| 164 | + OLD.object_type; |
| 165 | + END IF; |
| 166 | + |
| 167 | + EXECUTE cmd; |
| 168 | + RETURN NEW; |
| 169 | +END; |
| 170 | +$$; |
| 171 | + |
| 172 | +CREATE TRIGGER permissions_trigger |
| 173 | + INSTEAD OF UPDATE ON table_permissions |
| 174 | + FOR EACH ROW EXECUTE PROCEDURE permissions_trigger_func(); |
| 175 | + |
| 176 | +CREATE TRIGGER permissions_trigger |
| 177 | + INSTEAD OF UPDATE ON column_permissions |
| 178 | + FOR EACH ROW EXECUTE PROCEDURE permissions_trigger_func(); |
| 179 | + |
| 180 | +CREATE TRIGGER permissions_trigger |
| 181 | + INSTEAD OF UPDATE ON view_permissions |
| 182 | + FOR EACH ROW EXECUTE PROCEDURE permissions_trigger_func(); |
| 183 | + |
| 184 | +CREATE TRIGGER permissions_trigger |
| 185 | + INSTEAD OF UPDATE ON sequence_permissions |
| 186 | + FOR EACH ROW EXECUTE PROCEDURE permissions_trigger_func(); |
| 187 | + |
| 188 | +CREATE TRIGGER permissions_trigger |
| 189 | + INSTEAD OF UPDATE ON function_permissions |
| 190 | + FOR EACH ROW EXECUTE PROCEDURE permissions_trigger_func(); |
| 191 | + |
| 192 | +CREATE TRIGGER permissions_trigger |
| 193 | + INSTEAD OF UPDATE ON schema_permissions |
| 194 | + FOR EACH ROW EXECUTE PROCEDURE permissions_trigger_func(); |
| 195 | + |
| 196 | +CREATE TRIGGER permissions_trigger |
| 197 | + INSTEAD OF UPDATE ON database_permissions |
| 198 | + FOR EACH ROW EXECUTE PROCEDURE permissions_trigger_func(); |
| 199 | + |
| 200 | +CREATE TRIGGER permissions_trigger |
| 201 | + INSTEAD OF UPDATE ON all_permissions |
| 202 | + FOR EACH ROW EXECUTE PROCEDURE permissions_trigger_func(); |
0 commit comments