AWS Amplify 基于 db 字段值或关系的授权
Posted
技术标签:
【中文标题】AWS Amplify 基于 db 字段值或关系的授权【英文标题】:AWS Amplify authorization based on db field value or relationship 【发布时间】:2021-05-15 17:11:16 【问题描述】:我正在使用 Amplify (GraphQL api) 和 Quasar Framework 创建一个 web 应用程序。
使用 Amazon Cognito 进行身份验证。
假设数据库有这些实体: 拥有自己的个人资料的用户,他可以在其中管理自己的数据,如果他将“公共”布尔字段设置为 true,甚至可以将其公开。
拥有待办事项等的组织
用户可以成为一个(或多个)组织的员工,并且应该能够管理例如属于他成为员工的组织的待办事项。
我一直在弄清楚如何添加授权规则来实现这一点。
所有者授权应该适用于用户个人资料,但即便如此,如果用户在其个人资料中将“公共”布尔字段设置为 true,也不清楚如何设置使个人资料公开的规则。
例如:
type Todo @model @searchable @auth(rules: [allow: owner, operations: [read, create, update, delete]])
id: ID!
Title: String!
Description: String
这样,如果用户登录,他可以管理和列出自己的待办事项,但是我怎样才能让他查看和管理属于他是员工的组织的待办事项(员工将是一个连接表,它连接用户和组织)?
【问题讨论】:
【参考方案1】:我对这个问题进行了一些研究,虽然还远未完成,但我想分享一下。
-
尽管amplify docs 说,有可能组合多种自动化类型,但他们没有明确指定,你不能将它们组合在一个请求中(我是,这对他们来说很明显,但对于新手来说却不是我)。
当您使用
amplify update api
配置 AppSync GraphQL API 时,您选择默认授权类型。 await API.graphql()
从您前面发出的所有后续请求都将使用此默认值,除非您明确指定像 await API.graphql(Object.assign(graphqlOperation(listTodos),authMode: auth_mode))
这样的不同类型,其中 auth_mode
可以采用以下值之一:"API_KEY", "AWS_IAM", "AMAZON_COGNITO_USER_POOLS"
和 "OPENID_CONNECT"
。
在 Amplify 中有两种“真正的”公共授权类型 - "API_KEY"
和 "AWS_IAM"
。要激活其中任何一个(或两者),您应该执行以下操作:
type Todo @model
@auth(rules: [
allow: public ,
allow: public, provider: iam,
])
id: ID!
name: String!
description: String
owner: String
editors: [String]
entity: String
allow: public
代表"API_KEY"
, allow: public, provider: iam
- 代表"AWS_IAM"
。
要让"API_KEY"
正常工作,您应该配置 API KEY(例如 - 与 amplify update api
)。对于"AWS_IAM"
,您应该配置 Cognito 并在 Cognito 身份池中启用未经身份验证的身份。
此后,任何没有事先用户登录的表单await API.graphql(Object.assign(graphqlOperation(listTodos),authMode: auth_mode))
和auth_mode=API_KEY
或auth_mode=AWS_IAM
的请求都将成功(还有updateTodo、createTodo 和deleteTodo)。
在任何一种情况下,您都无法使用由用户启用的“开箱即用”的公共字段来实施您的工作流程,因为权限评估在在任何数据库信息可用之前完成。例如,IAM 授权使用由amplify update api
为您生成的未经身份验证的角色策略。您可以在 IAM 服务的 AWS 控制台中看到它。
为了部分实现您的私有/公共工作流程,我建议使用所谓的动态组授权(我们假设“AMAZON_COGNITO_USER_POOLS”身份验证模式)。在这种情况下,您在 Cognito 用户池中实现“公共”组,并使任何用户成为该组的成员(您可以使用 post signUp hooks 自动执行此操作)。
你的@auth 可能是这样的
type Todo @model
@auth(rules: [
allow: owner ,
allow: groups, groupsField: "groupsCanAccess", operations: [read] ,
])
id: ID!
name: String!
description: String
owner: String
editors: [String]
entity: String
groupsCanAccess: [String]
-
当您的用户决定是时候公开了,她请求 updateTodo 并将
groupsCanAccess
设置为 public
。如您所见,这是部分解决方案,因为要阅读待办事项,您的“公共”用户应该已注册。
为了部分实施您的员工组织工作流程,我可以建议下一个方法(我们再次假设“AMAZON_COGNITO_USER_POOLS”):
type User @model
@auth( rules: [
allow: owner ,
allow: groups, groupsField: "groupsCanAccess", operations: [read] ,
allow: groups, groups: ["Admin"]
])
id: ID!
owner: String! @auth(rules[allow: groups, groups: ["Admin"] ])
groupsCanAccess:[String]
@auth(rules[allow: owner,allow: groups, groups: ["Admin"] ])
todo: [Todo] @connection(keyName: "byUser", fields: ["owner"])
type Todo @model
(queries: null)
@auth (allow: owner, operations[delete])
@key [(name: "byUser", fields: ["owner", "description"])
id: ID!
name: String!
description: String
owner: String! @auth(rules[allow: owner])
这里每个人都可以创建,更新和读取Todo,但是读取操作只能通过User(查询:null)进行,所以除了所有者之外没有人可以获取特定的id来更新特定的Todo。尽管有人可以猜到这个 id 的可能性很小,但这是这种方法的缺点。没有所有者(字段上的感叹号)就不可能创建 Todo,除了所有者之外没有人可以创建 Todo(除了所有者之外没有人可以更改所有者字段)。请注意,操作[delete] 很重要,因为没有它,任何人都无法通过用户查询 Todo(allow: owner 相当于 allow: owner, operations[create,update,read,delete])。
所有者可以对用户执行任何操作,但她无法创建或删除用户,因为她无法更改受 perfield @auth 指令保护的所有者字段
管理员可以创建用户并将所有者设置为特定用户。
管理员和所有者可以更改 groupsCanAccess 字段。该数组中的每个元素都对应于某个组织。当管理员或所有者添加某个组织时,该组的每个成员都可以通过该用户访问该用户以及她的所有待办事项。缺点 - 所有者可以禁止管理员授予的访问权限。因为 Todos 不受更新保护 - 每个组成员都可以更改特定用户的 Todos。
将 Cognito 用户池用户添加到组的操作由管理员执行,超出了本文档的范围。缺点 - 每个 Cognito 池只有 300 个组。
最后 - 您当然可以选择手动调整 Amplify 自动生成的解析器。您可以使用“AWS_IAM”并通过分析您的 rezolver 映射模板中的 $context.identity.cognitoIdentityId 来组织所有者检查。如您所知 - 此 Cognito 身份池参数与用户池中的用户之间存在一对一的对应关系。当您需要存储公共和所有者的 Todos 时尤其方便(每个身份池 cognitoIdentityId 对应于未经身份验证的用户有一些唯一的)。
【讨论】:
【参考方案2】:有完全相同的问题 我希望暂时的解决方案是:添加一个 lamdaFunction : myResolver 并将这个函数添加到 graqphl-schema。在该函数中,a 可以通过 databaseClient 类进行所有过滤
架构
type Query
myqueryresolver(params: String): String @function (name: "myqueryresolver-$env")
现在您定义一个 lamda(在我的情况下,我使用通用形式 ....),您可以在其中执行所有非标准查询。
你调用这个函数:
const dataObj = await API.graphql(
query: myqueryresolver,
variables: "params": JSON.stringify(params)
);
所有的公共访问都通过这个函数调用。 附加提示 您可以使用“放大模拟”在本地运行它,并且可以在本地查看 console.log 的所有输出 - 无需在开发期间部署。
【讨论】:
以上是关于AWS Amplify 基于 db 字段值或关系的授权的主要内容,如果未能解决你的问题,请参考以下文章
通过 AWS amplify 和 ElasticSearch 查找地理和其他字段
如何将现有的 dynamo db 与 AWS Amplify 和 graphql 一起使用
如何按日期(createdAt)对aws-amplify中列表查询中的字段进行排序?
AWS Amplify 突变调用返回“UnknownArgument 类型的验证错误:未知字段参数”
AWS Amplify Appsync 在创建具有关系的对象时解决错误
在 React 应用程序中为基于 JavaScript AWS Amplify SDK / AWS Cognito 的身份验证分离用户池