知识库

创建和配置本地数据库角色

Neo4j 4.0 引入了先进的安全特性,即基于角色的访问控制,这在引入多数据库功能时尤为必要。

管理员可以在系统数据库上访问这些控制,系统数据库存在于每个 Neo4j 实例中。

系统数据库附带多个内置角色,但尚不完全清楚如何将此类权限授予受限于特定数据库的用户。

本文旨在解释这些角色的全局性质,并提供一些示例,说明如何在本地数据库级别实现这些内置角色的等效功能。

内置角色是全局的,适用于所有数据库

一个常见但错误的假设是角色可以在某个数据库上分配给用户,例如在数据库 db1 上将读取角色分配给 user_a。但角色与其适用的数据库并非相互独立。每个角色不仅包含其权限,还包含它能访问的数据库。权限本身也可以限定于特定数据库。

因此,内置角色适用的数据库已预先设定且不可更改:它们包含全局数据库访问权限,其权限适用于所有数据库,无论过去、现在还是将来。被授予读取角色的用户将在该 DBMS 上创建的每个数据库中都拥有读取权限。

其他内置角色也是如此。它们并非用于本地数据库级别。

在涉及按数据库访问和角色时,最好从权限角度思考,而不是内置角色。因此不要考虑在数据库 db1 上将读取角色授予 user_a,而是应考虑创建一个在 db1 上拥有读取权限和访问权限的新角色,并将该角色授予 user_a。

我们可以复制内置角色并调整其数据库访问权限

我们可以不必从头创建新角色,而是使用内置角色作为模板,当我们只需要将内置角色的等效功能限定到特定的一个或多个数据库时。

我们通过复制现有角色来创建新角色。然后我们可以撤销全局数据库访问权限,再授予该角色应访问的数据库或多个数据库的访问权限。

因此,要在数据库 db1 上创建等同于读取角色的角色并授予 user_a,我们可以在系统数据库中执行以下操作:

CREATE ROLE db1_reader AS COPY OF reader;
REVOKE GRANT ACCESS ON DATABASES * FROM db1_reader;
GRANT ACCESS ON DATABASE db1 TO db1_reader;
GRANT ROLE db1_reader TO user_a;

当然,user_a 和数据库 db1 必须先存在,才能成功执行。

这授予了等同于读取角色的权限,但将用户的访问限制在仅数据库 db1。

请注意,多个角色的权限可能会组合

需要注意的是,db1_reader 角色并未真正将读取权限限制在仅 db1。它仍然拥有对所有数据库的完整读取权限(从内置读取角色复制而来),只是目前该角色仅允许访问 db1。我们仅撤销了对所有数据库的 ACCESS 权限,而不是 READ 权限。

如果用户获得了另一个在不同数据库上授予访问权限且未对其权限进行约束的角色,则 db1_reader 角色固有的全局读取权限将使其能够在新数据库中拥有读取权限。

让我们通过添加新角色 db2_accessor 并将其授予 user_a 来说明这一点。

CREATE ROLE db2_accessor;
GRANT ACCESS ON DATABASE db2 TO db2_accessor;
GRANT ROLE db2_accessor TO user_a;

即使 db2_accessor 角色仅提供对数据库的访问权限,没有读取、写入或其他任何权限,但由于来自 db1_reader 的全局读取权限,user_a 可以读取 db2 上的所有内容。

让我们检查 user_a 的权限以验证

SHOW USER user_a PRIVILEGES;
╒═════════╤══════════╤════════════════╤═══════╤═════════════════╤══════════════╤════════╕
│"access" │"action"  │"resource"      │"graph"│"segment"        │"role"        │"user"  │
╞═════════╪══════════╪════════════════╪═══════╪═════════════════╪══════════════╪════════╡
│"GRANTED"│"read"    │"all_properties"│"*"    │"NODE(*)"        │"db1_reader"  │"user_a"│
├─────────┼──────────┼────────────────┼───────┼─────────────────┼──────────────┼────────┤
│"GRANTED"│"traverse"│"graph"         │"*"    │"NODE(*)"        │"db1_reader"  │"user_a"│
├─────────┼──────────┼────────────────┼───────┼─────────────────┼──────────────┼────────┤
│"GRANTED"│"read"    │"all_properties"│"*"    │"RELATIONSHIP(*)"│"db1_reader"  │"user_a"│
├─────────┼──────────┼────────────────┼───────┼─────────────────┼──────────────┼────────┤
│"GRANTED"│"traverse"│"graph"         │"*"    │"RELATIONSHIP(*)"│"db1_reader"  │"user_a"│
├─────────┼──────────┼────────────────┼───────┼─────────────────┼──────────────┼────────┤
│"GRANTED"│"access"  │"database"      │"db1"  │"database"       │"db1_reader"  │"user_a"│
├─────────┼──────────┼────────────────┼───────┼─────────────────┼──────────────┼────────┤
│"GRANTED"│"access"  │"database"      │"db2"  │"database"       │"db2_accessor"│"user_a"│
└─────────┴──────────┴────────────────┴───────┴─────────────────┴──────────────┴────────┘

