数据库高安全—角色权限:权限管理权限检查
目录
3.3 权限管理
3.4 权限检查
书接上文数据库高安全—角色权限:角色创建角色管理,从角色创建和角色管理两方面对高斯数据库的角色权限进行了介绍,本篇将从权限管理和权限检查方面继续解读高斯数据库的角色权限。
3.3 权限管理
(1) 访问控制列表
访问控制列表(Access Control List,ACL)是实现数据库对象权限管理的基础,每个对象都具有ACL,存储该对象的所有授权信息。当用户访问对象时,只有用户在对象的ACL中并且具有所需的权限才能够访问该对象。
每个ACL是由1个或多个AclItem构成的链表,每1个AclItem由授权者、被授权者和权限位3部分构成,记录着可在对象上进行操作的用户及其权限。
数据结构AclItem:
typedef struct AclItem {
Oid ai_grantee; // 被授权者的OID
Oid ai_grantor; // 授权者的OID
AclMode ai_privs; // 权限位:32位的比特位
} AclItem;
其中ai_privs字段是AclMode类型。AclMode是一个32位的比特位,其高16位为权限选项位,当该比特位取值为1时,表示AclItem中的ai_grantee对应的用户具有此对象的相应操作的授权权限,否则表示用户没有授权权限;低16位为操作权限位,当该比特位取值为1时,表示AclItem中的ai_grantee对应的用户具有此对象的相应操作权限,否则表示用户没有相应的权限。在AclMode的结构位图1中,Grant Option记录各权限的授权权限的授予情况。低16位记录各权限的授予情况,当授权语句使用ALL时,则表示对象的所有权限。
图1 openGauss AclMode结构图
在openGauss的具体实现中,我们将对对象执行DML类操作和DDL类操作的权限分别记在两个AclMode结构中,并以第15位的值来区分二者,最终实现对于每一个数据库对象,相同的授权者和被授权者对应两个不同的AclMode,分别表示记录DML类操作权限和DDL类操作权限,实现方式如图2和图3所示。
图2 openGauss记录DML类操作权限的AclMode结构
图3 openGauss记录DDL类操作权限的AclMode结构
每个权限参数代表的权限如表1所示。
表1 权限参数
参数 | 对象权限 | 参数 | 对象权限 |
a | INSERT | T | TEMPORARY |
r | SELECT | c | CONNECT |
w | UPDATE | p | COMPUTE |
d | DELETE | R | READ |
D | TRUNCATE | W | WRITE |
x | REFERENCES | A | ALTER |
t | TRIGGER | P | DROP |
X | EXECUTE | m | COMMENT |
U | USAGE | i | INDEX |
C | CREATE | v | VACUUM |
(2)对象权限管理
数据库对象权限管理主要通过使用SQL命令GRANT/REVOKE授予或回收一个或多个角色在对象上的权限。GRANT/REVOKE命令都由函数ExecuteGrantStmt实现,该函数只有一个GrantStmt类型的参数,基本流程如图4。
图4 openGauss对象权限管理代码接口
数据结构GrantStmt定义如下:
typedef struct GrantStmt {
NodeTag type;
bool is_grant; // true = 授权, false = 回收
GrantTargetType targtype; // 操作目标的类型
GrantObjectType objtype; // 被操作对象的类型:表,数据库,模式函数等
List* objects; // 被操作对象的集合
List* privileges; // 要操作权限列表
List* grantees; // 被授权者的集合
bool grant_option; // true = with grant option/grant option for
DropBehavior behavior; // 回收权限的行为:restrict,cascade
} GrantStmt;
函数ExecuteGrantStmt首先将GrantStmt结构转化为InternalGrant结构,将权限列表转化为内部的AclMode表示形式。当privileges 取值为NIL时,表示授予或回收所有的权限,此时置InternalGrant的all_privs字段为true,privileges字段为ACL_NO_RIGHTS。
数据结构InternalGrant:
typedef struct InternalGrant {
bool is_grant; // true=授权, false=回收
GrantObjectType objtype; // 被操作对象的类型:表,数据库,模式函数等
List* objects; // 被操作对象的集合
bool all_privs; // 是否授予或回收所有的权限
AclMode privileges; // AclMode形式表示的DML类操作对应的权限
AclMode ddl_privileges; // AclMode形式表示的DDL类操作对应的权限
List* col_privs; // 对列执行的DML类操作对应的权限
List* col_ddl_privs; // 对列执行的DDL类操作对应的权限
List* grantees; // 被授权者的集合
bool grant_option; // true=with grant option/grant option for
DropBehavior behavior; // 回收权限的行为:restrict,cascade
} InternalGrant;
函数ExecuteGrantStmt在完成结构转换之后,调用ExecGrantStmt_oids,根据对象类型分别调用相应对象的权限管理函数。接下来以表对象的权限管理过程为例,介绍权限管理的算法,函数ExecGrant_Relation的处理流程如图5所示。
图5 openGauss表对象权限管理流程图
函数ExecGrant_Relation用来处理表对象权限的授予或回收操作,入参为InternalGrant类型的变量,存储着授权或回收操作的操作对象信息、被授权者信息和权限信息。
-
首先从系统表pg_class中获取旧ACL,如果不存在旧的ACL,则新建一个ACL,并调用函数acldefault将默认的权限信息赋给该ACL。根据对象的不同,初始的缺省权限含有部分可赋予PUBLIC的权限。如果存在旧的ACL,则将旧的ACL存储为一个副本。
-
然后调用select_best_grantor来获取授权者对操作对象所拥有的授权权限avail_goptions;将参数avail_goptions传入函数restrict_and_check_grant,结合SQL命令中给出的操作权限,计算出实际需要授予或回收的权限。
-
再调用merge_acl_with_grant生成新的ACL,如果是授予权限,则将要授予的权限添加到旧ACL中;如果是回收权限,则将要被回收的权限从旧ACL中删除。
-
最后将新的ACL更新到系统表pg_class对应元组的ACL字段,完成授权或回收过程。
具体的函数实现如下:
static void ExecGrant_Relation(InternalGrant* istmt)
{
. . .
// 循环处理每一个表对象。
foreach (cell, istmt->objects) {
. . .
// 判断所要操作的表对象是否存在,若不存在则提示报错。
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
if (!HeapTupleIsValid(tuple))
ereport(
ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for relation %u", relOid)));
pg_class_tuple = (Form_pg_class)GETSTRUCT(tuple);
. . .
// 系统表pg_class中获取旧ACL,若不存在旧的ACL,则新建一个ACL,若存在旧的ACL,则将旧的ACL存储为一个副本。
ownerId = pg_class_tuple->relowner;
aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl, &isNull);
if (isNull) {
switch (pg_class_tuple->relkind) {
case RELKIND_SEQUENCE:
old_acl = acldefault(ACL_OBJECT_SEQUENCE, ownerId);
break;
default:
old_acl = acldefault(ACL_OBJECT_RELATION, ownerId);
break;
}
noldmembers = 0;
oldmembers = NULL;
} else {
old_acl = DatumGetAclPCopy(aclDatum);
noldmembers = aclmembers(old_acl, &oldmembers);
}
old_rel_acl = aclcopy(old_acl);
// 处理表级别的权限。
if (this_privileges != ACL_NO_RIGHTS) {
AclMode avail_goptions;
Acl* new_acl = NULL;
Oid grantorId;
HeapTuple newtuple = NULL;
Datum values[Natts_pg_class];
bool nulls[Natts_pg_class] = {false};
bool replaces[Natts_pg_class] = {false};
int nnewmembers;
Oid* newmembers = NULL;
AclObjectKind aclkind;
// 获取授权者grantorId和授权者对该操作对象所拥有的授权权限avail_goptions。
select_best_grantor(GetUserId(), this_privileges, old_acl, ownerId, &grantorId, &avail_goptions);
switch (pg_class_tuple->relkind) {
case RELKIND_SEQUENCE:
aclkind = ACL_KIND_SEQUENCE;
break;
default:
aclkind = ACL_KIND_CLASS;
break;
}
// 结合参数avail_goptions和SQL命令中给出的操作权限,计算出实际需要授予或回收的权限。
this_privileges = restrict_and_check_grant(istmt->is_grant,
avail_goptions,
istmt->all_privs,
this_privileges,
relOid,
grantorId,
aclkind,
NameStr(pg_class_tuple->relname),
0,
NULL);
// 生成新的ACL,并更新到系统表pg_class对应元组的ACL字段。
new_acl = merge_acl_with_grant(old_acl,
istmt->is_grant,
istmt->grant_option,
istmt->behavior,
istmt->grantees,
this_privileges,
grantorId,
ownerId);
. . .
replaces[Anum_pg_class_relacl - 1] = true;
values[Anum_pg_class_relacl - 1] = PointerGetDatum(new_acl);
newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, nulls, replaces);
simple_heap_update(relation, &newtuple->t_self, newtuple);
. . .
}
// 若存在列级授权或回收,则调用ExecGrant_Attribute 函数处理。
. . .
if (have_col_privileges) {
AttrNumber i;
for (i = 0; i < num_col_privileges; i++) {
if (col_privileges[i] == ACL_NO_RIGHTS)
continue;
ExecGrant_Attribute(istmt,
relOid,
NameStr(pg_class_tuple->relname),
i + FirstLowInvalidHeapAttributeNumber,
ownerId,
col_privileges[i],
attRelation,
old_rel_acl);
}
}
. . .
}
heap_close(attRelation, RowExclusiveLock);
heap_close(relation, RowExclusiveLock);
}
3.4 权限检查
用户在对数据库对象进行访问操作时,数据库会检查用户是否拥有该对象的操作权限。通常数据库对象的所有者和初始用户(superuser)拥有该对象的全部操作权限,其他普通用户需要被授予权限才可以执行相应操作。数据库通过查询数据库对象的访问控制列表(ACL)检查用户对数据库对象的访问权限,数据库对象的ACL保存在对应的系统表中,当被授予或回收对象权限时,系统表中保存的ACL权限位会被更新。常用的数据库对象权限检查函数、ACL检查函数、ACL所在系统表以及对象所有者检查函数对应关系如表2所示。
表2 数据库对象函数对应关系表
对象 | 权限检查 | ACL检查 | 所有者检查 | 系统表 |
table | pg_class_aclcheck | pg_class_aclmask | pg_class_ownercheck | pg_class |
column | pg_attribute_aclcheck | pg_attribute_aclmask | NA | pg_attribute |
database | pg_database_aclcheck | pg_database_aclmask | pg_database_ownercheck | pg_database |
function | pg_proc_aclcheck | pg_proc_aclmask | pg_proc_ownercheck | pg_proc |
language | pg_language_aclcheck | pg_language_aclmask | pg_language_ownercheck | pg_language |
largeobject | pg_largeobject_aclcheck_snapshot | pg_largeobject_aclmask_snapshot | pg_largeobject_ownercheck | pg_largeobject_metadata |
namespace | pg_namespace_aclcheck | pg_namespace_aclmask | pg_namespace_ownercheck | pg_namespace |
tablespace | pg_tablespace_aclcheck | pg_tablespace_aclmask | pg_tablespace_ownercheck | pg_tablespace |
foreign data wrapper | pg_foreign_data_wrapper_aclcheck | pg_foreign_data_wrapper_aclmask | pg_foreign_data_wrapper_ownercheck | pg_foreign_data_wrapper |
foreign server | pg_foreign_server_aclcheck | pg_foreign_server_aclmask | pg_foreign_server_ownercheck | pg_foreign_server |
type | pg_type_aclcheck | pg_type_aclmask | pg_type_ownercheck | pg_type |
下面以表的权限检查为例进行权限检查过程说明。表权限检查函数pg_class_aclcheck定义如下:
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode, bool check_nodegroup)
{
if (pg_class_aclmask(table_oid, roleid, mode, ACLMASK_ANY, check_nodegroup) != 0)
return ACLCHECK_OK;
else
return ACLCHECK_NO_PRIV;
}
pg_class_aclcheck函数有4个入参,其中table_oid用于表示待检查的表,roleid用于表示待检查的用户或角色,mode表示待检查的权限,此权限可以是一种权限也可以是多种权限的组合。第4个参数check_nodegroup用于表示是否检查nodegroup逻辑集群权限,如果调用时不给此参数赋值则默认为true。函数返回值为枚举类型AclResult,如果检查结果有权限返回ACLCHECK_OK,无权限则返回ACLCHECK_NO_PRIV。
pg_class_aclcheck函数通过调用pg_class_aclmask函数实现对象权限检查。pg_class_aclmask函数有5个参数,其中第4个参数how为AclMaskHow枚举类型,包括ACLMASK_ALL和ACLMASK_ANY两种取值,ACLMASK_ALL表示需要满足待检查权限mode中的所有权限,ACLMASK_ANY表示只需满足待检查权限mode中的一种权限即可。pg_class_aclmask函数的其余4个参数table_oid、roleid、mode和check_nodegroup直接由pg_class_aclcheck函数传入。pg_class_aclmask函数从“pg_class”系统表中获取ACL权限信息并调用aclmask函数完成权限位校验,通过AclMode数据类型返回权限检查结果。
以上内容从权限管理和权限检查两方面对角色权限进行了介绍,下篇将从传统审计和统一审计两方面对高斯数据库的审计追踪技术进行解读,敬请期待~
相关文章:

