PostgreSQL的权限(list\grant\revoke)一直以来都是它的糟点之一,权限的可视化不直观,不方便查询,特别对新手来说更困难,需要了解很多概念、系统表和系统函数,即便如此实操起来还是很麻烦;如果你想比较一下当前分配的权限是否是你所想要的权限,也没有很便捷的办法。pg_permissions就是为了解决这些问题而设计的,其实它的实现逻辑比较简单,针对不同的对象封装了不同的视图,便于直观地查询对象上面的权限,同时在视图上创建了触发器,当修改视图列granted时就触发对应的权限分配或回收语句;pg_permissions另外一个比较实用的功能就是权限对比,设计系统时设计人员在设计阶段就可以设计好各个对象的权限,只要把设计好的权限导入计划权限表permission_target,后期开发人员或DBA在开发阶段可能会随意的分配权限,设计人员只需要运行对比函数就能发现权限是否符合设计要求。
下面示例详细介绍pg_permissions的使用:
--安装扩展pg_permission
git clone https://github.com/cybertec-postgresql/pg_permission
cd pg_permission
USE_PGXS=1 make
USE_PGXS=1 make install
--创建测试库
postgres=# CREATE DATABASE contrib_regression;
CREATE DATABASE
--切换到测试库
postgres=# \c contrib_regression postgres;
You are now connected to database "contrib_regression" as user "postgres".
--创建扩展pg_permissions
contrib_regression=# CREATE EXTENSION pg_permissions;
CREATE EXTENSION
--创建3个测试用户
contrib_regression=# CREATE ROLE users;
CREATE ROLE
contrib_regression=# CREATE ROLE user1 LOGIN IN ROLE users;
CREATE ROLE
contrib_regression=# CREATE ROLE user2 LOGIN IN ROLE users;
CREATE ROLE
/* database */
--设计授予3个用户 数据库的'CONNECT','TEMPORARY'权限
contrib_regression=# INSERT INTO permission_target
contrib_regression-# (role_name, permissions, object_type, schema_name, object_name, column_name)
contrib_regression-# VALUES ('users', ARRAY['CONNECT','TEMPORARY']::perm_type[], 'DATABASE', NULL, NULL, NULL),
contrib_regression-# ('user1', ARRAY['CONNECT','TEMPORARY']::perm_type[], 'DATABASE', NULL, NULL, NULL),
contrib_regression-# ('user2', ARRAY['CONNECT','TEMPORARY']::perm_type[], 'DATABASE', NULL, NULL, NULL);
INSERT 0 3
--以下语句会报错,因为违法了表permission_target的检查约束,下文会给出permission_target表的结构
--这里报错的原因是:对DATABASE级别的授权不应该再指定具体的模式
contrib_regression=# INSERT INTO permission_target
contrib_regression-# (role_name, permissions, object_type, schema_name, object_name, column_name)
contrib_regression-# VALUES ('user2', ARRAY['CREATE']::perm_type[], 'DATABASE', 'public', NULL, NULL);
ERROR: new row for relation "permission_target" violates check constraint "permission_target_valid"
DETAIL: Failing row contains (4, user2, {CREATE}, DATABASE, public, null, null).
--以下是当前赋予的权限,可能与设计的权限有出入,最后可以通过一个函数对比出具体的差异项,再通过更新对应的视图修正误差的权限
--对数据库contrib_regression回收PUBLIC所有权限
contrib_regression=# REVOKE ALL ON DATABASE contrib_regression FROM PUBLIC;
REVOKE
--给users用户分配数据库contrib_regression的CONNECT, TEMPORARY权限,其中的角色组成员(user1和user2)也具有此权限,这个与设计的权限一致
contrib_regression=# GRANT CONNECT, TEMPORARY ON DATABASE contrib_regression TO users;
GRANT
--给user2用户额外分配了数据库contrib_regression的CREATE权限,这个与设计的权限不一致,用户user2多了一个CREATE权限
contrib_regression=# GRANT CREATE ON DATABASE contrib_regression TO user2;
GRANT
/* schema */
--设计授予用户模式相关的权限
contrib_regression=# INSERT INTO permission_target
contrib_regression-# (role_name, permissions, object_type, schema_name, object_name, column_name)
contrib_regression-# VALUES ('users', ARRAY['USAGE']::perm_type[], 'SCHEMA', 'appschema', NULL, NULL),
contrib_regression-# ('user1', ARRAY['USAGE','CREATE']::perm_type[], 'SCHEMA', 'appschema', NULL, NULL),
contrib_regression-# ('user2', ARRAY['USAGE']::perm_type[], 'SCHEMA', 'appschema', NULL, NULL);
INSERT 0 3
--报错原因同上
contrib_regression=# INSERT INTO permission_target
contrib_regression-# (role_name, permissions, object_type, schema_name, object_name, column_name)
contrib_regression-# VALUES ('user2', ARRAY['CREATE']::perm_type[], 'SCHEMA', 'appschema', 'sometable', NULL);
ERROR: new row for relation "permission_target" violates check constraint "permission_target_valid"
DETAIL: Failing row contains (8, user2, {CREATE}, SCHEMA, appschema, sometable, null).
--创建测试模式
contrib_regression=# CREATE SCHEMA appschema;
CREATE SCHEMA
--这里的当前授权与设计授权比缺失了CREATE权限(用户:user1)
contrib_regression=# GRANT USAGE ON SCHEMA appschema TO PUBLIC;
GRANT
--这里的当前授权与设计授权比多了CREATE权限(用户:user2)
contrib_regression=# GRANT CREATE ON SCHEMA appschema TO user2;
GRANT
/* table */
--设计授予用户 表相关的权限
contrib_regression=# INSERT INTO permission_target
contrib_regression-# (role_name, permissions, object_type, schema_name, object_name, column_name)
contrib_regression-# VALUES ('user1', ARRAY['SELECT','INSERT','UPDATE','DELETE']::perm_type[], 'TABLE', 'appschema', NULL, NULL),
contrib_regression-# ('user2', ARRAY['SELECT']::perm_type[], 'TABLE', 'appschema', NULL, NULL);
INSERT 0 2
--报错原因同上
contrib_regression=# INSERT INTO permission_target
contrib_regression-# (role_name, permissions, object_type, schema_name, object_name, column_name)
contrib_regression-# VALUES ('user2', ARRAY['INSERT']::perm_type[], 'TABLE', 'appschema', 'apptable', 'acolumn');
ERROR: new row for relation "permission_target" violates check constraint "permission_target_valid"
DETAIL: Failing row contains (11, user2, {INSERT}, TABLE, appschema, apptable, acolumn).
--创建测试表
contrib_regression=# CREATE TABLE appschema.apptable (
contrib_regression(# id integer PRIMARY KEY,
contrib_regression(# val text NOT NULL,
contrib_regression(# created timestamp with time zone NOT NULL DEFAULT current_timestamp
contrib_regression(# );
CREATE TABLE
contrib_regression=# CREATE TABLE appschema.apptable2 (
contrib_regression(# id integer PRIMARY KEY,
contrib_regression(# val text NOT NULL,
contrib_regression(# created timestamp with time zone NOT NULL DEFAULT current_timestamp
contrib_regression(# ); -- missing all permissions on this one
CREATE TABLE
--这里的当前授权与设计授权比缺失了DELETE权限(用户:user1)
contrib_regression=# GRANT SELECT, INSERT, UPDATE ON appschema.apptable TO user1;
GRANT
--这里的当前授权与设计授权比多了INSERT权限(用户:user2)
contrib_regression=# GRANT SELECT, INSERT ON appschema.apptable TO user2;
GRANT
/* column */
--设计授予用户 表列相关的权限
contrib_regression=# INSERT INTO permission_target
contrib_regression-# (role_name, permissions, object_type, schema_name, object_name, column_name)
contrib_regression-# VALUES ('user1', ARRAY['SELECT','INSERT','UPDATE','REFERENCES']::perm_type[], 'COLUMN', 'appschema', 'apptable2', 'val');
INSERT 0 1
--报错原因同上
contrib_regression=# INSERT INTO permission_target
contrib_regression-# (role_name, permissions, object_type, schema_name, object_name, column_name)
contrib_regression-# VALUES ('user2', ARRAY['DELETE']::perm_type[], 'COLUMN', 'appschema', 'apptable2', 'val');
ERROR: new row for relation "permission_target" violates check constraint "permission_target_valid"
DETAIL: Failing row contains (13, user2, {DELETE}, COLUMN, appschema, apptable2, val).
--由于没有对user1用户赋权,这里丢失了user1用户应该拥有的列上的所有权限
--这里的当前授权与设计授权比多了UPDATE (val)权限(用户:user2)
contrib_regression=# GRANT UPDATE (val) ON appschema.apptable2 TO user2;
GRANT
/* view */
--设计授予用户 视图相关的权限
contrib_regression=# INSERT INTO permission_target
contrib_regression-# (role_name, permissions, object_type, schema_name, object_name, column_name)
contrib_regression-# VALUES ('user1', ARRAY['SELECT','INSERT','UPDATE','DELETE']::perm_type[], 'VIEW', 'appschema', 'appview', NULL),
contrib_regression-# ('user2', ARRAY['SELECT']::perm_type[], 'VIEW', 'appschema', 'appview', NULL);
INSERT 0 2
--创建测试视图
contrib_regression=# CREATE VIEW appschema.appview AS
contrib_regression-# SELECT id, val FROM appschema.apptable;
CREATE VIEW
--这里的当前授权与设计授权比多了SELECT权限(用户:users)
contrib_regression=# GRANT SELECT ON appschema.appview TO users;
GRANT
--这里的当前授权与设计授权比缺失了UPDATE权限(用户:user1)
contrib_regression=# GRANT INSERT, DELETE ON appschema.appview TO user1;
/* sequence */
--设计授予用户 序列相关的权限
contrib_regression=# INSERT INTO permission_target
contrib_regression-# (role_name, permissions, object_type, schema_name, object_name, column_name)
contrib_regression-# VALUES ('users', ARRAY['USAGE']::perm_type[], 'SEQUENCE', 'appschema', 'appseq', NULL),
contrib_regression-# ('user1', ARRAY['USAGE','SELECT']::perm_type[], 'SEQUENCE', 'appschema', 'appseq', NULL),
contrib_regression-# ('user2', ARRAY['USAGE']::perm_type[], 'SEQUENCE', 'appschema', 'appseq', NULL);
INSERT 0 3
--创建测试序列
contrib_regression=# CREATE SEQUENCE appschema.appseq;
CREATE SEQUENCE
--这里的当前授权与设计授权比缺失了SELECT权限(用户:user1)
contrib_regression=# GRANT USAGE ON SEQUENCE appschema.appseq TO users;
GRANT
--这里的当前授权与设计授权比多了UPDATE权限(用户:user2)
contrib_regression=# GRANT UPDATE ON SEQUENCE appschema.appseq TO user2;
GRANT
/* function */
--设计授予用户 函数相关的权限
contrib_regression=# INSERT INTO permission_target
contrib_regression-# (role_name, permissions, object_type, schema_name, object_name, column_name)
contrib_regression-# VALUES ('user1', ARRAY['EXECUTE']::perm_type[], 'FUNCTION', 'appschema', 'appfun(integer)', NULL),
contrib_regression-# ('user2', ARRAY['EXECUTE']::perm_type[], 'FUNCTION', 'appschema', 'appfun(integer)', NULL);
INSERT 0 2
--报错原因同上
contrib_regression=# INSERT INTO permission_target
contrib_regression-# (role_name, permissions, object_type, schema_name, object_name, column_name)
contrib_regression-# VALUES ('users', ARRAY['UPDATE']::perm_type[], 'FUNCTION', 'appschema', 'appfun(integer)', NULL);
ERROR: new row for relation "permission_target" violates check constraint "permission_target_valid"
DETAIL: Failing row contains (21, users, {UPDATE}, FUNCTION, appschema, appfun(integer), null).
--在此测试案例中3个用户拥有函数的执行权限,本来设计是只授予user1和user2的执行权限,由于没有回收users的权限,users用户也拥有执行权限
contrib_regression=# CREATE FUNCTION appschema.appfun(i integer) RETURNS integer
contrib_regression-# LANGUAGE sql IMMUTABLE AS
contrib_regression-# 'SELECT i + 2';
CREATE FUNCTION
--查询所有当前分配的权限
contrib_regression=# SELECT object_type, role_name, schema_name, object_name, column_name, permission
contrib_regression-# FROM all_permissions
contrib_regression-# WHERE granted
contrib_regression-# AND role_name IN ('users', 'user1', 'user2')
contrib_regression-# AND coalesce(schema_name, 'appschema') = 'appschema'
contrib_regression-# ORDER BY object_type, role_name, schema_name, object_name, column_name, permission;
object_type | role_name | schema_name | object_name | column_name | permission
-------------+-----------+-------------+-----------------+-------------+------------
TABLE | user1 | appschema | apptable | | SELECT
TABLE | user1 | appschema | apptable | | INSERT
TABLE | user1 | appschema | apptable | | UPDATE
TABLE | user2 | appschema | apptable | | SELECT
TABLE | user2 | appschema | apptable | | INSERT
VIEW | user1 | appschema | appview | | SELECT
VIEW | user1 | appschema | appview | | INSERT
VIEW | user1 | appschema | appview | | DELETE
VIEW | user2 | appschema | appview | | SELECT
VIEW | users | appschema | appview | | SELECT
COLUMN | user2 | appschema | apptable2 | val | UPDATE
SEQUENCE | user1 | appschema | appseq | | USAGE
SEQUENCE | user2 | appschema | appseq | | UPDATE
SEQUENCE | user2 | appschema | appseq | | USAGE
SEQUENCE | users | appschema | appseq | | USAGE
FUNCTION | user1 | appschema | appfun(integer) | | EXECUTE
FUNCTION | user2 | appschema | appfun(integer) | | EXECUTE
FUNCTION | users | appschema | appfun(integer) | | EXECUTE
SCHEMA | user1 | appschema | | | USAGE
SCHEMA | user2 | appschema | | | USAGE
SCHEMA | user2 | appschema | | | CREATE
SCHEMA | users | appschema | | | USAGE
DATABASE | user1 | | | | CONNECT
DATABASE | user1 | | | | TEMPORARY
DATABASE | user2 | | | | CREATE
DATABASE | user2 | | | | CONNECT
DATABASE | user2 | | | | TEMPORARY
DATABASE | users | | | | CONNECT
DATABASE | users | | | | TEMPORARY
(29 rows)
--对比设计权限和当前分配权限之间的差异
--missing列的t值代表当前权限与设计时权限相比缺失了某个权限,f值代表当前权限与设计时相比多出了某个权限
contrib_regression=# SELECT * FROM permission_diffs()
contrib_regression-# WHERE role_name IN ('users', 'user1', 'user2')
contrib_regression-# ORDER BY object_type, schema_name, object_name, column_name, role_name, permission, missing;
missing | role_name | object_type | schema_name | object_name | column_name | permission
---------+-----------+-------------+-------------+-----------------+-------------+------------
t | user1 | TABLE | appschema | apptable | | DELETE
f | user2 | TABLE | appschema | apptable | | INSERT
t | user1 | TABLE | appschema | apptable2 | | SELECT
t | user1 | TABLE | appschema | apptable2 | | INSERT
t | user1 | TABLE | appschema | apptable2 | | UPDATE
t | user1 | TABLE | appschema | apptable2 | | DELETE
t | user2 | TABLE | appschema | apptable2 | | SELECT
t | user1 | VIEW | appschema | appview | | UPDATE
f | users | VIEW | appschema | appview | | SELECT
t | user1 | COLUMN | appschema | apptable2 | val | SELECT
t | user1 | COLUMN | appschema | apptable2 | val | INSERT
t | user1 | COLUMN | appschema | apptable2 | val | UPDATE
t | user1 | COLUMN | appschema | apptable2 | val | REFERENCES
f | user2 | COLUMN | appschema | apptable2 | val | UPDATE
t | user1 | SEQUENCE | appschema | appseq | | SELECT
f | user2 | SEQUENCE | appschema | appseq | | UPDATE
f | users | FUNCTION | appschema | appfun(integer) | | EXECUTE
t | user1 | SCHEMA | appschema | | | CREATE
f | user2 | SCHEMA | appschema | | | CREATE
f | user2 | DATABASE | | | | CREATE
(20 rows)
--这里举两个修正权限的例子,只需要把对应数据行上面的granted列值作对应修改即可
contrib_regression=# UPDATE column_permissions SET
contrib_regression-# granted = TRUE
contrib_regression-# WHERE role_name = 'user1'
contrib_regression-# AND schema_name = 'appschema'
contrib_regression-# AND object_name = 'apptable2'
contrib_regression-# AND column_name = 'val'
contrib_regression-# AND permission = 'REFERENCES';
UPDATE 1
--all_permissions此视图是多个视图的UNION ALL
contrib_regression=# UPDATE all_permissions SET
contrib_regression-# granted = FALSE
contrib_regression-# WHERE object_type = 'TABLE'
contrib_regression-# AND role_name = 'user2'
contrib_regression-# AND schema_name = 'appschema'
contrib_regression-# AND object_name = 'apptable'
contrib_regression-# AND permission = 'INSERT';
UPDATE 1
--上面修正了两条权限,对比后应该少了两条误差数据
contrib_regression=# SELECT * FROM permission_diffs()
contrib_regression-# WHERE role_name IN ('users', 'user1', 'user2')
contrib_regression-# ORDER BY object_type, schema_name, object_name, column_name, role_name, permission, missing;
missing | role_name | object_type | schema_name | object_name | column_name | permission
---------+-----------+-------------+-------------+-----------------+-------------+------------
t | user1 | TABLE | appschema | apptable | | DELETE
t | user1 | TABLE | appschema | apptable2 | | SELECT
t | user1 | TABLE | appschema | apptable2 | | INSERT
t | user1 | TABLE | appschema | apptable2 | | UPDATE
t | user1 | TABLE | appschema | apptable2 | | DELETE
t | user2 | TABLE | appschema | apptable2 | | SELECT
t | user1 | VIEW | appschema | appview | | UPDATE
f | users | VIEW | appschema | appview | | SELECT
t | user1 | COLUMN | appschema | apptable2 | val | SELECT
t | user1 | COLUMN | appschema | apptable2 | val | INSERT
t | user1 | COLUMN | appschema | apptable2 | val | UPDATE
f | user2 | COLUMN | appschema | apptable2 | val | UPDATE
t | user1 | SEQUENCE | appschema | appseq | | SELECT
f | user2 | SEQUENCE | appschema | appseq | | UPDATE
f | users | FUNCTION | appschema | appfun(integer) | | EXECUTE
t | user1 | SCHEMA | appschema | | | CREATE
f | user2 | SCHEMA | appschema | | | CREATE
f | user2 | DATABASE | | | | CREATE
(18 rows)
--以上多个语句报错都是违反了表约束
contrib_regression=# \d permission_target
Table "public.permission_target"
Column | Type | Collation | Nullable | Default
-------------+-------------+-----------+----------+-----------------------------------------------
id | integer | | not null | nextval('permission_target_id_seq'::regclass)
role_name | name | | not null |
permissions | perm_type[] | | not null |
object_type | obj_type | | not null |
schema_name | name | | |
object_name | text | | |
column_name | name | | |
Indexes:
"permission_target_pkey" PRIMARY KEY, btree (id)
Check constraints:
"permission_target_valid" CHECK (
CASE
WHEN object_type = 'DATABASE'::obj_type THEN schema_name IS NULL AND object_name IS NULL AND column_name IS NULL AND ARRAY['CONNECT'::perm_type, 'CREATE'::perm_type, 'TEMPORARY'::perm_type] @> permissions
WHEN object_type = 'SCHEMA'::obj_type THEN object_name IS NULL AND column_name IS NULL AND ARRAY['CREATE'::perm_type, 'USAGE'::perm_type] @> permissions
WHEN object_type = ANY (ARRAY['TABLE'::obj_type, 'VIEW'::obj_type]) THEN column_name IS NULL AND ARRAY['SELECT'::perm_type, 'INSERT'::perm_type, 'UPDATE'::perm_type, 'DELETE'::perm_type, 'TRUNCATE'::perm_type, 'REFERENCES'::perm_type, 'TRIGGER'::perm_type] @> permissions
WHEN object_type = 'SEQUENCE'::obj_type THEN column_name IS NULL AND ARRAY['SELECT'::perm_type, 'USAGE'::perm_type, 'UPDATE'::perm_type] @> permissions
WHEN object_type = 'FUNCTION'::obj_type THEN column_name IS NULL AND ARRAY['EXECUTE'::perm_type] @> permissions
WHEN object_type = 'COLUMN'::obj_type THEN ARRAY['SELECT'::perm_type, 'INSERT'::perm_type, 'UPDATE'::perm_type, 'REFERENCES'::perm_type] @> permissions
ELSE NULL::boolean
END)
复制
插件内建的几个视图:
- database_permissions: permissions granted on the current database
- schema_permissions: permissions granted on schemas
- table_permissions: permissions granted on tables
- view_permissions: permissions granted on views
- column_permissions: permissions granted on table and view columns
- function_permissions: permissions granted on functions
- sequence_permissions: permissions granted on sequences
- all_permissions: permissions on all objects (UNION of the above)
所有视图都有相同的列;如果列对当前视图没有意义,则该列为NULL;这些视图可用于检查当前授予的数据库对象权限;可以更新这些视图的“granted”列,从而执行相应的“grant”或“revoke”命令。
注意:超级用户不会显示在视图中,因为他们自动拥有所有权限。
参考:
https://github.com/cybertec-postgresql/pg_permissions
最后修改时间:2021-09-11 10:53:04
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。
评论
真正的幸福,不是拥有更多东西,而是拥有的东西都能让你感到快乐。
1年前

评论
相关阅读
玩一玩系列——玩玩pg_mooncake(PostgreSQL的高性能列存新贵)
小满未满、
507次阅读
2025-03-03 17:18:03
王炸!OGG 23ai 终于支持从PostgreSQL备库抽取数据了
曹海峰
385次阅读
2025-03-09 12:54:06
玩一玩系列——玩玩login_hook(一款即将停止维护的PostgreSQL登录插件)
小满未满、
370次阅读
2025-03-08 18:19:28
明明想执行的SQL是DELETE、UPDATE,但为什么看到的是SELECT(FDW的实现原理解析)
小满未满、
344次阅读
2025-03-19 23:11:26
PostgreSQL初/中/高级认证考试(3.15)通过考生公示
开源软件联盟PostgreSQL分会
293次阅读
2025-03-20 09:50:36
IvorySQL 4.4 发布 - 基于 PostgreSQL 17.4,增强平台支持
通讯员
181次阅读
2025-03-20 15:31:04
套壳论
梧桐
176次阅读
2025-03-09 10:58:17
命名不规范,事后泪两行
xiongcc
164次阅读
2025-03-13 14:26:08
版本发布| IvorySQL 4.4 发布
IvorySQL开源数据库社区
110次阅读
2025-03-13 09:52:33
宝藏PEV,助力你成为SQL优化高手
xiongcc
98次阅读
2025-03-09 23:34:23