【Oracle/GaussDB/MogDB】统一权限查询

【Oracle/GaussDB/MogDB】权限查询

背景

客户在系统下线流程中,有一步操作是锁定该系统对各个数据库登录的账号,但由于历史原因,没有完整记录每个数据库账号有哪些权限,导致有时关闭账号后,部分生产业务出现异常。现客户提出需求,期望从各个数据库中捞出所有用户的所有权限,以便后续关闭账号前确认账号和各个系统之间的相关性。

数据库类型要求包括ORACLE/MogDB/GaussDB,查询出来的字段保持一致以便于各系统汇总。

各数据库版本:

  • ORACLE 19C
  • GaussDB 506.0
  • MogDB 5.0.1

分析

ORACLE权限

  1. dba_tab_privs
    对象权限
  2. dba_role_privs
    角色权限
  3. dba_sys_privs
    系统权限

MogDB和GaussDB权限

MogDB/GaussDB的权限视图和ORACLE差异较大,且随着版本迭代,部分对象权限并未加入到权限视图中,存在遗漏,因此考虑从各个基表中直接获取相关权限

  1. pg_roles
    该视图记录各个用户拥有的系统角色,比如是否为sysadmin/monadmin。不使用基表pg_auhid的原因是防止某些场景下出现查询无权限的报错
  2. gs_db_privilege
    该表记录any权限,如create any table
  3. pg_auth_members
    该表记录role的继承关系,如grant user1 to user2;
  4. 所有包含*acl字段的对象表
sql 复制代码
select attrelid::regclass from pg_catalog.pg_attribute where attname like '%acl';
表名 描述 GaussDB和MogDB是否都有
pg_default_acl 新建对象的默认权限 Y
pg_pltemplate 过程语言的"模板"信息 Y
pg_tablespace 表空间 Y
pg_type 数据类型 Y
pg_attribute 字段 Y
pg_proc 存储过程 Y
pg_class 表、索引、序列 Y
pg_database 数据库 Y
pg_foreign_server 外部服务器 Y
pg_foreign_data_wrapper 外部数据封装器 Y
pg_language 编程语言 Y
pg_namespace 命名空间(schema) Y
pg_largeobject_metadata 大对象元数据 Y
pg_extension_data_source 外部数据源对象的信息(已废弃) Y
pg_directory 目录 Y
gs_package Y
pgxc_group 节点组信息(仅分布式使用) Y
gs_client_global_keys 密态等值特性中客户端加密主密钥相关信息 Y
gs_column_keys 密态等值特性中列加密密钥相关信息 Y
gs_database_link dblink N
gs_pdb pdb N
gs_database_details 显示pg_database中各个数据库的开启关闭状态(pdb) N
dbe_perf.pdb_info 查询gs_pdb的视图 N
snapshot.snap_pdb_info dbe_perf.pdb_info的快照 N

从上表可以看到该版本GaussDB和MogDB中,差异为DBLINK和PDB,MogDB没有这两个功能,考虑到当前客户的开发规范中禁止使用这两个功能,因此可以不用区分GaussDB和MogDB的权限查询SQL。

系统用户过滤

查询时需要剔除授权给系统用户/角色的权限,因此grantee的oid需要大于select datlastsysoid from pg_database where datname=current_database()datlastsysoid表示预留的最大系统oid,超过这个值的都表示在数据库初始化之后创建的oid。

对于ORACLE,也需要剔除系统用户。

一种较为通用的过滤方式为(11g~23C):

sql 复制代码
select username from dba_users where created >(
 select max(created)/*+1/24*/ from dba_users
  where default_tablespace in ('SYSAUX','SYSTEM','APEX') and account_status like '%LOCKED')
  and nvl(password,'x') not in ('EXTERNAL')
  AND  default_tablespace NOT in ('SYSAUX','SYSTEM','APEX');

注意上面这种方式不适用于按 装库、创建业务用户、安装ORACLE组件用户这样的顺序执行的库。

但在当前客户的标准环境中,所有自建用户都会指定自定义表空间,表空间的命名为"TBS"开头,因此在该客户环境中以默认表空间名作为过滤条件比较简单。

sql 复制代码
select username from dba_users where default_tablespace like 'TBS%';

整合SQL

对象权限

