细粒度图数据访问控制组件,数据库管理员通过配置
特殊角色
账户的形式实现,支持在线自定义配置
进行热更新
权限操作,不用重启数据库服务。读写权限的控制粒度可以细化到标签、关系、属性,并且区分不同的用户对数据删除权限进行严格管控。
主要实现
细粒度读写
和细粒度读
权限的配置。细粒度写
可以控制用户只能操作某些标签、关系以及属性,是否可以新建与修改属性、标签和关系,取决于权限的细粒度配置。细粒度读
可以限制用户只能执行某些数据的查询,并支持指定具体的Query,相当于为用户指定了一个Query白名单。
图数据库基于角色的访问控制体系,在使用时通过将不同角色组合,分配给同一用户多个角色,可以实现更丰富的访问控制功能。例如,某个用户具有部分子图的读取权限,但是也需要具备对某个标签的多个属性拥有修改、删除、创建的权限。
图数据库的主要角色权限类型:
编号 | 类型 | 说明 |
---|---|---|
1 | Reader | 只读 |
2 | Publisher | 读写数据 |
3 | Architect | 定义索引、限制等数据库模式对象 |
4 | Administrator | 管理用户和角色,管理数据库运行 |
5 | Editor | 可以读写但是无法新建标签和关系 |
6 | Define | 自定义角色 |
其中
ongdb-lab-security
组件主要是实现第六类Define
权限配置,例如要根据特定标签、关系和属性上的过滤条件对图的一部分子图实现访问控制,则可以通过ongdb-lab-security
组件实现。生成的配置文件在图数据库安装路径auth
目录下。
访问控制的基本思路是定义新用户和角色,为该角色指定只有运行某些扩展过程的权限,然后在扩展过程中根据调用过程的用户及其角色,应用不同的过滤条件,按照标签、关系类型、属性值对结果进行筛选。
这些用户因为只有运行特定过程的权利,因此无法通过Browser登陆数据库来运行Cypher查询,而只能通过应用客户端访问数据库。
在图数据库节点执行安装组件、修改配置、重启图数据库操作;如果是集群环境所有节点需要执行相同操作。
- 1.将
ongdb-lab-security-*.jar
放在plugins
文件夹 - 2.修改
conf
配置
// 为角色指定权限
dbms.security.procedures.roles=olab.security.publisher.*:publisher_proc;olab.security.reader*:reader_proc;olab.security.get*:publisher_proc,reader_proc;routing:publisher_proc,reader_proc;apoc.bolt.execute:publisher_proc,reader_proc;
- 3.重启图数据库
WITH ['reader_proc','publisher_proc'] AS roles
UNWIND roles AS role
CALL dbms.security.createRole(role) RETURN role;
// 如果需要自定义修改用户则修改`reader_users`和`publisher_users`再进行下一步操作即可
// 预定义特殊Reader用户
WITH ['reader-1','reader-2'] AS reader_users
// 预定义特殊Publisher用户
WITH ['publisher-1','publisher-2'] AS publisher_users,reader_users
WITH apoc.coll.union(reader_users,publisher_users) AS users
UNWIND users AS user
// 默认密码`abc%pro`,初次登陆时不用修改口令
// CALL dbms.security.deleteUser(user) RETURN user;
CALL dbms.security.createUser(user,'abc%pro',false) RETURN user;
- 为特殊Reader用户指定角色
WITH ['reader-1','reader-2'] AS reader_users
UNWIND reader_users AS user
// 为特殊Reader用户指定角色
CALL dbms.security.addRoleToUser('reader_proc',user) RETURN user;
- 特殊Publisher用户指定角色
WITH ['publisher-1','publisher-2'] AS publisher_users
UNWIND publisher_users AS user
// 为特殊Publisher用户指定角色
CALL dbms.security.addRoleToUser('publisher_proc',user) RETURN user;
Operator
参数定义了对标签、关系、属性的操作权限的级别定义,使用olab.security.setPublisher
过程为用户分配数据编辑权限。拥有创建指定数据的特殊权限账户,在创建数据时节点和关系会默认带有系统用户名字段__system_users
和字段用户归属标记字段__system_field_users
,存储为一个数组格式,这两个字段主要使用JSON字符串保存数据创建人的信息。在使用DELETER_RESTRICT
权限时,会根据该字段来判断用户是否对数据拥有删除权限。【该功能和READER_FORBID
操作类型一起增加到To Do List】
权限下发机制,当节点拥有一个
PUBLISHER
权限时,用户可以对已有数据进行编辑修改并且可以创建新数据,对所属属性都可以执行PUBLISHER
操作,但是无法删除。如果这时对节点的属性指定了一个DELETER_RESTRICT
权限,那么用户则可以对自己创建的属性执行删除操作。如果对节点的属性指定了一个DELETER
权限,则当前用户可以删除任何其它用户创建的属性数据。
需要注意的是,使用
olab.security.setPublisher
或olab.security.setReader
给原有用户增加权限时可以直接进行设置新权限,权限系统会自动进行追加。
操作级别 | 类型 | 说明 |
---|---|---|
1 | READER_FORBID | 禁止读取 |
2 | READER | 可以读取 |
3 | EDITOR | 对已有数据的编辑修改权限 【需要注意的是当用户对标签或关系类型具有该权限时表示也可以新增删除标签或关系类型】 【属性设置为该权限时只可修改属性值】 |
4 | PUBLISHER | 对已有数据的编辑修改权限、拥有创建新数据权限 |
5 | DELETER_RESTRICT | 对已有数据的编辑修改权限、拥有创建新数据权限、拥有删除数据权限 【仅允许删除用户自己创建的数据】 【当属性设置为该权限时,如果用户对节点或关系存在大于该级别的权限则属性也可以执行一样的权限级别】 |
6 | DELETER | 对已有数据的编辑修改权限、拥有创建新数据权限、拥有删除数据权限 |
- 为
Publisher-1
配置权限
说明:
- 配置Publisher权限【合并权限列表】【admin】
- 对于
properties
配置字段值检查check
时可以使用olab.security.getValueTypes
查看可配置的值检查类型- properties不为空时表示对可操作属性进行限制,为空时表示没有配置属性权限
- 对属性值进行检查:为用户设置权限时,对于值的类型也设置了
check
操作,在这里检查用户输入的值类型是否满足管理员限定的要求- 对值也可以限制:设置权限时可设置
invalid_values
参数,表示对值进行验证,如果值包含在这个列表中则提示错误,通常使用在限制用户输入错误的值或限制用户不能设置特定的属性值constraint
参数支持对属性增加EXISTS
限制,表示该属性必须存在,不需要加限制则保留空字符串即可
olab.security.setPublisher
入参:
- @param username:用户名
- @param nodeLabels:可操作的标签列表【可为空】【追加】 label properties[<field,operator>] operator
- @param relTypes:可操作的关系类型列表【可为空】【追加】 start_label type end_label properties[<field,operator[]>] operator
CALL olab.security.setPublisher('publisher-1',
[
{
label:'Person',
invalid_values:['人物'],
properties:[
{
field:'name',
operator:'DELETER_RESTRICT',
check:'STRING',
invalid_values:['001',''],
constraint:''
}
],
operator:'EDITOR'
}
,
{
label:'Movie',
invalid_values:['电影'],
properties:[
{
field:'name',
operator:'DELETER_RESTRICT',
check:'STRING',
invalid_values:['001',''],
constraint:'EXISTS'
}
],
operator:'EDITOR'
}
],
[
{
start_label:'Person',
type:'ACTED_IN',
invalid_values:['参演'],
end_label:'Movie',
operator:'DELETER_RESTRICT',
properties:[
{
field:'date',
operator:'PUBLISHER',
check:'INTEGER',
invalid_values:['2021',''],
constraint:'EXISTS'
}
]
}
]
) YIELD username,currentRole,nodeLabels,relTypes RETURN username,currentRole,nodeLabels,relTypes
- 为
Reader-1
配置权限
// 配置Reader权限【合并权限列表】【admin】
// @param username:用户名
// @param queries:可执行查询列表 query_id query【可为空】【追加】【query_id不可重复】【Query需要返回属性键值格式,不支持直接返回节点和关系】
// 配置时可加入一个`description`字段对查询含义进行描述,方便使用者理解;该参数为可选参数
CALL olab.security.setReader('reader-1',
[
{
query_id:'query001',
query:'MATCH (n) RETURN n.name AS name LIMIT 10'
}
,
{
query_id:'query002',
query:'MATCH (n) WITH n LIMIT 10 RETURN olab.result.transfer(n) AS mapList;',
description:'查询任意十个节点并输出Table'
}
,
{
query_id:'query003',
query:'MATCH ()-[r]->() WITH r LIMIT 10 WITH olab.result.transfer(r) AS mapList UNWIND mapList AS map RETURN map;'
}
,
{
query_id:'query004',
query:'MATCH (tom {name:$name}) RETURN tom.name AS name,tom.born AS born;'
}
]
) YIELD username,currentRole,queries RETURN username,currentRole,queries
- 获取指定用户的权限列表
CALL olab.security.fetchUserAuth('reader-1') YIELD value RETURN value
- 重置指定用户的权限列表
// @param username:用户名
CALL olab.security.clear('reader-1') YIELD username,currentRole RETURN username,currentRole
- 获取所有的已配置权限列表
CALL olab.security.list() YIELD value RETURN value
以
publisher-1
用户为例
- 1.查看拥有的权限
CALL olab.security.get() YIELD value RETURN value
// 对照权限定义
CALL olab.security.getAuth() YIELD operate,level,description RETURN operate,level,description
- 2.合并节点
CALL olab.security.publisher.merge.node({label},{merge_field},{merge_value},{[other_pros]}) YIELD value RETURN value
- 3.删除节点
CALL olab.security.publisher.delete.node({node_id}) YIELD value RETURN value
- 4.合并关系
CALL olab.security.publisher.merge.relationship({start_id},{end_id},{merge_rel_type},{rel_pros}) YIELD value RETURN value
- 5.删除关系
CALL olab.security.publisher.delete.relationship({rel_id}) YIELD value RETURN value
- 6.修改节点的属性值
CALL olab.security.publisher.update.node({node_id},{field_name},{field_value}) YIELD value RETURN value
- 7.修改关系的属性值
CALL olab.security.publisher.update.relationship({rel_id},{field_name},{field_value}) YIELD value RETURN value
- 8.删除节点的属性键
CALL olab.security.publisher.remove.node.key({node_id},{field_name}) YIELD value RETURN value
- 9.删除节点的某个标签
CALL olab.security.publisher.remove.node.label({node_id},{label}) YIELD value RETURN value
- 10.删除关系的属性键
CALL olab.security.publisher.remove.relationship.key({rel_id},{field_name}) YIELD value RETURN value
- 11.增加节点的标签
CALL olab.security.publisher.add.node.label({node_id},{label}) YIELD value RETURN value
以
reader-1
用户为例
- 1.查看拥有的权限
CALL olab.security.get() YIELD value RETURN value
- 2.执行查询
CALL olab.security.reader('query001',NULL) YIELD value RETURN value;
CALL olab.security.reader('query004',{name:'Tom Hanks'}) YIELD value RETURN value;
- 在单机情况下下列写入操作可以正常执行,但是在集群状态下只读节点上该查询是无法正常执行的
CALL olab.security.publisher.merge.node('Person','name','test',NULL) YIELD value RETURN value
- 集群状态下使用
routing
过程进行路由转发,并在auth
文件下配置security_routing.access
文件,并写入bolt协议映射的CORE节点的地址即可
// 配置好以后,在只读节点也可以运行该查询
CALL routing("publisher-1","abc%25pro","CALL olab.security.publisher.merge.node('Person','name','test',NULL) YIELD value RETURN value")