GraphGists

图数据库用于权限和访问控制

授权和访问控制解决方案存储有关各方(例如,安全管理员)和资源(例如,业务用户和所有者)的信息,以及管理对这些资源访问的规则。然后,他们应用这些规则的控制系统来确定谁可以代表其他用户访问或操作一组操作。访问控制传统上是通过使用目录服务或通过在应用程序的后端构建自定义解决方案来实现的。不幸的是,在关系数据库上开发的分层目录结构随着数据集大小的增长而遭受连接痛苦,变得缓慢且无响应,最终提供糟糕的最终用户体验。

复杂性带来麻烦

使用像 Neo4j 这样的图数据库进行访问控制的关键好处是,引入高度风险的复杂需求会大大降低。当业务部门请求一组用于访问控制用例的需求时,负责交付系统的软件开发人员往往会遇到相当多的阻力。这种阻力就是我所说的软件工程项目的“复杂性妥协”。

Neo4j 解决复杂性问题

复杂性妥协是工程团队随着时间推移累积技术债务的关键因素之一。Neo4j 经过精心设计,旨在优雅地降低软件项目的复杂性。Neo4j 是一种成熟的数据库解决方案,沃尔玛、Glassdoor、思科系统、惠普、CrunchBase/AOL 等公司的工程团队都依赖于它,以及许多其他顶级企业。数十万个工程小时都投入到使 Neo4j 成为最成熟的 NoSQL 数据库,专门处理图状数据。

Neo4j 可以存储跨越数十亿个各方和资源的复杂且密集连接的访问控制结构。它提供了 SQL 数据库的熟悉性,但旨在处理您最复杂的用例和需求,这些用例和需求处理分层和非分层数据结构。

工程团队喜欢 Neo4j

在业务团队和产品经理的眼中,Neo4j 使软件开发人员、工程师和架构师看起来熟练、可靠且有价值,他们从软件解决方案的每个用例需求的实现中获益。不再需要妥协复杂性

Neo4j 对 SQL 开发人员来说很熟悉

通过一种类似 SQL 的查询语言,它提供了每秒遍历数百万个关系的能力,访问控制用例附带的复杂性可以简化为简单而优雅的解决方案。

本教程涵盖的内容

与网络管理和分析一样,图数据库访问控制解决方案允许自上而下和自下而上的查询。本教程将探讨如何使用 Neo4j 和 Cypher 来确定

  • 特定安全管理员可以管理哪些资源——公司结构、产品、服务、协议、帐户和最终用户?

  • 最终用户可以对哪些资源执行操作?

  • 给定特定资源,谁可以修改其访问设置或权限?

ACME 系统的访问控制

本教程说明了虚构公司 ACME Systems 的组织结构。在 ACME Systems 中,安全管理员被分配到一个或多个用户组,这些用户组通过下面描述的继承关系连接到一些而非全部公司。如果公司未连接到用户组,它将继承父公司的安全管理员权限。

每个公司都通过WORKS_FOR关系分配一个或多个业务用户,并且每个业务用户都通过HAS_ACCOUNT关系分配一个或多个帐户。

组-公司继承规则
  • ALLOWED_INHERIT 将安全管理员组连接到组织单元,允许该组内的安全管理员管理该组织单元。此权限由父组织单元的子级继承。

  • ALLOWED_DO_NOT_INHERIT 以一种允许该组内的管理员管理组织单元但不管理其任何子级的方式将安全管理员组连接到组织单元。

  • DENIED 禁止安全管理员访问组织单元。此权限由父组织单元的子级继承。DENIED优先于ALLOWED_INHERIT,但从属于ALLOWED_DO_NOT_INHERIT

the whole graph

细粒度或粗粒度关系?

请注意,ACME Systems 访问控制数据模型使用细粒度关系(ALLOWED_INHERITALLOWED_DO_NOT_INHERITDENIED),而不是由属性限定的一般粗粒度关系,例如名为PERMISSION的关系类型,该关系类型在该关系上具有allowedinherited属性。ACME Systems 对这两种方法都进行了性能测试,并确定细粒度、无属性的方法比使用关系属性的粗粒度方法快了近两倍

示例数据集

在此查询中,我们使用 Cypher 查询语言设置了示例数据集。我们将使用此数据集来回答我们之前提出的问题。

访问控制级别及其交互

尽管不太复杂,但此 GraphGist 具有许多相互关联的部分。在探索不同类型的访问控制时,让我们从简单的查询到复杂的查询逐步进行。

ALLOWED_INHERIT

同样,ALLOWED_INHERIT将管理员组连接到组织单元,从而允许该组内的管理员管理该组织单元。此权限由父组织单元的子级继承。