sql 复制代码
--对象权限 for GaussDB/MogDB
select grantee ,owner,name as OBJECT_NAME,grantor,privilege ,grantable, type  as OBJECT_TYPE from (
 SELECT /*+no nestloop*/ t.oid, t.oid_class, t.type
                 , r.rolname as owner, n.nspname as schema
                 , t.name, t.column_name
                 , case grantor when 0 then 'public' else  ro.rolname end as grantor
                 , case grantee when 0 then 'public' else re.rolname end as grantee
                 , t.privilege, t.is_grantable as grantable
              FROM (select oid, 'pg_default_acl' as oid_class
                         , CONCAT('DEFAULT PRIVILEGES (',(case defaclobjtype when 'r' then 'RELATION' when 'S' then 'SEQUENCE' when 'f' then 'FUNCTION' when 'T' then 'TYPE' when 'K' then 'CLIENT_GLOBAL_KEYS' when 'k' then 'COLUMN_KEYS' else defaclobjtype::text end),')') as type
                         , defaclrole as owner
                         , defaclnamespace as schema
                         , null as NAME
                         , NULL as column_name
                         , (aclexplode(defaclacl)).grantor AS grantor
                         , (aclexplode(defaclacl)).grantee AS grantee
                         , (aclexplode(defaclacl)).privilege_type AS privilege
                         , (aclexplode(defaclacl)).is_grantable AS is_grantable
                      from pg_catalog.pg_default_acl
                     where defaclacl is not null
                     union all
                    select oid, 'pg_class' as oid_class
                         , case relkind WHEN 'r' ::"char" THEN 'TABLE' ::text
                                        WHEN 'v' ::"char" THEN 'VIEW' ::text
                                        WHEN 'm' ::"char" THEN 'MATERIALIZED VIEW' ::text
                                        WHEN 'i' ::"char" THEN 'INDEX' ::text
                                        WHEN 'I' ::"char" THEN 'GLOBAL INDEX' ::text
                                        WHEN 'S' ::"char" THEN 'SEQUENCE' ::text
                                        WHEN 'f' ::"char" THEN 'FOREIGN TABLE' ::text
                                        WHEN 'c' ::"char" THEN 'COMPOSITE TYPE' ::text
                                        WHEN 't' ::"char" THEN 'TOAST' ::text
                                        when 'L' ::"char" then 'LARGE SEQUENCE' ::text
                                        ELSE  relkind ::text END as type
                         , relowner as owner
                         , relnamespace as schema
                         , relname::text as NAME
                         , NULL as column_name
                         , (aclexplode(relacl)).grantor AS grantor
                         , (aclexplode(relacl)).grantee AS grantee
                         , (aclexplode(relacl)).privilege_type AS privilege
                         , (aclexplode(relacl)).is_grantable AS is_grantable
                      from pg_catalog.pg_class
                     where relacl is not null
                     union all
                    select c.oid, 'pg_class' as oid_class
                         , 'COLUMN' as type
                         , c.relowner as owner
                         , c.relnamespace as schema
                         , c.relname::text as name
                         , a.attname::text as column_name
                         , (aclexplode(a.attacl)).grantor AS grantor
                         , (aclexplode(a.attacl)).grantee AS grantee
                         , (aclexplode(a.attacl)).privilege_type AS privilege
                         , (aclexplode(a.attacl)).is_grantable AS is_grantable
                      from pg_catalog.pg_attribute as a
                      join pg_catalog.pg_class as c on a.attrelid = c.oid
                     where a.attacl is not null
                     union all
                    select oid, 'pg_database' as oid_class
                         , 'DATABASE' as type
                         , datdba as owner
                         , null as schema
                         , datname::text as name
                         , null as column_name
                         , (aclexplode(datacl)).grantor AS grantor
                         , (aclexplode(datacl)).grantee AS grantee
                         , (aclexplode(datacl)).privilege_type AS privilege
                         , (aclexplode(datacl)).is_grantable AS is_grantable
                      from pg_catalog.pg_database
                     where datacl is not null
                     union all
                    select oid, 'pg_foreign_data_wrapper' as oid_class
                         , 'FOREIGN DATA WRAPPER' as type
                         , fdwowner as owner
                         , null as schema
                         , fdwname::text as name
                         , null as column_name
                         , (aclexplode(fdwacl)).grantor AS grantor
                         , (aclexplode(fdwacl)).grantee AS grantee
                         , (aclexplode(fdwacl)).privilege_type AS privilege
                         , (aclexplode(fdwacl)).is_grantable AS is_grantable
                      from pg_catalog.pg_foreign_data_wrapper
                     where fdwacl is not null
                     union all
                    select oid, 'pg_foreign_server' as oid_class
                         , 'FOREIGN SERVER' as type
                         , srvowner as owner
                         , null as schema
                         , srvname::text as name
                         , null as column_name
                         , (aclexplode(srvacl)).grantor AS grantor
                         , (aclexplode(srvacl)).grantee AS grantee
                         , (aclexplode(srvacl)).privilege_type AS privilege
                         , (aclexplode(srvacl)).is_grantable AS is_grantable
                      from pg_catalog.pg_foreign_server
                     where srvacl is not null
                     union all
                    select oid, 'pg_type' as oid_class
                         , 'TYPE' as type
                         , typowner as owner
                         , typnamespace as schema
                         , typname::text as name
                         , null as column_name
                         , (aclexplode(typacl)).grantor AS grantor
                         , (aclexplode(typacl)).grantee AS grantee
                         , (aclexplode(typacl)).privilege_type AS privilege
                         , (aclexplode(typacl)).is_grantable AS is_grantable
                      from pg_catalog.pg_type
                     where typacl is not null
                     union all 
                     select oid,'gs_package' as oid_class
                     ,'PACKAGE' as type 
                     ,pkgowner as owner
                     ,pkgnamespace as schema
                     ,pkgname as name
                     ,null as column_name
                       , (aclexplode(pkgacl)).grantor AS grantor
                         , (aclexplode(pkgacl)).grantee AS grantee
                         , (aclexplode(pkgacl)).privilege_type AS privilege
                         , (aclexplode(pkgacl)).is_grantable AS is_grantable
                 from pg_catalog.gs_package 
                     union all
                    select oid, 'pg_proc' as oid_class
                         , case prokind  when 'p' then  'PROCEDURE'  else  'FUNCTION'  end as type
                         , proowner as owner
                         , pronamespace as schema
                         , proname::text as name
                         , null as column_name
                         , (aclexplode(proacl)).grantor AS grantor
                         , (aclexplode(proacl)).grantee AS grantee
                         , (aclexplode(proacl)).privilege_type AS privilege
                         , (aclexplode(proacl)).is_grantable AS is_grantable
                      from pg_catalog.pg_proc
                     where proacl is not null and propackageid=0
                     union all
                    select oid, 'pg_language' as oid_class
                         , 'LANGUAGE' as type
                         , lanowner as owner
                         , null as schema
                         , lanname::text as name
                         , null as column_name
                         , (aclexplode(lanacl)).grantor AS grantor
                         , (aclexplode(lanacl)).grantee AS grantee
                         , (aclexplode(lanacl)).privilege_type AS privilege
                         , (aclexplode(lanacl)).is_grantable AS is_grantable
                      from pg_catalog.pg_language
                     where lanacl is not null
                     union all
                    select oid, 'pg_largeobject_metadata' as oid_class
                         , 'LARGE OBJECT' as type
                         , lomowner as owner
                         , null as schema
                         , oid::text as name
                         , null as column_name
                         , (aclexplode(lomacl)).grantor AS grantor
                         , (aclexplode(lomacl)).grantee AS grantee
                         , (aclexplode(lomacl)).privilege_type AS privilege
                         , (aclexplode(lomacl)).is_grantable AS is_grantable
                      from pg_catalog.pg_largeobject_metadata
                     where lomacl is not null
                     union all
                    select oid, 'pg_namespace' as oid_class
                         , 'SCHEMA' as type
                         , nspowner as owner
                         , null as schema
                         , nspname::text as name
                         , null as column_name
                         , (aclexplode(nspacl)).grantor AS grantor
                         , (aclexplode(nspacl)).grantee AS grantee
                         , (aclexplode(nspacl)).privilege_type AS privilege
                         , (aclexplode(nspacl)).is_grantable AS is_grantable
                      from pg_catalog.pg_namespace
                     where nspacl is not null
                     union all
                    select oid, 'pg_tablespace' as oid_class
                         , 'TABLESPACE' as type
                         , spcowner as owner
                         , null as schema
                         , spcname::text as name
                         , null as column_name
                         , (aclexplode(spcacl)).grantor AS grantor
                         , (aclexplode(spcacl)).grantee AS grantee
                         , (aclexplode(spcacl)).privilege_type AS privilege
                         , (aclexplode(spcacl)).is_grantable AS is_grantable
                      from pg_catalog.pg_tablespace
                     where spcacl is not null
                     union all 
                     select null, 'pg_directory' as oid_class
                         , 'DIRECTORY' as type
                         , owner as owner
                         , owner as schema
                         , dirname::text as name
                         , null as column_name
                         , (aclexplode(diracl)).grantor AS grantor
                         , (aclexplode(diracl)).grantee AS grantee
                         , (aclexplode(diracl)).privilege_type AS privilege
                         , (aclexplode(diracl)).is_grantable AS is_grantable
                      from pg_catalog.pg_directory
                     where diracl is not null
                      union all 
                     select null, 'gs_client_global_keys' as oid_class
                         , 'CLIENT_GLOBAL_KEYS' as type
                         , key_owner as owner
                         , key_namespace as schema
                         , global_key_name::text as name
                         , null as column_name
                         , (aclexplode(key_acl)).grantor AS grantor
                         , (aclexplode(key_acl)).grantee AS grantee
                         , (aclexplode(key_acl)).privilege_type AS privilege
                         , (aclexplode(key_acl)).is_grantable AS is_grantable
                      from pg_catalog.gs_client_global_keys
                     where key_acl is not null
                      union all 
                     select null, 'gs_column_keys' as oid_class
                         , 'COLUMN_KEYS' as type
                         , key_owner as owner
                         , key_namespace as schema
                         , column_key_name::text as name
                         , null as column_name
                         , (aclexplode(key_acl)).grantor AS grantor
                         , (aclexplode(key_acl)).grantee AS grantee
                         , (aclexplode(key_acl)).privilege_type AS privilege
                         , (aclexplode(key_acl)).is_grantable AS is_grantable
                      from pg_catalog.gs_column_keys
                     where key_acl is not null
                 ) t
              join pg_catalog.pg_roles r on t.owner = r.oid
              join pg_catalog.pg_namespace n on t.schema = n.oid
              left join pg_catalog.pg_roles ro on t.grantor = ro.oid
              left join pg_catalog.pg_roles re on t.grantee = re.oid
             where t.grantor <> t.grantee
           and (t.grantee>(select datlastsysoid  from pg_database where datname=current_database()) 
           --or t.grantee=0 --GRANT XXX TO PUBLIC
           )
       );

