用户权限管理

管理员,用户和组

管理员可以创建其他管理员、用户和组。非管理员的用户不能创建其他用户或组。

当 DolphinDB 集群第一次启动时,会自动创建一个管理员,用户 ID 为 “admin”,密码为 “123456”。”admin” 管理员具有所有的权限,并且不能被删除。其他管理员在刚创建时,是没有任何权限的,需要手动赋予他权限。

管理员可以赋予或禁止其他管理员/用户/组的权限,亦可撤销权限设置。

用户可以属于 0, 1 或多个组,把用户添加到组或把用户从组中移除会影响用户的访问权限。

权限类型

新创建的管理员、用户和组没有任何权限。超级管理员或其他具有管理员权限的用户可以使用 grantdenyrevoke 命令为非超级管理员的用户赋予权限、禁止权限,以及撤销权限设置,对应的权限状态为 Allow,Deny 和 None。

以下是 DolphinDB 支持的权限类型( accessType)、权限作用的范围(objs)和权限含义的详细表格。需要注意的是,如果运行在高可用环境下,并升级 server 到2.00.9版本,则需要手动为 admin 用户授予 DB_OWNER 权限:

accessType objs(标量或向量) 权限说明 注释
TABLE_READ 全局(*)、表(dfs://db/tb) 读取所有/指定数据表
TABLE_WRITE 全局(*)、表(dfs://db/tb) 写入所有/指定数据表,包含表数据的增删改权限
TABLE_INSERT 全局(*)、表(dfs://db/tb) 追加数据到指定数据表 2.00.9/1.30.21 新增权限
TABLE_UPDATE 全局(*)、表(dfs://db/tb) 更新指定数据表的数据 2.00.9/1.30.21 新增权限
TABLE_DELETE 全局(*)、表(dfs://db/tb) 删除指定数据表的数据 2.00.9/1.30.21 新增权限
DBOBJ_CREATE 全局(*)、库(dfs://db) 在所有/指定数据库创建表
DBOBJ_DELETE 全局(*)、库(dfs://db) 在所有/指定数据库删除表(包括数据和表结构)
DB_READ 全局(*)、库(dfs://db) 读某库所有表 2.00.9/1.30.21 新增权限
DB_WRITE 全局(*)、库(dfs://db) 写某库所有表,包含数据库所有表的增删改权限 2.00.9/1.30.21 新增权限
DB_INSERT 全局(*)、库(dfs://db) 追加数据到某库所有表 2.00.9/1.30.21 新增权限
DB_UPDATE 全局(*)、库(dfs://db) 更新数据到某库所有表 2.00.9/1.30.21 新增权限
DB_DELETE 全局(*)、库(dfs://db) 删除数据到某库所有表 2.00.9/1.30.21 新增权限
VIEW_EXEC 全局(*)、视图(viewName) 执行所有/指定视图
DB_OWNER 全局(*或不指定)、grant 时支持指定特定前缀的库(dfs://{dbPrefix}*) 库:创建数据库/删除自己的数据库
表:创建/删除自己数据库下的表以及增加/删除表的分区、增加/删除表的列、更改表名。
权限:grant/revoke/deny 其他用户对自己创建数据库的以下权限:TABLE_READ, TABLE_WRITE, TABLE_INSERT, TABLE_UPDATE, TABLE_DELETE, DBOBJ_CREATE, DBOBJ_DELETE, DB_READ, DB_WRITE, DB_INSERT, DB_UPDATE, DB_DELETE
DB_MANAGE 全局(*或不指定)、库(dfs://db) 赋予任意或指定数据库的管理权限,包括:
库:删除数据库
表:创建/删除/重命名数据表
分区:增加/删除分区
列:增加/删除/重命名/替换列
VIEW_OWNER 普通用户(组)创建视图的权限。只有管理员才能 grant/deny/revoke 这个权限。普通用户(组)被赋予该权限后,可以进行以下操作:
可以创建视图(addFunctionView),且在创建视图后自动拥有该视图的 VIEW_EXEC 权限
drop 自己创建的视图(dropFunctionView)
为其它用户 grant/deny/revoke 自己所创建视图的 VIEW_EXEC 权限
通过 getFunctionViews()查看自己创建的所有视图
2.00.10.4 新增权限
SCRIPT_EXEC 全局(*或不指定) 执行脚本
TEST_EXEC 全局(*或不指定) 执行测试脚本
QUERY_RESULT_MEM_LIMIT 内存大小,单位为 GB 限制用户的查询内存大小 2.00.9/1.30.21 新增权限,仅用于 grant 函数和 revoke 函数
TASK_GROUP_MEM_LIMIT 内存大小,单位为 GB 限制用户发送的批量子查询占用的内存大小 2.00.9/1.30.21 新增权限,仅用于 grant 函数和 revoke 函数

注意:

  • 当用户创建定时作业(函数 scheduleJob)时,若作业中包含库/表的读写或修改操作,不需要具备相关的权限。但执行定时作业时,需要具备与对象相关的权限。

  • 上述权限仅支持作用在分布式数据库。

兼容性说明:

  • 1.30.15 版本后支持对用户赋予(grant)、禁止(deny)或撤销(revoke)共享内存表/共享流数据表/流数据引擎的读(TABLE_READ)或写(TABLE_WRITE)权限。

  • 1.30.21 版本后:

    • 新增了更细粒度的表权限(TABLE_INSERT/TABLE_UPDATE/TABLE_DELETE),以及库权限(DB_INSERT/DB_UPDATE/DB_DELETE)。

    • 修改了 DB_MANAGE 的权限,不再支持创库,只支持对库进行 DDL 级别的操作管理,因此升级后需要重新赋予原 DB_MANAGE 用户 DB_OWNER 权限。

    • 新增了权限类型 QUERY_RESULT_MEM_LIMIT,TASK_GROUP_MEM_LIMIT 用于约束用户查询内存的上限。

若对共享流数据表开启权限管理,需要注意:

  1. 在订阅节点,用户使用 subscribeTable 函数订阅流数据表之前,应当确认其是否具有流表的 TABLE_READ 权限。 没有 TABLE_READ 权限的用户不能订阅该流表。

  2. 在发布节点,若需要向共享流数据表写入数据,需要同时具有 TABLE_READ 和 TABLE_WRITE 的权限。

  3. 流表的发布端和订阅端不在同一个节点时,权限对象必须为 “nodeAlias:tableName”,比如:deny(`amy,TABLE_READ,”DFS_NODE1:st”), DFS_NODE1 为流数据表所在节点 Alias,st 为流数据表名。

  4. 只有流数据表的创建者或 admin 用户可以删除表。

  5. 使用 grant 赋予流数据表权限后,建议使用 revoke 进行撤销。

  6. 当用户从一个流数据表中订阅数据存入数据表时,应当确保其有写入此数据表的权限。

权限确定规则

权限作用对象由高到底分为全局(*),数据库,表三个层级。

grant/revoke/deny 权限给某个对象时,系统会先检查是否存在更高层级的权限:

  • 若不存在,则先 revoke 该层级下所有对象的原来的权限,再执行 grant/revoke/deny 操作。

  • 若存在:

    • 更高层级的权限为 “Allow”,则 grant/revoke 操作无效,deny 操作可以生效。

    • 更高层级的权限为 “Deny”,则 grant/revoke/deny 的操作均无效。

例1:先设置表级权限,再设置库级权限:

deny(`userA, TABLE_READ, "dfs://testdb/pt")
grant(`userA, DB_READ, "dfs://testdb")

先执行 deny 禁止 userA 访问表 pt。再执行 grant 操作时,由于数据库 dfs://testdb 的权限层级大于数据表 pt ,因此:会先 revoke 数据库下所有表的读权限,原来 userA 对表 pt 的权限状态 “Deny” 将被清空;再赋予 userA 该数据库下所有表的读权限。因此 userA 对表 pt 的最终权限状态为 “Allow”。

例 2:先设置库级权限,再设置表级权限:

grant(`userA, DB_READ, "dfs://testdb")
revoke(`userA, TABLE_READ, "dfs://testdb/pt")

先执行 grant 允许 userA 访问数据库 dfs://testdb 下的所有表。再执行 revoke 操作时,由于已经存在一个库级的读权限,因此不能单独 revoke/allow 表的权限。因此 userA 对表 pt 的最终权限状态仍为 “Allow”。

若替换上述语句为:

grant(`userA, DB_READ, "dfs://testdb")
deny(`userA, TABLE_READ, "dfs://testdb/pt")

由于 deny 操作可以执行,最终 userA 对表 pt 的权限状态为 “Deny”, 对该数据库下其他分布式表的权限状态仍为 “Allow”。

若一个用户属于多个组,则用户的最终权限是个人权限(通过函数 getUserAccess 获取)和所属组权限(通过函数 getGroupAccess 获取)的共同决定的结果。

以用户 userA 对数据库 dfs://testdb 下的表 pt 的读权限为例:

系统遍历 userA 所属的用户组和 userA 个人的权限列表,检查在全局、数据库dfs://testdb 以及表 pt 上,是否存在状态为 “Deny” 的读权限 。

  • 若存在 “Deny” 状态,则 userA 对表 pt 的最终权限状态为 “Deny”。

  • 若不存在 “Deny” 状态,但存在 “Allow” 状态,则 userA 对表 pt 的最终权限状态为 “Allow”。

  • 否则,userA 对表 pt 的最终权限状态为 “None”。

管理员通过 grant/revoke/deny 命令更改某个用户的权限后,该用户的权限会在重新登录账户或新的 session 中生效。

函数权限

只有管理员才能执行的权限函数如下:

此外部分运维函数也只能由管理员执行,具体请参考函数详情的说明。

DDL 和 DML 操作相关函数的权限校验

注意:下表中拥有 DB_OWNER 权限的用户必须为创建数据库或表的用户。

函数 权限(1.30.21/2.00.9以下版本) 权限(1.30.21/2.00.9及以上版本)
database或createDB创建数据库 DB_MANAGE, DB_OWNER DB_OWNER
database 加载已有的库 无校验 无校验
dropDatabase DB_MANAGE, DB_OWNER DB_MANAGE, DB_OWNER
createTable DBOBJ_CREATE, DB_OWNER DBOBJ_CREATE, DB_OWNER, DB_MANAGE
dropTable DBOBJ_DELETE, DB_OWNER DBOBJ_DELETE, DB_MANAGE, DB_OWNER
createPartitionedTable DB_MANAGE, DB_OWNER DBOBJ_CREATE, DB_MANAGE, DB_OWNER
renameTable DBOBJ_DELETE, DB_OWNER 自己创建的表或拥有TABLE_READ权限,且拥有以下任一种权限:DBOBJ_CREATE, DB_OWNER, DB_MANAGE
loadTable 自己创建的表,TABLE_READ, VIEW_EXEC 自己创建的表,TABLE_READ, VIEW_EXEC
addPartitions DB_MANAGE, DB_OWNER DB_MANAGE, DB_OWNER
dropPartition DBOBJ_DELETE, DB_OWNER 指定参数 deleteSchema=false: DB_MANAGE, DB_OWNER, DB_DELETE, TABLE_DELETE(全局)<br>指定参数 deleteSchema=true:DB_MANAGE, DB_OWNER
addColumn 自己创建的表,DBOBJ_CREATE 自己创建的表或拥有TABLE_READ权限,且拥有以下任一种权限:DB_MANAGE, DB_OWNER, DBOBJ_CREATE
dropColumns! DB_MANAGE, DB_OWNER 自己创建的表或拥有TABLE_READ权限,且拥有以下任一种权限:DB_MANAGE, DB_OWNER, DBOBJ_DELETE
rename! DB_MANAGE, DB_OWNER 自己创建的表或拥有TABLE_READ权限,且拥有以下任一种权限:DBOBJ_CREATE, DB_OWNER, DB_MANAGE
replaceColumn! 自己创建的表或拥有TABLE_READ权限,且拥有以下任一种权限:DBOBJ_CREATE, DB_OWNER, DB_MANAGE
setColumnComment 自己创建的表,DBOBJ_CREATE 自己创建的表或拥有TABLE_READ权限,且拥有以下任一种权限:DB_MANAGE, DB_OWNER, DBOBJ_CREATE
truncate 自己创建的表,TABLE_WRITE, VIEW_EXEC 自己创建的表,TABLE_WRITE, VIEW_EXEC
upsert/SQL update 自己创建的表,VIEW_EXEC 自己创建的表,TABLE_WRITE, VIEW_EXEC
SQL delete 自己创建的表,TABLE_WRITE, VIEW_EXEC 自己创建的表,TABLE_WRITE, VIEW_EXEC

权限和函数可见性

相关函数
getAllDBs
getClusterDFSDatabases
getDFSDatabases
getDFSTablesByDatabase

注意,这些函数的返回值受当前登录用户的权限影响:

  • 若用户是管理员或者拥有 DB_MANAGE 权限,则可以看见当前节点所有数据库;

  • 若用户拥有 DB_OWNER 权限,则可以看见当前节点该用户所创建的数据库;

例子

  1. 以管理员身份登录系统,并创建数据库 dfs://db1 以及其内的数据表 pt1。

$ login(`admin, `123456);

$ n=1000000
$ ID=rand(10, n)
$ x=rand(100, n)
$ t1=table(ID, x)

$ db=database("dfs://db1", HASH,  [INT, 2]);
$ pt1 = db.createPartitionedTable(t1, `pt1, `ID)
$ pt1.append!(t1)
  1. 创建名为 “football” 的组,该组包含3个成员,分别是 EliManning、JoeFlacco 和 DeionSanders。该组的所有成员都可以读取 dfs://db1/pt1 表中的数据。用户 DeionSanders 可以删除数据库。

$ createUser(`EliManning, "AB123!@")
$ createUser(`JoeFlacco, "CD234@#")
$ createUser(`DeionSanders, "EF345#$")
$ createGroup(`football, `EliManning`JoeFlacco`DeionSanders)
$ grant(`football, TABLE_READ, "dfs://db1/pt1")
$ grant("DeionSanders", DB_MANAGE);

用户 EliManning 不可创建数据库:

$ login(`EliManning, "AB123!@");
$ db=database("dfs://db2", HASH,  [INT, 2]);

$ db = database("dfs://db2", HASH, [4,2]) => Not granted to create or delete databases.
  1. 增加两个成员到 “football” 组中,并且把 JoeFlacco 从组中移除。使用 getUsersByGroupId 函数可以获取 “football” 组中的用户。

$ login(`admin, `123456);

$ createUser(`AlexSmith, "GH456$%")
$ createUser(`NickFoles, "IJ567%^")
$ addGroupMember(`AlexSmith`NickFoles, `football)
$ deleteGroupMember(`JoeFlacco, `football)
$ getUsersByGroupId(`football);

["AlexSmith","DeionSanders","EliManning","NickFoles"]
  1. 创建名为 “baseball” 的组,该组包含 3 个成员,分别是 CliffLee、ShoheiOhtani 和 DeionSanders。

$ createUser(`CliffLee, "GH456$%")
$ createUser(`ShoheiOhtani, "IJ567%^")
$ createGroup(`baseball, `CliffLee`ShoheiOhtani`DeionSanders)

DeionSanders 属于两个组。使用 getGroupsByUserId 函数可以获取 DeionSanders 所在的组。

$ getGroupsByUserId(`DeionSanders);

["football","baseball"]

进行以下权限设置:

$ grant(`baseball, DBOBJ_CREATE, "dfs://db1")
$ deny(`baseball, TABLE_READ, "dfs://db1/pt1")
$ deny(`baseball, DB_MANAGE);

“football” 组可以读取 dfs://db1/pt1 表,而 “baseball” 组被禁止了该权限。因此,DeionSanders 不具有该权限。

$ login(`DeionSanders, "EF345#$");
$ t = loadTable("dfs://db1","pt1");
t = loadTable("dfs://db1", "pt1") => Not granted to read table dfs://db1/pt1

尽管 DeionSanders 之前已经被赋予了删除数据库的权限,但是步骤4中的 “baseball” 组被禁止了该权限。DeionSanders 作为 “baseball” 组的一个成员,也被禁止了该权限。当 DeionSanders 不属于 “baseball” 组,或撤销禁止 “baseball” 组删除数据库的权限,或授予 “baseball” 组该权限时,用户 DeionSanders 才具有删除数据库的权限。

  1. 定义一个计算表 dfs://db1/pt1 行数的函数,将它定义为视图,并赋予 “baseball” 组执行该视图的权限。

$ login(`admin, `123456);

$ def countPt1(){
$     return exec count(*) from loadTable("dfs://db1","pt1")
$ }

$ addFunctionView(countPt1)
$ grant("baseball", VIEW_EXEC, "countPt1");

尽管 “baseball” 组没有读取表 dfs://db1/pt1 的权限,但是该组成员可以通过视图 countPt1 来获取表中的记录条数。以 ShoheiOhtani 身份登录,执行以下脚本:

$ login(`ShoheiOhtani, "IJ567%^");
$ countPt1();
1000000
  1. 定义一个函数,以计算表 dfs://db1/pt1 中 ID 为指定值的某列的最大值,将其定义为视图,并赋予 “baseball” 组执行该视图的权限。

$ login(`admin, `123456);

$ def getMax(column, idValue){
$ return exec max(column) from loadTable("dfs://db1","pt1") where id=idValue
$ }

$ addFunctionView(getMax)
$ grant("baseball", VIEW_EXEC, "getMax");

用户 CliffLee 可以登录并执行视图 getMax。

$ login(`CliffLee, "GH456$%")
$ getMax(x, 6);
99
  1. 管理员赋予用户 MitchTrubisky DB_OWNER 的权限:

$ login(`admin, `123456);
$ createUser(`MitchTrubisky, "JI3564^")
$ grant(`MitchTrubisky,DB_OWNER);

MitchTrubisky 创建数据表 dfs://dbMT/dt,并赋予用户 NickFoles 读取该数据表的权限:

$ login(`MitchTrubisky, "JI3564^");
$ db = database("dfs://dbMT", VALUE, 1..10)
$ t=table(1..1000 as id, rand(100, 1000) as x)
$ dt = db.createTable(t, "dt").append!(t)
$ grant(`NickFoles, TABLE_READ, "dfs://dbMT/dt");

用户 NickFoles 登录并读取 dfs://dbMT/dt 数据进行计算:

$ login(`NickFoles, "IJ567%^")
$ select max(x)-min(x) from loadTable("dfs://dbMT"gi, "dt");
99