数据库高安全—角色权限:权限管理权限检查
目录 3.3 权限管理 3.4 权限检查 书接上文数据库高安全—角色权限:角色创建角色管理,从角色创建和角色管理两方面对高斯数据库的角色权限进行了介绍,本篇将从权限管理和权限检查方面继续解读高斯数据库的角色权限。 3.3 权限管理 &#x…...
FastAPI 的依赖注入与生命周期管理深度解析
FastAPI 的依赖注入与生命周期管理深度解析 目录 🔧 依赖注入与 FastAPI 高级特性 1.1 依赖注入的基础与核心概念1.2 FastAPI 的依赖注入机制与设计理念1.3 FastAPI 依赖注入的异步特性 🕹 生命周期与依赖的异步管理 2.1 依赖的生命周期管理࿱…...

【express-generator】05-路由中间件和错误处理(第一阶段收尾)
一、前言 上篇文章我们介绍了express-generator的请求体解析,重点讲了常用的请求体数据格式(JSON/URL 编码的表单数据)以及一个FILE文件上传,同时搭配代码示范进行辅助理解。 二、本篇重点 我们继续第一阶段的知识,…...
Linux环境下确认并操作 Git 仓库
在软件开发和版本控制中,Git 已成为不可或缺的工具。有时,我们需要确认某个目录是否是一个 Git 仓库,并在该目录中运行脚本。本文将详细介绍如何确认 /usr/local/src/zcxt/backend/policy-system-backend 目录是否是一个 Git 仓库,…...