我们可以使用 DENY 来屏蔽权限

权限能够组合并不一定是障碍。有时这会非常有用。

例如,如果我们接受这种组合读取访问,但希望确保无论使用哪个数据库,该用户都不能读取或匹配 :Person 节点上的 SSN 属性。我们可以添加一个专门用于此限制的特殊角色。

CREATE ROLE ssn_blind;
DENY MATCH {ssn, SSN} ON GRAPH * NODES Person TO ssn_blind;
GRANT ROLE ssn_blind TO user_a;

将权限范围收窄可以保持权限的可预测性

如果我们想要最严格的安全级别,将权限范围设定为授予新角色(以及访问新数据库)时不会意外产生过宽的权限,那么在创建新角色时就必须放弃复制内置角色的做法。它们的权限是跨数据库的,这可能对我们的需求来说过于宽松。

相反,我们需要手动授予权限,并将其限定到相关的数据库或多个数据库。

让我们删除 db1_reader 角色并以更窄的权限范围重新创建它

CREATE OR REPLACE ROLE db1_reader;
GRANT ACCESS ON DATABASE db1 TO db1_reader;
GRANT MATCH {*} ON GRAPH db1 to db1_reader;
GRANT ROLE db1_reader TO user_a;

/docs/cypher-manual/current/access-control/manage-privileges/#access-control-graph-权限[MATCH 权限] 是 READ 和 TRAVERSE 权限的简写,因此可以为我们省去一行。

现在让我们再次检查 user_a 的权限

SHOW USER user_a PRIVILEGES;
╒═════════╤══════════╤════════════════╤═══════╤═════════════════╤══════════════╤════════╕
│"access" │"action"  │"resource"      │"graph"│"segment"        │"role"        │"user"  │
╞═════════╪══════════╪════════════════╪═══════╪═════════════════╪══════════════╪════════╡
│"GRANTED"│"read"    │"all_properties"│"db1"  │"NODE(*)"        │"db1_reader"  │"user_a"│
├─────────┼──────────┼────────────────┼───────┼─────────────────┼──────────────┼────────┤
│"GRANTED"│"traverse"│"graph"         │"db1"  │"NODE(*)"        │"db1_reader"  │"user_a"│
├─────────┼──────────┼────────────────┼───────┼─────────────────┼──────────────┼────────┤
│"GRANTED"│"read"    │"all_properties"│"db1"  │"RELATIONSHIP(*)"│"db1_reader"  │"user_a"│
├─────────┼──────────┼────────────────┼───────┼─────────────────┼──────────────┼────────┤
│"GRANTED"│"traverse"│"graph"         │"db1"  │"RELATIONSHIP(*)"│"db1_reader"  │"user_a"│
├─────────┼──────────┼────────────────┼───────┼─────────────────┼──────────────┼────────┤
│"GRANTED"│"access"  │"database"      │"db1"  │"database"       │"db1_reader"  │"user_a"│
├─────────┼──────────┼────────────────┼───────┼─────────────────┼──────────────┼────────┤
│"GRANTED"│"access"  │"database"      │"db2"  │"database"       │"db2_accessor"│"user_a"│
├─────────┼──────────┼────────────────┼───────┼─────────────────┼──────────────┼────────┤
│"DENIED" │"read"    │"property(SSN)" │"*"    │"NODE(Person)"   │"ssn_blind"   │"user_a"│
├─────────┼──────────┼────────────────┼───────┼─────────────────┼──────────────┼────────┤
│"DENIED" │"read"    │"property(ssn)" │"*"    │"NODE(Person)"   │"ssn_blind"   │"user_a"│
└─────────┴──────────┴────────────────┴───────┴─────────────────┴──────────────┴────────┘

我们可以看到,尽管 user_a 对 db1 和 db2 两个数据库都有访问权限,但我们授予 db1_reader 的读取和遍历权限仅限于 db1。User_a 可以访问 db2,但在没有为其现有角色或新角色授予更多权限之前,无法在 db2 上执行任何操作。

© . This site is unofficial and not affiliated with Neo4j, Inc.