授权

这是 GraphQL Library 7 版本的文档。对于长期支持 (LTS) 版本 5,请参考 GraphQL Library 5 LTS 版本

授权规则涵盖了生成的 Cypher 查询允许访问的具体数据。它们使用谓词来评估由 GraphQL 查询生成的 Cypher 所访问的数据,从而在节点及其属性的上下文中允许或禁止执行。

所有授权规则都隐含了对身份验证的要求,因为这些规则通常是根据 JWT 载荷中的值进行评估的。

在使用 @authentication 指令配置的显式身份验证情况下,它仅在 Cypher 转换时进行评估。如果查询需要身份验证但请求未经过身份验证,则该请求永远不会到达数据库。

@authorization 指令不适用于订阅,它仅适用于查询(Queries)和变更(Mutations)。如果您打算在 API 中使用订阅并希望保护这些事件,请改用 @subscriptionsAuthorization 来配置订阅的授权。

过滤规则

过滤规则会过滤掉用户无权访问的数据,且不会抛出任何错误。这些规则被转换为过滤谓词,并根据数据库中匹配到的数据进行评估。

此类规则的过滤器工作方式与查询和聚合的过滤器完全相同。请参阅 过滤 (Filtering)

过滤规则不仅保护数据,还向未经授权的用户掩盖了该数据是否存在的信息。

例如,以下是如何过滤掉不属于当前 UserPost 节点:

type User @node {
    id: ID!
}

type Post @node @authorization(filter: [
    { where: { node: { authors: { some: { id: { eq: "$jwt.sub" } } } } } }
]) {
    title: String!
    content: String!
    authors: [User!]! @relationship(type: "AUTHORED", direction: IN)
}

操作

可以配置过滤仅在某些操作上执行

  • 读取 (READ)

  • AGGREGATE(聚合)

  • UPDATE(更新)

  • DELETE

  • CREATE_RELATIONSHIP(创建关系)

  • DELETE_RELATIONSHIP(删除关系)

例如,仅对读取和聚合文章(post)要求过滤:

type Post @node @authorization(filter: [
    { operations: [READ, AGGREGATE], where: { node: { author: { id: { eq: "$jwt.sub" } } } } }
]) {
    title: String!
    content: String!
    authors: [User!]! @relationship(type: "AUTHORED", direction: IN)
}

如果没有包含操作列表的 operations 参数,GraphQL 库会将授权配置视为已提供了完整的操作列表。

验证规则

如果针对用户无权访问的数据执行查询,验证规则会抛出错误。这些规则通过包含对 apoc.util.validatePredicate 调用的过滤谓词在数据库中进行评估。

例如,以下是如何在除用户本人或管理员之外的任何人访问 User 时抛出错误:

type JWT @jwt {
    roles: [String!]!
}

type User @node @authorization(validate: [
    { where: { node: { id: { eq: "$jwt.sub" } } } }
    { where: { jwt: { roles: { includes: "admin" } } } }
]) {
    id: ID!
}

操作

可以配置验证仅在某些操作上执行

  • 读取 (READ)

  • AGGREGATE(聚合)

  • CREATE

  • UPDATE(更新)

  • DELETE

  • CREATE_RELATIONSHIP(创建关系)

  • DELETE_RELATIONSHIP(删除关系)

例如,仅在更新或删除文章时要求验证:

type Post @node @authorization(validate: [
    {
        operations: [UPDATE, DELETE]
        where: { node: { authors: { some: { id: { eq: "$jwt.sub" } } } } }
    }
]) {
    title: String!
    content: String!
    authors: [User!]! @relationship(type: "AUTHORED", direction: IN)
}

如果没有包含操作列表的 operations 参数,GraphQL 库会将授权配置视为已提供了完整的操作列表。

何时执行 (When)

可以配置验证仅在操作执行之前或之后进行。这是通过使用 when 参数完成的,该参数接受以下值的数组:

  • BEFORE(之前)

  • AFTER(之后)

此外,某些操作仅支持在之前或之后进行验证,下表对此进行了总结:

操作 (operation) when

读取 (READ)

BEFORE(之前)

AGGREGATE(聚合)

BEFORE(之前)

CREATE

AFTER(之后)

UPDATE(更新)

BEFORE, AFTER

DELETE

BEFORE(之前)

CREATE_RELATIONSHIP(创建关系)

BEFORE, AFTER

DELETE_RELATIONSHIP(删除关系)

BEFORE, AFTER

例如,假设您希望允许某人更新文章。如果您想在更新后检查文章作者是否仍然是当前用户,请执行以下操作:

type Post @node @authorization(validate: [
    { operations: [UPDATE], when: [AFTER], where: { node: { author: { id: { eq: "$jwt.sub" } } } } }
]) {
    title: String!
    content: String!
    author: User! @relationship(type: "AUTHORED", direction: IN)
}

字段授权

@authorization 指令既可以用在对象类型上,也可以用在它们的字段上,本页之前的示例大部分使用的是前者。当应用于字段时,授权规则仅在对该字段执行匹配操作时才会被评估。例如,考虑一个带有 password 字段的 User 类型:

type User @node {
    id: ID!
    username: String!
    password: String! @authorization(validate: [{ operations: [READ, UPDATE], where: { node: { id: { eq: "$jwt.sub" } } } }])
}

执行以下查询时,不需要有效的身份:

{
    users {
        username
    }
}

然而,考虑以下查询:

{
    users {
        username
        password
    }
}

这将要求请求中提供有效的 JWT,并且匹配的用户将根据 JWT 主题(subject)进行过滤。这也适用于尝试更新 password 字段的情况,更新将仅应用于与 JWT 匹配的用户。

无身份验证的授权

默认情况下,每个授权检查都隐式要求身份验证,但可以针对每个规则禁用此要求。例如,当节点具有标记该节点是否应公开的属性时,可能就是这种情况。

例如,如果某些 Post 节点是私有的且属于特定的 User,而其他 Post 节点是公开的且任何用户均可读取,以下是设置方法:

type User @node {
    id: ID!
}

type Post @node @authorization(filter: [
    {
        requireAuthentication: false,
        operations: [READ],
        where: { node: { public: { eq: true } } }
    }
]) {
    title: String!
    content: String!
    public: Boolean!
    authors: [User!]! @relationship(type: "AUTHORED", direction: IN)
}

规则排序

在每个规则集(filtervalidate)中,规则通过 OR 连接。这两个规则集之间通过 AND 连接。

例如,以下规则允许在 JWT 角色声明包含 admin 或者节点上的 locked 属性为 false 时更新 User 节点:

type User @node @authorization(validate: [
    { operations: [UPDATE], where: { jwt: { roles: { includes: "admin" } } } }
    { operations: [UPDATE], where: { node: { locked: { eq: false } } } }
]) {
    id: ID!
    locked: Boolean!
}

如果您想结合“用户必须是管理员”和“locked 属性必须为 false”这两个规则来更新 User 节点,请在单个规则中使用 AND 将它们都添加到 where 字段中:

type User @node @authorization(validate: [
    { operations: [UPDATE], where: { AND: [{ jwt: { roles: { includes: "admin" } } }, { node: { locked: {eq: false} } }] } }
]) {
    id: ID!
    locked: Boolean!
}