--对象权限 for oracle
select P.GRANTEE, P.OWNER, P.TABLE_NAME AS OBJECT_NAME, P.GRANTOR, P.PRIVILEGE, P.GRANTABLE,O.OBJECT_TYPE from dba_tab_privs P,DBA_OBJECTS O
 where P.owner=O.OWNER AND P.table_name=O.OBJECT_NAME AND OBJECT_TYPE NOT IN ('PACKAGE BODY','TYPE BODY')
 AND grantee in (select username from dba_users where default_tablespace like 'TBS%') and grantee<>grantor;

GaussDB的SQL套了子查询,是考虑到之后这个SQL可以在其他场景下复用。

--or t.grantee=0该条件表示将对象授权给public,和客户沟通,该场景下无法确认待关闭的用户是否会访问该对象,暂不考虑,因此注释了这个条件。

ORACLE中这个关联方式其实是不严谨的,但至少数据不会遗漏。

SYS+ROLE权限

考虑到sys权限和role权限在ORACLE和GaussDB/MogDB中的分类存在互相交叉的情况,因此直接合并sys权限和role权限的查询结果:

sql 复制代码
--sys+role权限 for GaussDB/MogDB
select rolname grantee,
       case when key like 'rol%' then substr(key,4) else key end as  PRIVILEGE
 from (
select rolname,
   (json_each_text(row_to_json(pg_roles))).key ,
   (json_each_text(row_to_json(pg_roles))).value
 from pg_roles where oid>(select datlastsysoid  from pg_database where datname=current_database()))
