@@ -100,8 +100,18 @@ The extension provides a number of views:
100100
101101- ` all_permissions ` : permissions on all objects (` UNION ` of the above)
102102
103- All views have the same columns; a column is NULL if it has no meaning
104- for the current view.
103+ All views share the same columns:
104+
105+ - ` object_type ` : the kind of object (` TABLE ` , ` VIEW ` , ` COLUMN ` , ` SEQUENCE ` ,
106+ ` FUNCTION ` , ` SCHEMA ` or ` DATABASE ` )
107+ - ` role_name ` : the role the permission applies to
108+ - ` schema_name ` , ` object_name ` , ` column_name ` : identify the object
109+ - ` permission ` : the privilege in question (` SELECT ` , ` USAGE ` , ` EXECUTE ` , ...)
110+ - ` granted ` : whether the privilege is currently in effect (` t ` ) or not (` f ` )
111+
112+ A column is NULL if it has no meaning for the current view; for example,
113+ ` column_name ` is only set in ` column_permissions ` , and ` schema_name ` is
114+ always NULL in ` database_permissions ` .
105115
106116These views can be used to examine the currently granted permissions on
107117database objects.
@@ -112,6 +122,187 @@ appropriate `GRANT` or `REVOKE` command to be executed.
112122** Note:** Superusers are not shown in the views, as they automatically have all
113123permissions.
114124
125+ #### Examples ####
126+
127+ The examples in this section assume the following objects and grants:
128+
129+ CREATE ROLE appuser LOGIN;
130+ CREATE ROLE reporting LOGIN;
131+ CREATE ROLE auditor LOGIN;
132+
133+ CREATE SCHEMA appschema;
134+ CREATE TABLE appschema.invoice (
135+ id integer PRIMARY KEY,
136+ customer text NOT NULL,
137+ amount numeric NOT NULL
138+ );
139+ CREATE VIEW appschema.invoice_v AS SELECT id, customer FROM appschema.invoice;
140+ CREATE SEQUENCE appschema.invoice_id_seq;
141+ CREATE FUNCTION appschema.total() RETURNS numeric LANGUAGE sql
142+ AS 'SELECT sum(amount) FROM appschema.invoice';
143+
144+ GRANT USAGE ON SCHEMA appschema TO appuser, reporting, auditor;
145+ GRANT SELECT, INSERT, UPDATE, DELETE ON appschema.invoice TO appuser;
146+ GRANT SELECT ON appschema.invoice TO reporting;
147+ GRANT SELECT ON appschema.invoice_v TO reporting;
148+ GRANT SELECT (customer) ON appschema.invoice TO auditor;
149+ GRANT USAGE ON SEQUENCE appschema.invoice_id_seq TO appuser;
150+ REVOKE EXECUTE ON FUNCTION appschema.total() FROM PUBLIC;
151+ GRANT EXECUTE ON FUNCTION appschema.total() TO reporting;
152+
153+ ` schema_permissions ` shows the ` USAGE ` and ` CREATE ` privileges on schemas:
154+
155+ SELECT object_type, role_name, schema_name, permission, granted
156+ FROM schema_permissions
157+ WHERE schema_name = 'appschema' AND role_name IN ('appuser', 'reporting');
158+
159+ object_type | role_name | schema_name | permission | granted
160+ -------------+-----------+-------------+------------+---------
161+ SCHEMA | appuser | appschema | USAGE | t
162+ SCHEMA | appuser | appschema | CREATE | f
163+ SCHEMA | reporting | appschema | USAGE | t
164+ SCHEMA | reporting | appschema | CREATE | f
165+
166+ ` table_permissions ` lists every table privilege for each role; ` granted ` tells
167+ you which ones are actually in effect:
168+
169+ SELECT role_name, object_name, permission, granted
170+ FROM table_permissions
171+ WHERE schema_name = 'appschema' AND role_name IN ('appuser', 'reporting');
172+
173+ role_name | object_name | permission | granted
174+ -----------+-------------+------------+---------
175+ appuser | invoice | SELECT | t
176+ appuser | invoice | INSERT | t
177+ appuser | invoice | UPDATE | t
178+ appuser | invoice | DELETE | t
179+ appuser | invoice | TRUNCATE | f
180+ appuser | invoice | REFERENCES | f
181+ appuser | invoice | TRIGGER | f
182+ reporting | invoice | SELECT | t
183+ reporting | invoice | INSERT | f
184+ reporting | invoice | UPDATE | f
185+ reporting | invoice | DELETE | f
186+ reporting | invoice | TRUNCATE | f
187+ reporting | invoice | REFERENCES | f
188+ reporting | invoice | TRIGGER | f
189+
190+ ` view_permissions ` is the same, but for views:
191+
192+ SELECT role_name, object_name, permission, granted
193+ FROM view_permissions
194+ WHERE schema_name = 'appschema' AND role_name = 'reporting';
195+
196+ role_name | object_name | permission | granted
197+ -----------+-------------+------------+---------
198+ reporting | invoice_v | SELECT | t
199+ reporting | invoice_v | INSERT | f
200+ reporting | invoice_v | UPDATE | f
201+ reporting | invoice_v | DELETE | f
202+ reporting | invoice_v | TRUNCATE | f
203+ reporting | invoice_v | REFERENCES | f
204+ reporting | invoice_v | TRIGGER | f
205+
206+ ` column_permissions ` reports column level privileges. Note that ` granted ` is
207+ only ` t ` when the privilege is granted * on the column itself* ; a privilege held
208+ at the table level is not repeated here (use ` table_permissions ` for that).
209+ Here ` auditor ` may read only the ` customer ` column:
210+
211+ SELECT role_name, object_name, column_name, permission, granted
212+ FROM column_permissions
213+ WHERE schema_name = 'appschema' AND object_name = 'invoice' AND role_name = 'auditor';
214+
215+ role_name | object_name | column_name | permission | granted
216+ -----------+-------------+-------------+------------+---------
217+ auditor | invoice | amount | SELECT | f
218+ auditor | invoice | amount | INSERT | f
219+ auditor | invoice | amount | UPDATE | f
220+ auditor | invoice | amount | REFERENCES | f
221+ auditor | invoice | customer | SELECT | t
222+ auditor | invoice | customer | INSERT | f
223+ auditor | invoice | customer | UPDATE | f
224+ auditor | invoice | customer | REFERENCES | f
225+ auditor | invoice | id | SELECT | f
226+ auditor | invoice | id | INSERT | f
227+ auditor | invoice | id | UPDATE | f
228+ auditor | invoice | id | REFERENCES | f
229+
230+ ` sequence_permissions ` covers the ` SELECT ` , ` USAGE ` and ` UPDATE ` privileges on
231+ sequences:
232+
233+ SELECT role_name, object_name, permission, granted
234+ FROM sequence_permissions
235+ WHERE schema_name = 'appschema' AND role_name IN ('appuser', 'reporting');
236+
237+ role_name | object_name | permission | granted
238+ -----------+----------------+------------+---------
239+ appuser | invoice_id_seq | SELECT | f
240+ appuser | invoice_id_seq | UPDATE | f
241+ appuser | invoice_id_seq | USAGE | t
242+ reporting | invoice_id_seq | SELECT | f
243+ reporting | invoice_id_seq | UPDATE | f
244+ reporting | invoice_id_seq | USAGE | f
245+
246+ ` function_permissions ` reports the ` EXECUTE ` privilege on functions. Keep in
247+ mind that functions are executable by ` PUBLIC ` by default, so `REVOKE ... FROM
248+ PUBLIC` is usually needed before per-role grants are meaningful:
249+
250+ SELECT role_name, object_name, permission, granted
251+ FROM function_permissions
252+ WHERE schema_name = 'appschema' AND role_name IN ('appuser', 'reporting');
253+
254+ role_name | object_name | permission | granted
255+ -----------+-------------+------------+---------
256+ appuser | total() | EXECUTE | f
257+ reporting | total() | EXECUTE | t
258+
259+ ` database_permissions ` shows the ` CREATE ` , ` CONNECT ` and ` TEMPORARY ` privileges
260+ on the current database (` CONNECT ` and ` TEMPORARY ` are granted to ` PUBLIC ` by
261+ default):
262+
263+ SELECT role_name, permission, granted
264+ FROM database_permissions
265+ WHERE role_name IN ('appuser', 'reporting');
266+
267+ role_name | permission | granted
268+ -----------+------------+---------
269+ appuser | CREATE | f
270+ appuser | CONNECT | t
271+ appuser | TEMPORARY | t
272+ reporting | CREATE | f
273+ reporting | CONNECT | t
274+ reporting | TEMPORARY | t
275+
276+ ` all_permissions ` is the union of all of the above. Filtering on
277+ ` granted ` gives a concise overview of everything a set of roles can actually do:
278+
279+ SELECT object_type, role_name, object_name, column_name, permission
280+ FROM all_permissions
281+ WHERE granted AND role_name IN ('appuser', 'reporting', 'auditor')
282+ AND coalesce(schema_name, 'appschema') = 'appschema'
283+ ORDER BY object_type, role_name, object_name, column_name, permission;
284+
285+ object_type | role_name | object_name | column_name | permission
286+ -------------+-----------+----------------+-------------+------------
287+ TABLE | appuser | invoice | | SELECT
288+ TABLE | appuser | invoice | | INSERT
289+ TABLE | appuser | invoice | | UPDATE
290+ TABLE | appuser | invoice | | DELETE
291+ TABLE | reporting | invoice | | SELECT
292+ VIEW | reporting | invoice_v | | SELECT
293+ COLUMN | auditor | invoice | customer | SELECT
294+ SEQUENCE | appuser | invoice_id_seq | | USAGE
295+ FUNCTION | reporting | total() | | EXECUTE
296+ SCHEMA | appuser | | | USAGE
297+ SCHEMA | auditor | | | USAGE
298+ SCHEMA | reporting | | | USAGE
299+ DATABASE | appuser | | | CONNECT
300+ DATABASE | appuser | | | TEMPORARY
301+ DATABASE | auditor | | | CONNECT
302+ DATABASE | auditor | | | TEMPORARY
303+ DATABASE | reporting | | | CONNECT
304+ DATABASE | reporting | | | TEMPORARY
305+
115306### Tables ###
116307
117308The extension provides a table ` permission_target ` with which you can describe
@@ -133,6 +324,50 @@ be there but isn't; if `missing` is `FALSE`, the result row is a permission that
133324is there even though it is not defined in ` permission_target ` (an extra
134325permission).
135326
327+ #### Comparing desired and actual state ####
328+
329+ Suppose ` appuser ` should be able to read and write all tables in ` appschema ` ,
330+ while ` reporting ` should only be allowed to read them. We record that as the
331+ desired state:
332+
333+ INSERT INTO permission_target (role_name, permissions, object_type, schema_name)
334+ VALUES ('appuser', '{SELECT,INSERT,UPDATE,DELETE}', 'TABLE', 'appschema'),
335+ ('reporting', '{SELECT}', 'TABLE', 'appschema');
336+
337+ Over time the actual grants drifted: ` appuser ` never received ` DELETE ` , and
338+ ` reporting ` was accidentally given ` INSERT ` :
339+
340+ GRANT SELECT, INSERT, UPDATE ON appschema.invoice TO appuser;
341+ GRANT SELECT, INSERT ON appschema.invoice TO reporting;
342+
343+ ` permission_diffs() ` reports exactly these two deviations:
344+
345+ SELECT * FROM permission_diffs()
346+ WHERE role_name IN ('appuser', 'reporting');
347+
348+ missing | role_name | object_type | schema_name | object_name | column_name | permission
349+ ---------+-----------+-------------+-------------+-------------+-------------+------------
350+ t | appuser | TABLE | appschema | invoice | | DELETE
351+ f | reporting | TABLE | appschema | invoice | | INSERT
352+
353+ The ` DELETE ` privilege is missing for ` appuser ` (` missing ` is ` t ` ), while
354+ ` reporting ` holds a surplus ` INSERT ` privilege (` missing ` is ` f ` ). Because the
355+ permission views are updatable, both can be corrected without writing ` GRANT `
356+ or ` REVOKE ` by hand:
357+
358+ -- grant the missing privilege
359+ UPDATE table_permissions SET granted = true
360+ WHERE role_name = 'appuser' AND schema_name = 'appschema'
361+ AND object_name = 'invoice' AND permission = 'DELETE';
362+
363+ -- revoke the surplus privilege
364+ UPDATE table_permissions SET granted = false
365+ WHERE role_name = 'reporting' AND schema_name = 'appschema'
366+ AND object_name = 'invoice' AND permission = 'INSERT';
367+
368+ Running ` permission_diffs() ` again now returns no rows: the actual state
369+ matches the desired state.
370+
136371Installation
137372------------
138373
@@ -177,6 +412,44 @@ subdirectory of that directory, e.g.
177412
178413You still have to run ` CREATE EXTENSION ` as described above.
179414
415+ Compliance
416+ ----------
417+
418+ Virtually every security and data-protection framework requires that access to
419+ data be limited to those who need it (the principle of least privilege), that
420+ this access be reviewed regularly, and that the review be auditable.
421+ ` pg_permissions ` directly supports those obligations: you declare the intended
422+ permissions in ` permission_target ` , prove the current state with the
423+ ` *_permissions ` views, and detect any drift with ` permission_diffs() ` .
424+
425+ This makes the extension useful when demonstrating database access controls
426+ for, among others:
427+
428+ - ** SOX** (Sarbanes-Oxley): IT general controls under section 404 — evidence
429+ that access to financial data is restricted and periodically reviewed.
430+
431+ - ** PCI DSS** : requirements 7 and 8, which mandate restricting access to
432+ cardholder data on a business need-to-know basis.
433+
434+ - ** GDPR** : Article 32 (security of processing), which expects access to
435+ personal data to be limited to what is necessary.
436+
437+ - ** HIPAA** : the Security Rule's access-control standard
438+ (§ 164.312(a)) for electronic protected health information.
439+
440+ - ** ISO/IEC 27001 / 27002** : the access-control objectives (Annex A.9 /
441+ control 5.15 and following) covering formal entitlement and periodic review.
442+
443+ - ** SOC 2** : the Security and Confidentiality trust-services criteria around
444+ logical access.
445+
446+ - ** DORA** : the EU Digital Operational Resilience Act's access-management
447+ requirements for financial entities.
448+
449+ The extension does not enforce these controls on its own; it provides the
450+ reporting and reconciliation you need to define a desired state, prove the
451+ current state, and catch any divergence between audits.
452+
180453Support
181454-------
182455
0 commit comments