
前阵子帮客户处理GBase 8s数据库的权限整改有一项工作就是取消一个开发账号上的EXTEND角色。这个操作听起来简单实际动手时却有不少细节需要注意比如这个角色到底管什么、撤销后对已有UDR有没有影响、为什么撤销完用户还能建UDR。今天我把这个问题从头到尾捋一遍希望给正在做同类权限回收的DBA和运维同学一点参考。先解释一下GBase 8s是南大通用推出的一款关系型数据库语法体系保留了很多Informix时代的习惯。EXTEND在GBase 8s里不是函数也不是方法而是一种和数据库权限有关的角色通常翻译成“扩展角色”。它专门用来控制用户能不能在数据库里创建用户自定义例程UDR比如存储过程、自定义函数、C语言或Java编写的扩展函数。换句话说一旦用户被授予了EXTEND他就有能力给数据库“加料”如果你不想让他继续这么干就需要把这个角色收回来。1. EXTEND角色是什么为什么需要管1.1 先理清EXTEND在GBase 8s里的真实含义GBase 8s的权限体系里数据库级权限通常分成几档CONNECT、RESOURCE、DBA还有EXTEND。很多刚开始接触这个数据库的人会下意识地把EXTEND理解成编程语言里的“扩展函数”比如Python的list.extend()、jQuery的$.extend实际上在GBase 8s里它是一个权限角色官方文档里也把它归类为与用户访问级别相关的角色之一。用大白话说EXTEND角色解决的是这样一个问题数据库的内置功能不够用时你是否允许某个用户用C、Java或者SPL存储过程语言给数据库扩展新的例程。UDR就是User-Defined Routine也就是用户自定义例程它可以是一个函数也可以是一个存储过程甚至可以是自定义的复杂数据类型。EXTEND角色就是判断某个用户有没有资格“开发并注册”这些自定义能力。所以当有人和你说“GBase 8s的EXTEND”他大概率不是在讨论某个扩展方法而是在讨论某个数据库账号是否有权注册UDR。这个区分非常重要因为我在实际运维中就见过不少同事听到“EXTEND”先跑去翻前端文档翻了一圈才发现方向错了。1.2 EXTEND角色能做什么不能做什么EXTEND角色能做的事集中在“创建和注册UDR”这一块具体包括编写并注册SPL存储过程和函数将C语言或Java编写的自定义函数注册为数据库可调用的UDR注册用户自定义数据类型及其支撑函数。但如果有人以为有了EXTEND就能为所欲为那就想多了。它不能直接读取其他用户表的数据除非对方把表的访问权限授予了你它也不能随意修改表结构如果不同时具备RESOURCE权限它更不能管理用户和授权那是DBA的事。把EXTEND角色类比成“能不能进后厨做新菜”的资格再合适不过你有资格研发新菜不代表你能翻老板的账本也不代表你能决定餐厅菜单。这种权限边界的清晰划分也是数据库权限管控的基本逻辑。1.3 什么时候需要取消EXTEND角色实际操作中取消EXTEND角色的触发场景通常很明确员工离职或调岗原账号不再需要高级开发权限权限复核发现账号被过度授权按最小权限原则整改安全审计报告中明确点出某账号持有EXTEND要求在限定时间内回收项目收尾临时参与数据迁移或功能扩展的账号不再需要写UDR。这里先提醒一句取消EXTEND角色不等于删除用户更不等于删除该用户已经创建的UDR对象。它只是把“以后新增和注册UDR”的能力收回已经存在的函数、过程以及它们被授予的执行权限一般不会被自动删除。2. 动手前先搞清楚权限与现状2.1 查看用户当前被授予的角色回收权限最忌讳摸黑操作。先确认清楚目标用户当前到底持有哪些数据库级权限再决定怎么撤。在GBase 8s里每个用户在某个数据库中的数据库级权限情况最终会记录在系统目录表sysusers中。以DBA身份登录后可以直接执行SELECT username, usertype FROM sysusers WHERE username u_dev;如果返回记录里usertype列显示的标识对应EXTEND不同版本的显示方式有差异有的直接是“X”有的会带扩展角色标识就说明该用户当前持有EXTEND角色。一些图形化管理工具更直观会在用户信息面板里直接列出CONNECT、RESOURCE、EXTEND、DBA等标签。注意一点用户名大小写要看实际建库建用户时的习惯GBase 8s部分版本对标识符大小写敏感。拿不准就先查一下SELECT DISTINCT username FROM sysusers;看看库里的真实写法再带条件查询避免因为大小写问题漏掉记录。2.2 判断当前操作者是否有权限取消EXTEND角色本质上是在执行REVOKE操作。数据库对REVOKE的权限要求很明确执行者必须是目标数据库的DBA权限持有者或者通过WITH ADMIN OPTION等机制被授予了用户权限管理权。普通用户执行REVOKE EXTEND大概率会收到类似“没有此权限”的报错。遇到报错先别急着怀疑语句写错先检查两件事当前连接的数据库是不是目标数据库登录用户是不是该库的DBA或者具备对应的授权管理能力。在我自己处理的案例里一半以上的撤销失败是因为操作者连错了库剩下的是因为用了普通业务账号去执行管理操作。2.3 评估撤销后的影响面权限回收影响面评估比执行SQL本身更重要尤其是EXTEND这种不直接作用于数据的角色它对存量对象的影响容易让人误判。首先存量UDR不会因为撤销而被删除。已经创建好的函数、存储过程会继续存在之前授权的执行权限也不会自动失效。其次增量开发会受限制目标用户后续再想注册新的SPL函数或C/Java UDR数据库会直接拒绝。第三正在运行的会话不一定立刻生效有些应用连接池会缓存权限状态稳妥做法是让目标用户的所有会话退出重连。最后如果用户本身还有DBA权限那单独撤销EXTEND基本等于没撤因为DBA权限已经隐含了扩展能力这一点后面会专门展开。3. 取消EXTEND角色的完整操作流程3.1 核心语法与官方语句格式GBase 8s沿用SQL标准里的REVOKE语句取消EXTEND角色的基本语法如下REVOKE EXTEND FROM user_name;如果是从多个用户批量回收可以写成REVOKE EXTEND FROM u_dev1, u_dev2, u_dev3;如果希望取消所有用户通过PUBLIC获得的EXTEND权限则执行REVOKE EXTEND FROM PUBLIC;你可能会疑惑用户名外面为什么要加单引号。在实际GBase 8s环境中数据库登录用户通常按字符串处理官方很多示例也使用单引号。但部分版本或客户端工具允许不带引号直接写用户名。我的建议是先按带引号的方式执行如果报语法错误再打开当前版本的《GBase 8s SQL指南》核对语法图以官方写法为准。这里还要说明一下REVOKE EXTEND属于权限管理类DDL操作执行后即时生效不需要重启数据库服务。这一点和创建表、创建索引这类DDL是一样的。3.2 典型场景实操演示我用一个常见整改任务来演示开发账号u_dev原本拥有CONNECT、RESOURCE、EXTEND三种数据库级权限现在要求收回EXTEND只保留CONNECT和RESOURCE。第一步用DBA账号登录目标数据库。如果使用字符界面工具dbaccess可以这样进入dbaccess demo_db -第二步先查一下当前权限状态确认u_dev的usertype值SELECT username, usertype FROM sysusers WHERE username u_dev;第三步执行撤销语句REVOKE EXTEND FROM u_dev;第四步再次查询验证。如果usertype列中不再显示EXTEND对应的标记说明权限已经回收成功。第五步让u_dev重新建立会话然后尝试注册一个需要EXTEND角色的外部UDR比如CREATE FUNCTION test_extend_c(v INT) RETURNING INT EXTERNAL NAME /usr/gbasedbt/extlib/test.so LANGUAGE C;在已经失去EXTEND角色的用户下执行这种语句应当得到权限不足的提示如果还能成功执行说明权限没有真正收干净需要回到第2章的方法重新排查。3.3 撤销后的权限配置建议权限收回之后建议根据用户的后续职责做一次权限配置复核。如果用户只是需要继续维护表结构、写普通的SPL逻辑保留RESOURCE和CONNECT就够了。这种场景下撤销EXTEND不会影响他的常规开发工作。如果是调岗或离职人员建议把RESOURCE、EXTEND甚至DBA一起明确回收REVOKE DBA, RESOURCE, EXTEND FROM u_dev;然后再根据实际情况决定是否连CONNECT权限也撤掉或者直接冻结账号。另外所有权限变更都建议记录下来包括变更时间、操作人、执行语句、验证结果。每次权限整改都留痕后续安全审计和问题排障会省很多事。4. 实操路上的坑与排查方法4.1 撤销时报错没有权限现象很好认在非DBA用户下执行REVOKE EXTEND数据库直接给出权限不足的提示或者明明用DBA登录了却提示当前数据库中没有该用户。后者多半是连错了数据库。排查时可以按这个顺序来先执行SELECT CURRENT FROM systables确认当前连接的数据库再执行SELECT username, usertype FROM sysusers确认目标用户是否真的存在于当前库最后确认登录用户是否具备DBA角色。我还遇到过一种情况有同事从实例A登录却去撤销实例B里某个用户的权限自然怎么执行都不对。这种问题不属于语法范畴属于操作习惯问题养成“操作前先确认当前环境”的习惯就能避免。4.2 撤销成功但用户还能创建UDR这个问题我在支持群里见过多次原因基本集中在以下几点。一是用户的实际权限级别是DBA。DBA权限本身隐含了创建UDR的能力单撤EXTEND没有意义。你需要和业务方确认这个账号是否必须保留DBA如果不需要就一并回收或者重建一个最小权限账号。二是EXTEND权限来自PUBLIC。用户没有被单独授予EXTEND但PUBLIC这个公共主体拥有EXTEND等于所有用户都能迂回获得扩展能力。这种情况下要单独撤销某个用户不生效得执行REVOKE EXTEND FROM PUBLIC;当然执行这条语句前务必评估影响范围确认是否会影响其他正常需要UDR权限的用户。三是应用连接池缓存了旧权限。有些连接池在连接建立时拉取了权限快照DBA撤销后池里的旧连接仍然“以为”自己还有权限。解决办法是让目标应用重启连接池或者让用户所有会话退出重连。四是用户所属的角色又把权限补了回来。如果用户属于某个SQL ROLE角色的授权情况也要同步检查只撤销用户个体角色权限一旦下发还是会被用户继承到。4.3 EXTEND、RESOURCE、DBA概念混淆把EXTEND、RESOURCE、DBA搞混是非常典型的问题。这里整理了一张速查表方便对号入座数据库级权限/角色核心能力典型应用场景CONNECT允许连接数据库访问已授权对象报表查询、前台业务账号RESOURCE在CONNECT基础上可创建表、索引、视图等对象应用开发、表结构维护EXTEND创建、注册、删除用户自定义例程(UDR)需要开发C/Java/SPL扩展功能的高级开发DBA最高权限包含用户管理、对象管理、权限分配DBA、运维管理员、临时迁移账号从表里可以看出EXTEND不等于RESOURCE也不是RESOURCE的升级版。两者更像是分工不同RESOURCE管“建表”EXTEND管“建函数/过程”DBA则是“一把全收”。权限整改时先把账号按这个表对号入座再决定撤哪些思路会清晰很多。4.4 撤销语句执行成功但就是不生效这种“不生效”多半和会话缓存有关。GBase 8s在部分版本里对已建立会话的权限检查存在缓存DBA在同一时刻撤销但用户不重连就不会重新加载权限状态。处理办法是通知目标用户退出所有数据库会话再重新登录如果客户端比较老可能需要等权限缓冲刷新。最直接的办法是约定一个维护窗口使用管理工具强制断开指定账号的在线会话断开后重连再看效果。5. 顺带聊聊被热搜带偏的“extend”5.1 jQuery的$.extend与$.fn.extend区别搜索“extend”时你会看到大量前端文章。jQuery里有同名但作用不同的两个API$.extend给jQuery核心对象扩展静态方法调用时直接用$比如myMethod$.fn.extend给jQuery实例对象扩展方法供选择器结果调用比如$(div).myPlugin()。简单记一个扩展“类本身”一个扩展“类实例”。这和你写的jQuery插件机制直接相关但和GBase 8s的EXTEND角色完全是两码事纯粹是英文单词恰好一样。5.2 Python的extend()函数到底做了什么Python里的list.extend()是列表对象的实例方法作用是把一个可迭代对象中的每个元素依次追加到当前列表末尾。它和append()最大的区别是a [1, 2] a.extend([3, 4]) # 结果是 [1, 2, 3, 4] b [1, 2] b.append([3, 4]) # 结果是 [1, 2, [3, 4]]extend()是就地修改原列表返回None不是返回一个新列表。很多Python新手在这里栽过跟头。不过这个extend和数据库权限管理依旧没有关系。5.3 回到GBase 8s别把数据库EXTEND当方法调用会搜到这些热词很正常因为“extend”在编程世界里到处都是。但当你面对GBase 8sEXTEND必须放在权限管理语境里理解它是角色/权限级别不是函数方法常见的语法不是xxx.extend()而是GRANT EXTEND TO和REVOKE EXTEND FROM。遇到权限问题建议带上“GBase 8s”做限定词搜索比如“GBase 8s REVOKE EXTEND”“GBase 8s EXTEND角色”能少踩不少信息干扰。6. 权限回收的两点心得补充自己做权限整改时踩过几次坑最大的体会是权限回收的难点往往不在执行REVOKE而在操作前的梳理和操作后的验证。EXTEND这种角色看着不常用实际影响大稍不注意就会漏掉PUBLIC授权或者连接池缓存这类隐性路径导致整轮整改白做。最后分享一个小习惯每次权限变更前我会先把sysusers里相关用户的usertype截图或导出留档执行完REVOKE之后再对比一次确认变化符合预期再把变更记录整理进运维手册。这套流程看起来繁琐但在审计和排障的时候真的能给你省下大把时间。