在 ACME Systems 中,安全管理员 Ben 可以管理 Skunkworks 和 Spinoff 两家公司的员工,这要归功于 Group1(Ben 是成员)与 Acme 之间以及 Group1 与 Startup 之间的ALLOWED_INHERIT关系。

MATCH paths=(admin:administrator {name:'Ben'})-[:MEMBER_OF]->()-[:ALLOWED_INHERIT]->(c1:company)<-[:CHILD_OF*0..3]-(c2:company)<-[:WORKS_FOR]-(employee)-[:HAS_ACCOUNT]->(account)
RETURN admin.name AS Admin, c1.name AS `Parent Company`, c2.name AS `Child Company`, employee.name AS Employee

ALLOWED_DO_NOT_INHERIT

同样,ALLOWED_DO_NOT_INHERIT以一种允许该组内的管理员管理组织单元但不管理其任何子级的方式将管理员组连接到组织单元。Sarah 作为 Group 2 的成员,可以管理 Acme,但不能管理其子级 Spinoff,因为 Group 2 通过ALLOWED_DO_NOT_INHERIT关系而不是ALLOWED_INHERIT关系连接到 Acme。

此查询探讨了由于ALLOWED_DO_NOT_INHERIT关系,管理员 Sarah 不允许管理哪些用户

MATCH paths=(admin:administrator {name:'Sarah'})-[:MEMBER_OF]->()-[:ALLOWED_DO_NOT_INHERIT]->(c1:company)<-[:CHILD_OF*1..3]-(c2:company)<-[:WORKS_FOR]-(employee)-[:HAS_ACCOUNT]->(account)
RETURN admin.name AS Admin, c1.name AS `Parent Company`, c2.name AS `Child Company`, employee.name AS Employee

Note Sarah doesn’t manage Emily

DENIED

同样,DENIED禁止管理员访问组织单元。此权限由父组织单元的子级继承。在 ACME Systems 中,这最好通过管理员 Liz 及其相对于 Big Co、Acquired Ltd、Subsidiary 和 One-Map Shop 的权限来说明。

让我们看看 Liz没有DENIED限制的情况

MATCH paths=(admin:administrator { name:'Liz' })-[:MEMBER_OF]->()-[:ALLOWED_INHERIT]->(:company)<-[:CHILD_OF*0..3]-(:company)<-[:WORKS_FOR]-(employee)-[:HAS_ACCOUNT]->(account)
RETURN paths

让我们看看 LizDENIED限制的情况

MATCH paths=(admin:administrator { name:'Liz' })-[:MEMBER_OF]->()-[:ALLOWED_INHERIT]->(:company)<-[:CHILD_OF*0..3]-(c:company)<-[:WORKS_FOR]-(employee)-[:HAS_ACCOUNT]->(account)
WHERE NOT ((admin)-[:MEMBER_OF]->()-[:DENIED]->()<-[:CHILD_OF*0..3]-(c))
RETURN paths

由于她属于 Group 4 并在 Big Co 上拥有ALLOWED_INHERIT权限,Liz 可以管理 Big Co。但是,尽管这是一种可继承的关系,但 Liz 无法管理 Acquired Ltd 或 Subsidiary。Liz 所属的 Group 5 被DENIED访问 Acquired Ltd 及其子级(包括 Subsidiary)。

但是,Liz 可以管理 One-Map Shop,这要归功于授予 Group 6(Liz 属于的最后一个组)的ALLOWED_DO_NOT_INHERIT权限。

让我们再次查看该查询,这次添加ALLOWED_DO_NOT_INHERIT

MATCH paths=(admin:administrator {name:'Liz'})-[:MEMBER_OF]->()-[:ALLOWED_INHERIT]->()<-[:CHILD_OF*0..3]-(c:company)<-[:WORKS_FOR]-(employee)-[:HAS_ACCOUNT]->(account)
WHERE NOT ((admin)-[:MEMBER_OF]->()-[:DENIED]->()<-[:CHILD_OF*0..3]-(c))
RETURN paths
UNION
MATCH paths=(admin:administrator {name:'Liz'})-[:MEMBER_OF]->()-[:ALLOWED_DO_NOT_INHERIT]->()<-[:WORKS_FOR]-(employee)-[:HAS_ACCOUNT]->(account)
RETURN paths

回顾一下,DENIED 的优先级高于 ALLOWED_INHERIT,但低于 ALLOWED_DO_NOT_INHERIT。因此,如果管理员通过 ALLOWED_DO_NOT_INHERITDENIED 连接到公司,则 ALLOWED_DO_NOT_INHERIT 优先。

注意:Cypher 支持 UNIONUNION ALL 运算符。UNION 从最终结果集中消除重复结果,而 UNION ALL 包含所有重复项。

查找管理员可以访问的所有资源