where  value ='true'
union all 
select rolname ,privilege_type from gs_db_privilege p,pg_roles r 
where r.oid=p.roleid and r.oid>(select datlastsysoid  from pg_database where datname=current_database())
union all 
select r2.rolname,r1.rolname from pg_auth_members m,pg_roles r1,pg_roles r2 where m.roleid=r1.oid and m.member=r2.oid
;

--SYS+ROLE权限 for oracle
select grantee,privilege from dba_sys_privs where grantee in (select username from dba_users where default_tablespace like 'TBS%')
union all
select GRANTEE, GRANTED_ROLE from dba_role_privs where grantee in (select username from dba_users where default_tablespace like 'TBS%');
相关推荐
小陈工1 小时前
Python Web开发入门(十七):Vue.js与Python后端集成——让前后端真正“握手言和“
开发语言·前端·javascript·数据库·vue.js·人工智能·python
科技小花5 小时前
数据治理平台架构演进观察:AI原生设计如何重构企业数据管理范式
数据库·重构·架构·数据治理·ai-native·ai原生
一江寒逸5 小时前
零基础从入门到精通MySQL(中篇):进阶篇——吃透多表查询、事务核心与高级特性,搞定复杂业务SQL
数据库·sql·mysql
D4c-lovetrain5 小时前
linux个人心得22 (mysql)
数据库·mysql
阿里小阿希6 小时前
CentOS7 PostgreSQL 9.2 升级到 15 完整教程
数据库·postgresql
荒川之神6 小时前
Oracle 数据仓库雪花模型设计(完整实战方案)
数据库·数据仓库·oracle
做个文艺程序员6 小时前
MySQL安全加固十大硬核操作
数据库·mysql·安全
不吃香菜学java6 小时前
Redis简单应用
数据库·spring boot·tomcat·maven
一个天蝎座 白勺 程序猿6 小时前
Apache IoTDB(15):IoTDB查询写回(INTO子句)深度解析——从语法到实战的ETL全链路指南
数据库·apache·etl·iotdb
不知名的老吴7 小时前
Redis的延迟瓶颈:TCP栈开销无法避免
数据库·redis·缓存