UDP -- 简易聊天室
目录 gitee(内有详细代码) 图解 MessageRoute.hpp UdpClient.hpp UdpServer.hpp Main.hpp 运行结果(本地通信) 如何分开对话显示? gitee(内有详细代码) chat_room zihuixie/Linux_Lear…...

NVIDIA在CES 2025上的三大亮点:AI芯片、机器人与自动驾驶、全新游戏显卡
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...

【通俗理解】AI的两次寒冬:从感知机困局到深度学习前夜
AI的两次寒冬:从感知机困局到深度学习前夜 引用(中英双语) 中文: “第一次AI寒冬,是因为感知机局限性被揭示,让人们失去了对算法可行性的信心。” “第二次AI寒冬,则是因为专家系统的局限性和硬…...

transformer深度学习实战CCTSDB中国交通标志识别
本文采用RT-DETR作为核心算法框架,结合PyQt5构建用户界面,使用Python3进行开发。RT-DETR以其高效的实时检测能力,在多个目标检测任务中展现出卓越性能。本研究针对CCTSDB交通标志数据集进行训练和优化,该数据集包含丰富的CCTSDB交…...

JavaWeb开发(六)XML介绍
1. XML介绍 1.1. 什么是XML (1)XML 指可扩展标记语言(EXtensible Markup Language)XML 是一种很像HTML的标记语言。 (2)XML 的设计宗旨是传输数据(目前主要是作为配置文件),而不是显示数据。 (3&a…...
使用pbootcms开发一个企业官网
V:llike620 pbootcms开源PHP建站系统 https://www.pbootcms.com/ 配置网站 域名解析后,网站绑定到程序根目录即可 例如:本地域名是dobot.test ,那么也要同步本地的hosts是 127.0.0.1 dobot.test 需要配置下伪静态规则 location / {if (!-e $r…...

Linux C编程——文件IO基础
文件IO基础 一、简单的文件 IO 示例二、文件描述符三、open 打开文件1. 函数原型2. 文件权限3. 宏定义文件权限4. 函数使用实例 四、write 写文件五、read 读文件六、close 关闭文件七、Iseek 绍 Linux 应用编程中最基础的知识,即文件 I/O(Input、Outout…...
【信息系统项目管理师】高分论文:论信息系统项目的风险管理(人民医院的信息系统)
更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 论文1、规划风险管理2、项目风险识别3、风险定性分析4、风险定量分析5、制定风险应对6、实施风险应对计划7、监督风险论文 2022年6月,我作为项目经理承担了XX县人民医院的信息系统建设,该项目总投资300万,其…...

UE播放声音
蓝图中有两个播放声音的函数 Play Sound 2D 和 Play Sound at Location Play Sound 2D没有声音距离衰减,一般用于界面ui Play Sound at Location 有声音距离衰减,一般用于枪声,场景声等,比较常用...
Docker Compose 启动 Harbor 并指定网络
1. 介绍 Harbor 是一个开源的企业级 Docker 镜像仓库,提供镜像存储、访问控制、安全扫描等功能。使用 Docker Compose 启动 Harbor 时,您可以指定一个自定义网络,以便管理容器之间的网络通信。在本示例中,我们将创建一个名为 har…...
WebSocket 实战案例:从设计到部署
在前六篇文章中,我们深入探讨了 WebSocket 的基础原理、服务端开发、客户端实现、安全实践、性能优化和测试调试。今天,让我们通过一个实战案例,看看如何将这些知识应用到实际项目中。我曾在一个大型在线教育平台中,通过 WebSocket 实现了实时互动课堂,支持了数万名师生的同时在…...
selenium合集
环境搭建步骤 安装selenium pip install selenium 安装浏览器 安装浏览器驱动 谷歌浏览器:chromdriver.exe ie浏览器:ieserverdriver.exe FireFox浏览器:geckodriver.exe 特别注意⚠️:下载驱动版本必须与浏览器版本一致 下载地址 淘宝镜像࿱…...
JVM生产环境常用参数配置及调优建议
一、生产常用参数配置 JAVA_OPTS"-server -Xms3000m -Xmx3000m -Xmn1500m -XX:UseG1GC -XX:ConcGCThreads8 -XX:PrintGCDetails -XX:PrintGCTimeStamps -Xloggc:./g1-gc.log -XX:MaxMetaspaceSize256m -XX:-UseGCOverheadLimit -XX:UseCompressedOops -XX:HeapDumpOnOu…...
Spring Boot 3 实现 MySQL 主从数据库之间的数据同步
✅ Spring Boot 3 实现 MySQL 主从数据库之间的数据同步 在实际项目中,为了提高 系统的读性能 和 数据的可用性,通常会使用 主从数据库架构。Spring Boot 提供了对 多数据源 的良好支持,可以轻松配置 主从数据库 的数据同步,实现…...

【小程序开发】- 小程序版本迭代指南(版本发布教程)
一,版本号 版本号是小程序版本的标识,通常由一系列数字组成,如 1.0.0、1.1.0 等。版本号的格式通常是 主版本号.次版本号.修订号 主版本号:当小程序有重大更新或不兼容的更改时,主版本号会增加。 次版本号:…...
MySQL 间隙锁避免“可重复读”出现“幻读”
在数据库事务处理中,可重复读(Repeatable Read)是一个常用的隔离级别,但其默认行为可能导致幻读现象。然而,在 MySQL 的实现中,通过 **间隙锁(Gap Lock)**机制,能够避免幻…...

手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...
解决:Android studio 编译后报错\app\src\main\cpp\CMakeLists.txt‘ to exist
现象: android studio报错: [CXX1409] D:\GitLab\xxxxx\app.cxx\Debug\3f3w4y1i\arm64-v8a\android_gradle_build.json : expected buildFiles file ‘D:\GitLab\xxxxx\app\src\main\cpp\CMakeLists.txt’ to exist 解决: 不要动CMakeLists.…...

STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...
2025年低延迟业务DDoS防护全攻略:高可用架构与实战方案
一、延迟敏感行业面临的DDoS攻击新挑战 2025年,金融交易、实时竞技游戏、工业物联网等低延迟业务成为DDoS攻击的首要目标。攻击呈现三大特征: AI驱动的自适应攻击:攻击流量模拟真实用户行为,差异率低至0.5%,传统规则引…...