让我们朝着图数据库管理员在内省或探索数据库时可能看到的方面迈进一步。每当现场管理员登录系统时,都会显示一个基于浏览器的列表,其中包含他可以管理的所有员工和员工帐户。

让我们看看任何管理员可以访问的所有资源

MATCH paths=(admin:administrator)-[:MEMBER_OF]->()-[:ALLOWED_INHERIT]->()<-[:CHILD_OF*0..3]-(company)<-[:WORKS_FOR]-(employee)-[:HAS_ACCOUNT]->(account)
WHERE NOT ((admin)-[:MEMBER_OF]->()-[:DENIED]->()<-[:CHILD_OF*0..3]-(company))
RETURN admin.name AS Admin, employee.name AS Employee, collect(account.name) AS Accounts
ORDER BY Admin ASC
UNION
MATCH paths=(admin)-[:MEMBER_OF]->()-[:ALLOWED_DO_NOT_INHERIT]->()<-[:WORKS_FOR]-(employee)-[:HAS_ACCOUNT]->(account)
RETURN admin.name AS Admin, employee.name AS Employee, collect(account.name) AS Accounts
ORDER BY Admin ASC

此查询匹配每个管理员可以访问的所有资源,并考虑了 ALLOWED_INHERITALLOWED_DO_NOT_INHERITDENIED 控制之间的交互。

确定管理员是否可以访问某个资源

我们刚刚查看的查询返回了管理员可以管理的员工和帐户列表。在 Web 应用程序中,每个这些资源(员工、帐户)都可以通过其自己的 URI 访问。给定一个友好的 URI(例如,http: //acme/accounts/ 5436),有什么可以阻止管理员意外更改未授权的帐户?

需要一个查询来确定管理员是否可以访问特定资源

MATCH p=(admin:administrator)-[:MEMBER_OF]->()-[:ALLOWED_INHERIT]->()<-[:CHILD_OF*0..3]-(company:company)
WHERE NOT ((admin)-[:MEMBER_OF]->()-[:DENIED]->()<-[:CHILD_OF*0..3]-(company))
RETURN admin.name AS Admin, collect(company.name) AS Resource
UNION
MATCH p=(admin)-[:MEMBER_OF]->()-[:ALLOWED_DO_NOT_INHERIT]->(company)
RETURN admin.name AS Admin, collect(company.name) AS Resource

查找帐户的管理员

前两个查询表示图的“自顶向下”视图。在本教程的最后一个查询中,我们将讨论数据的“自底向上”视图。给定一个资源——员工或帐户——谁可以管理它?

这是查询

MATCH p=(resource {name:'Acct 10'})-[:WORKS_FOR|HAS_ACCOUNT*1..2]-(company)-[:CHILD_OF*0..3]->()<-[:ALLOWED_INHERIT]-()<-[:MEMBER_OF]-(admin)
WHERE NOT ((admin)-[:MEMBER_OF]->()-[:DENIED]->()<-[:CHILD_OF*0..3]-(company))
RETURN resource.name AS Resource, collect(admin.name) AS Admins
UNION
MATCH p=(resource {name:'Acct 10'})-[:WORKS_FOR|HAS_ACCOUNT*1..2]-(company)<-[:ALLOWED_DO_NOT_INHERIT]-()<-[:MEMBER_OF]-(admin)
RETURN resource.name AS Resource, collect(admin.name) AS Admins

该查询类似于前两个自顶向下的查询,但反过来。请注意 Cypher 如何使用 OR 管道来选择员工或帐户资源。

Exploring Account 10

总结

在 Neo4j 中建模资源图非常自然,因为正在建模的域本质上是一个图。Neo4j 提供快速安全的访问并回答重要问题,例如

  • 用户可以访问哪些订阅?用户是否可以访问给定的资源?客户参与了哪些协议?

这些操作的速度和准确性非常关键,因为用户登录系统后无法继续,直到授权计算完成。

Neo4j 为密集连接的权限树提供了亚秒级查询的可能性,从而提高了系统的性能特征。此外,Neo4j 允许在图中忠实地重现客户的结构和内容层次结构,无需修改,从而消除了专门针对特定应用程序的数据复制和反规范化。通过不必专门针对特定应用程序的性能需求来专门化数据,Neo4j 为在其他应用程序中扩展和重用客户图提供了基础。

由 Neo4j 社区提供

本教程由 Neo4j 社区和大家庭中一些最热情的开发人员提供。如果您发现它有用,请与您的开发人员同事分享,以便他们也能从中受益。

参考文献

此 GraphGist 具有 Ian Robinson 和 Jim Webber 撰写的 O’Reily“图数据库”书籍中的内容。您可以在 https://www.graphdatabases.com/ 以电子书的形式免费获取。

有益的后续步骤

查看社区管理的开发者资源网站 https://www.neo4j.com/developer