IoTDB 为用户提供了权限管理操作,为用户提供对数据与集群系统的权限管理功能,保障数据与系统安全。 本篇介绍IoTDB 中权限模块的基本概念、用户定义、权限管理、鉴权逻辑与功能用例。在 JAVA 编程环境中,您可以使用 JDBC API 单条或批量执行权限管理类语句。
用户即数据库的合法使用者。一个用户与一个唯一的用户名相对应,并且拥有密码作为身份验证的手段。一个人在使用数据库之前,必须先提供合法的(即存于数据库中的)用户名与密码,作为用户成功登录。
数据库提供多种操作,但并非所有的用户都能执行所有操作。如果一个用户可以执行某项操作,则称该用户有执行该操作的权限。权限通常需要一个路径来限定其生效范围,可以使用路径模式灵活管理权限。
角色是若干权限的集合,并且有一个唯一的角色名作为标识符。角色通常和一个现实身份相对应(例如交通调度员),而一个现实身份可能对应着多个用户。这些具有相同现实身份的用户往往具有相同的一些权限,角色就是为了能对这样的权限进行统一的管理的抽象。
安装初始化后的 IoTDB 中有一个默认用户:root,默认密码为root。该用户为管理员用户,固定拥有所有权限,无法被赋予、撤销权限,也无法被删除,数据库内仅有一个管理员用户。
一个新创建的用户或角色不具备任何权限。
拥有 MANAGE_USER、MANAGE_ROLE 的用户或者管理员可以创建用户或者角色,需要满足以下约束:
4~32个字符,支持使用英文大小写字母、数字、特殊字符(!@#$%^&*()_+-=)
用户无法创建和管理员用户同名的用户。
4~32个字符,可使用大写小写字母、数字、特殊字符(!@#$%^&*()_+-=),密码默认采用 SHA-256 进行加密。
4~32个字符,支持使用英文大小写字母、数字、特殊字符(!@#$%^&*()_+-=)
用户无法创建和管理员用户同名的角色。
IoTDB 主要有两类权限:序列权限、全局权限。
序列权限约束了用户访问数据的范围与方式,支持对绝对路径与前缀匹配路径授权,可对timeseries 粒度生效。
下表描述了这类权限的种类与范围:
| 权限名称 | 描述 |
|---|---|
| READ_DATA | 允许读取授权路径下的序列数据。 |
| WRITE_DATA | 允许读取授权路径下的序列数据。 允许插入、删除授权路径下的的序列数据。 允许在授权路径下导入、加载数据,在导入数据时,需要拥有对应路径的 WRITE_DATA 权限,在自动创建数据库与序列时,需要有 MANAGE_DATABASE 与 WRITE_SCHEMA 权限。 |
| READ_SCHEMA | 允许获取授权路径下元数据树的详细信息: 包括:路径下的数据库、子路径、子节点、设备、序列、模版、视图等。 |
| WRITE_SCHEMA | 允许获取授权路径下元数据树的详细信息。 允许在授权路径下对序列、模版、视图等进行创建、删除、修改操作。 在创建或修改 view 的时候,会检查 view 路径的 WRITE_SCHEMA 权限、数据源的 READ_SCHEMA 权限。 在对 view 进行查询、插入时,会检查 view 路径的 READ_DATA 权限、WRITE_DATA 权限。 允许在授权路径下设置、取消、查看TTL。 允许在授权路径下挂载或者接触挂载模板。 |
全局权限约束了用户使用的数据库功能、限制了用户执行改变系统状态与任务状态的命令,用户获得全局授权后,可对数据库进行管理。
下表描述了系统权限的种类:
| 权限名称 | 描述 |
|---|---|
| MANAGE_DATABASE | - 允许用户创建、删除数据库. |
| MANAGE_USER | - 允许用户创建、删除、修改、查看用户。 |
| MANAGE_ROLE | - 允许用户创建、删除、查看角色。 允许用户将角色授予给其他用户,或取消其他用户的角色。 |
| USE_TRIGGER | - 允许用户创建、删除、查看触发器。 与触发器的数据源权限检查相独立。 |
| USE_UDF | - 允许用户创建、删除、查看用户自定义函数。 与自定义函数的数据源权限检查相独立。 |
| USE_CQ | - 允许用户创建、开始、停止、删除、查看管道。 允许用户创建、删除、查看管道插件。 与管道的数据源权限检查相独立。 |
| USE_PIPE | - 允许用户注册、开始、停止、卸载、查询流处理任务。 - 允许用户注册、卸载、查询注册流处理任务插件。 |
| EXTEND_TEMPLATE | - 允许自动扩展模板。 |
| MAINTAIN | - 允许用户查询、取消查询。 允许用户查看变量。 允许用户查看集群状态。 |
| USE_MODEL | - 允许用户创建、删除、查询深度学习模型 |
关于模板权限:
在 IoTDB 中,用户可以由三种途径获得权限:
取消用户的权限,可以由以下几种途径:
集团1.公司1.**的读权限时制定了 grant option 关键字,那么 A 可以将集团1.公司1以下的任意节点、序列的读权限转授给他人, 同样也可以取消其他用户 集团1.公司1 下任意节点的读权限。集团1.公司1.工厂1 的读权限, 在取消 集团1.公司1.** 的读权限时,会清除用户A 的 集团1.公司1.工厂1 的读权限。用户权限主要由三部分组成:权限生效范围(路径), 权限类型, with grant option 标记:
userTest1 :
root.t1.** - read_schema, read_data - with grant option
root.** - write_schema, write_data - with grant option
每个用户都有一个这样的权限访问列表,标识他们获得的所有权限,可以通过 LIST PRIVILEGES OF USER <username> 查看他们的权限。
在对一个路径进行鉴权时,数据库会进行路径与权限的匹配。例如检查 root.t1.t2 的 read_schema 权限时,首先会与权限访问列表的 root.t1.**进行匹配,匹配成功,则检查该路径是否包含待鉴权的权限,否则继续下一条路径-权限的匹配,直到匹配成功或者匹配结束。
在进行多路径鉴权时,对于多路径查询任务,数据库只会将有权限的数据呈现出来,无权限的数据不会包含在结果中;对于多路径写入任务,数据库要求必须所有的目标序列都获得了对应的权限,才能进行写入。
请注意,下面的操作需要检查多重权限
IoTDB 提供了组合权限,方便用户授权:
| 权限名称 | 权限范围 |
|---|---|
| ALL | 所有权限 |
| READ | READ_SCHEMA、READ_DATA |
| WRITE | WRITE_SCHEMA、WRITE_DATA |
组合权限并不是一种具体的权限,而是一种简写方式,与直接书写对应的权限名称没有差异。
下面将通过一系列具体的用例展示权限语句的用法,非管理员执行下列语句需要提前获取权限,所需的权限标记在操作描述后。
CREATE USER <userName> <password> eg: CREATE USER user1 'passwd'
DROP USER <userName> eg: DROP USER user1
CREATE ROLE <roleName> eg: CREATE ROLE role1
DROP ROLE <roleName> eg: DROP ROLE role1
GRANT ROLE <ROLENAME> TO <USERNAME> eg: GRANT ROLE admin TO user1
REVOKE ROLE <ROLENAME> FROM <USER> eg: REVOKE ROLE admin FROM user1
LIST USER
LIST ROLE
LIST USER OF ROLE <roleName> eg: LIST USER OF ROLE roleuser
用户可以列出自己的角色,但列出其他用户的角色需要拥有 MANAGE_ROLE 权限。
LIST ROLE OF USER <username> eg: LIST ROLE OF USER tempuser
用户可以列出自己的权限信息,但列出其他用户的权限需要拥有 MANAGE_USER 权限。
LIST PRIVILEGES OF USER <username>; eg: LIST PRIVILEGES OF USER tempuser;
用户可以列出自己具有的角色的权限信息,列出其他角色的权限需要有 MANAGE_ROLE 权限。
LIST PRIVILEGES OF ROLE <roleName>; eg: LIST PRIVILEGES OF ROLE actor;
用户可以修改自己的密码,但修改其他用户密码需要具备MANAGE_USER 权限。
ALTER USER <username> SET PASSWORD <password>; eg: ALTER USER tempuser SET PASSWORD 'newpwd';
用户使用授权语句对赋予其他用户权限,语法如下:
GRANT <PRIVILEGES> ON <PATHS> TO ROLE/USER <NAME> [WITH GRANT OPTION]; eg: GRANT READ ON root.** TO ROLE role1; eg: GRANT READ_DATA, WRITE_DATA ON root.t1.** TO USER user1; eg: GRANT READ_DATA, WRITE_DATA ON root.t1.**,root.t2.** TO USER user1; eg: GRANT MANAGE_ROLE ON root.** TO USER user1 WITH GRANT OPTION; eg: GRANT ALL ON root.** TO USER user1 WITH GRANT OPTION;
用户使用取消授权语句可以将其他的权限取消,语法如下:
REVOKE <PRIVILEGES> ON <PATHS> FROM ROLE/USER <NAME>; eg: REVOKE READ ON root.** FROM ROLE role1; eg: REVOKE READ_DATA, WRITE_DATA ON root.t1.** FROM USER user1; eg: REVOKE READ_DATA, WRITE_DATA ON root.t1.**, root.t2.** FROM USER user1; eg: REVOKE MANAGE_ROLE ON root.** FROM USER user1; eg: REVOKE ALL ON ROOT.** FROM USER user1;
非管理员用户执行授权/取消授权语句时,需要对<PATHS> 有<PRIVILEGES> 权限,并且该权限是被标记带有 WITH GRANT OPTION 的。
在授予取消全局权限时,或者语句中包含全局权限时(ALL 展开会包含全局权限),须指定 path 为 root.**。 例如,以下授权/取消授权语句是合法的:
GRANT MANAGE_USER ON root.** TO USER user1; GRANT MANAGE_ROLE ON root.** TO ROLE role1 WITH GRANT OPTION; GRANT ALL ON root.** TO role role1 WITH GRANT OPTION; REVOKE MANAGE_USER ON root.** FROM USER user1; REVOKE MANAGE_ROLE ON root.** FROM ROLE role1; REVOKE ALL ON root.** FROM ROLE role1;
下面的语句是非法的:
GRANT READ, MANAGE_ROLE ON root.t1.** TO USER user1; GRANT ALL ON root.t1.t2 TO USER user1 WITH GRANT OPTION; REVOKE ALL ON root.t1.t2 FROM USER user1; REVOKE READ, MANAGE_ROLE ON root.t1.t2 FROM ROLE ROLE1;
<PATH> 必须为全路径或者以双通配符结尾的匹配路径,以下路径是合法的:
root.** root.t1.t2.** root.t1.t2.t3
以下的路径是非法的:
root.t1.* root.t1.**.t2 root.t1*.t2.t3
根据本文中描述的 样例数据 内容,IoTDB 的样例数据可能同时属于 ln, sgcc 等不同发电集团,不同的发电集团不希望其他发电集团获取自己的数据库数据,因此我们需要将不同的数据在集团层进行权限隔离。
使用 CREATE USER <userName> <password> 创建用户。例如,我们可以使用具有所有权限的root用户为 ln 和 sgcc 集团创建两个用户角色,名为 ln_write_user, sgcc_write_user,密码均为 write_pwd。建议使用反引号(`)包裹用户名。SQL 语句为:
CREATE USER `ln_write_user` 'write_pwd' CREATE USER `sgcc_write_user` 'write_pwd'
此时使用展示用户的 SQL 语句:
LIST USER
我们可以看到这两个已经被创建的用户,结果如下:
IoTDB> CREATE USER `ln_write_user` 'write_pwd' Msg: The statement is executed successfully. IoTDB> CREATE USER `sgcc_write_user` 'write_pwd' Msg: The statement is executed successfully. IoTDB> LIST USER; +---------------+ | user| +---------------+ | ln_write_user| | root| |sgcc_write_user| +---------------+ Total line number = 3 It costs 0.012s
此时,虽然两个用户已经创建,但是他们不具有任何权限,因此他们并不能对数据库进行操作,例如我们使用 ln_write_user 用户对数据库中的数据进行写入,SQL 语句为:
INSERT INTO root.ln.wf01.wt01(timestamp,status) values(1509465600000,true)
此时,系统不允许用户进行此操作,会提示错误:
IoTDB> INSERT INTO root.ln.wf01.wt01(timestamp,status) values(1509465600000,true) Msg: 803: No permissions for this operation, please add privilege WRITE_DATA on [root.ln.wf01.wt01.status]
现在,我们用 root 用户分别赋予他们向对应路径的写入权限.
我们使用 GRANT <PRIVILEGES> ON <PATHS> TO USER <username> 语句赋予用户权限,例如:
GRANT WRITE_DATA ON root.ln.** TO USER `ln_write_user` GRANT WRITE_DATA ON root.sgcc1.**, root.sgcc2.** TO USER `sgcc_write_user`
执行状态如下所示:
IoTDB> GRANT WRITE_DATA ON root.ln.** TO USER `ln_write_user` Msg: The statement is executed successfully. IoTDB> GRANT WRITE_DATA ON root.sgcc1.**, root.sgcc2.** TO USER `sgcc_write_user` Msg: The statement is executed successfully.
接着使用ln_write_user再尝试写入数据
IoTDB> INSERT INTO root.ln.wf01.wt01(timestamp, status) values(1509465600000, true) Msg: The statement is executed successfully.
授予用户权限后,我们可以使用 REVOKE <PRIVILEGES> ON <PATHS> FROM USER <USERNAME>来撤销已经授予用户的权限。例如,用root用户撤销ln_write_user和sgcc_write_user的权限:
REVOKE WRITE_DATA ON root.ln.** FROM USER `ln_write_user` REVOKE WRITE_DATA ON root.sgcc1.**, root.sgcc2.** FROM USER `sgcc_write_user`
执行状态如下所示:
IoTDB> REVOKE WRITE_DATA ON root.ln.** FROM USER `ln_write_user` Msg: The statement is executed successfully. IoTDB> REVOKE WRITE_DATA ON root.sgcc1.**, root.sgcc2.** FROM USER `sgcc_write_user` Msg: The statement is executed successfully.
撤销权限后,ln_write_user就没有向root.ln.**写入数据的权限了。
IoTDB> INSERT INTO root.ln.wf01.wt01(timestamp, status) values(1509465600000, true) Msg: 803: No permissions for this operation, please add privilege WRITE_DATA on [root.ln.wf01.wt01.status]
角色是权限的集合,而权限和角色都是用户的一种属性。即一个角色可以拥有若干权限。一个用户可以拥有若干角色与权限(称为用户自身权限)。
目前在 IoTDB 中并不存在相互冲突的权限,因此一个用户真正具有的权限是用户自身权限与其所有的角色的权限的并集。即要判定用户是否能执行某一项操作,就要看用户自身权限或用户的角色的所有权限中是否有一条允许了该操作。用户自身权限与其角色权限,他的多个角色的权限之间可能存在相同的权限,但这并不会产生影响。
需要注意的是:如果一个用户自身有某种权限(对应操作 A),而他的某个角色有相同的权限。那么如果仅从该用户撤销该权限无法达到禁止该用户执行操作 A 的目的,还需要从这个角色中也撤销对应的权限,或者从这个用户将该角色撤销。同样,如果仅从上述角色将权限撤销,也不能禁止该用户执行操作 A。
同时,对角色的修改会立即反映到所有拥有该角色的用户上,例如对角色增加某种权限将立即使所有拥有该角色的用户都拥有对应权限,删除某种权限也将使对应用户失去该权限(除非用户本身有该权限)。
在 1.3 版本前,权限类型较多,在这一版实现中,权限类型做了精简,并且添加了对权限路径的约束。
数据库 1.3 版本的权限路径必须为全路径或者以双通配符结尾的匹配路径,在系统升级时,会自动转换不合法的权限路径和权限类型。 路径上首个非法节点会被替换为**, 不在支持的权限类型也会映射到当前系统支持的权限上。
例如:
| 权限类型 | 权限路径 | 映射之后的权限类型 | 权限路径 |
|---|---|---|---|
| CREATE_DATBASE | root.db.t1.* | MANAGE_DATABASE | root.** |
| INSERT_TIMESERIES | root.db.t2.*.t3 | WRITE_DATA | root.db.t2.** |
| CREATE_TIMESERIES | root.db.t2*c.t3 | WRITE_SCHEMA | root.db.** |
| LIST_ROLE | root.** | (忽略) |
新旧版本的权限类型对照可以参照下面的表格(--IGNORE 表示新版本忽略该权限):
| 权限名称 | 是否路径相关 | 新权限名称 | 是否路径相关 |
|---|---|---|---|
| CREATE_DATABASE | 是 | MANAGE_DATABASE | 否 |
| INSERT_TIMESERIES | 是 | WRITE_DATA | 是 |
| UPDATE_TIMESERIES | 是 | WRITE_DATA | 是 |
| READ_TIMESERIES | 是 | READ_DATA | 是 |
| CREATE_TIMESERIES | 是 | WRITE_SCHEMA | 是 |
| DELETE_TIMESERIES | 是 | WRITE_SCHEMA | 是 |
| CREATE_USER | 否 | MANAGE_USER | 否 |
| DELETE_USER | 否 | MANAGE_USER | 否 |
| MODIFY_PASSWORD | 否 | -- IGNORE | |
| LIST_USER | 否 | -- IGNORE | |
| GRANT_USER_PRIVILEGE | 否 | -- IGNORE | |
| REVOKE_USER_PRIVILEGE | 否 | -- IGNORE | |
| GRANT_USER_ROLE | 否 | MANAGE_ROLE | 否 |
| REVOKE_USER_ROLE | 否 | MANAGE_ROLE | 否 |
| CREATE_ROLE | 否 | MANAGE_ROLE | 否 |
| DELETE_ROLE | 否 | MANAGE_ROLE | 否 |
| LIST_ROLE | 否 | -- IGNORE | |
| GRANT_ROLE_PRIVILEGE | 否 | -- IGNORE | |
| REVOKE_ROLE_PRIVILEGE | 否 | -- IGNORE | |
| CREATE_FUNCTION | 否 | USE_UDF | 否 |
| DROP_FUNCTION | 否 | USE_UDF | 否 |
| CREATE_TRIGGER | 是 | USE_TRIGGER | 否 |
| DROP_TRIGGER | 是 | USE_TRIGGER | 否 |
| START_TRIGGER | 是 | USE_TRIGGER | 否 |
| STOP_TRIGGER | 是 | USE_TRIGGER | 否 |
| CREATE_CONTINUOUS_QUERY | 否 | USE_CQ | 否 |
| DROP_CONTINUOUS_QUERY | 否 | USE_CQ | 否 |
| ALL | 否 | All privilegs | |
| DELETE_DATABASE | 是 | MANAGE_DATABASE | 否 |
| ALTER_TIMESERIES | 是 | WRITE_SCHEMA | 是 |
| UPDATE_TEMPLATE | 否 | -- IGNORE | |
| READ_TEMPLATE | 否 | -- IGNORE | |
| APPLY_TEMPLATE | 是 | WRITE_SCHEMA | 是 |
| READ_TEMPLATE_APPLICATION | 否 | -- IGNORE | |
| SHOW_CONTINUOUS_QUERIES | 否 | -- IGNORE | |
| CREATE_PIPEPLUGIN | 否 | USE_PIPE | 否 |
| DROP_PIPEPLUGINS | 否 | USE_PIPE | 否 |
| SHOW_PIPEPLUGINS | 否 | -- IGNORE | |
| CREATE_PIPE | 否 | USE_PIPE | 否 |
| START_PIPE | 否 | USE_PIPE | 否 |
| STOP_PIPE | 否 | USE_PIPE | 否 |
| DROP_PIPE | 否 | USE_PIPE | 否 |
| SHOW_PIPES | 否 | -- IGNORE | |
| CREATE_VIEW | 是 | WRITE_SCHEMA | 是 |
| ALTER_VIEW | 是 | WRITE_SCHEMA | 是 |
| RENAME_VIEW | 是 | WRITE_SCHEMA | 是 |
| DELETE_VIEW | 是 | WRITE_SCHEMA | 是 |