针对 Cognito 用户的 AWS AppSync 事件订阅过滤

Posted

技术标签:

【中文标题】针对 Cognito 用户的 AWS AppSync 事件订阅过滤【英文标题】:AWS AppSync Event Subscription Filtering on Cognito User 【发布时间】:2019-04-03 00:31:27 【问题描述】:

我有以下架构:

input CreateEventInput 
    userID: String!
    eventID: ID!
    type: String!
    data: String
    dateTime: AWSDateTime!


type Mutation 
    createEvent(input: CreateEventInput!): event


type Subscription 
    onCreateEvent(): event
        @aws_subscribe(mutations: ["createEvent"])

createEvent 解析器将userID 设置为:

"key" : 
        "userID" : $util.dynamodb.toDynamoDBJson($context.identity.username),
        "eventID" : $util.dynamodb.toDynamoDBJson($util.autoId())
    

我想限制订阅,以便仅将userID = $context.identity.username 的记录返回给用户。

有人知道怎么设置吗?我想我需要一个关于订阅的解析器,但我找不到一个明确的例子,你有一个主分区键 (userID) 和主排序键 (eventID)。

非常感谢任何帮助或指导。如果需要,我可以更改架构或数据库。

更新

我相信我可以将订阅响应映射模板设置为:

#if($context.identity.username != $context.arguments.userID)
    $utils.unauthorized()
#else
##User is authorized, but we return null to continue
    null
#end

但是,我不知道该在请求映射模板中添加什么。

【问题讨论】:

明确地说,您是否让用户订阅他们自己的突变? 没错。我想防止他们得到其他用户的突变。 【参考方案1】:

我认为,根据用户过滤订阅的第一步最简单,只需对架构进行小幅更新,将“输入”形状分解为突变的单个输入。具体来说:

type mutation 
  createEvent(userID: String!, eventID: ID!, type: String!, 
    data: String, dateTime: AWSDateTime!): event

... other stuff...
type Subscription 
  onCreateEvent(userId: String!): event
  @aws_subscribe(mutations: ["createEvent"])

对此有几点说明:

1) 这假设您希望这是订阅的要求。如果不是,如果您希望它成为可选规则,请删除 !。根据您的评论,我相信您会想要它。

2) 订阅过滤器(这是订阅操作中的 userId 参数)要求过滤器在突变的响应中。因此,请确保在客户端上定义操作时,在响应中包含 userId。

3) 这是应用订阅过滤器所必需的。服务不会知道 userId 是什么,除非它是突变的直接输入,将其放在内部和输入形状将不起作用。

现在,就确定用户不能只订阅其他人的用户名而言。我相信你在看this docs page。这将起作用,完全有效,并且可以使用与该文档页面中的示例接近的内容来完成,但它基于具有权限查找表和 Dynamo 解析器。如果您没有或希望避免使用一个,稍作调整应该能够使其与无/本地解析器一起使用。如果没有权限表或任何要检查的东西,我强烈建议使用本地/无解析器。

具体来说,我相信您可以将响应映射模板中的内容移动到新的无/本地解析器的映射模板...

#if($context.identity.username != $context.arguments.userID)
    $utils.unauthorized()
#else
##User is authorized, but we return null to continue
    null
#end

...并让响应映射模板成为默认响应,那么您就可以在权限表中没有不必要的基础设施或设置不会发生的发电机交互的死代码。相反,所有这些都会检查输入中的用户名与 Cognito 令牌中的用户名。

【讨论】:

打破架构是/没有必要的。你能告诉我你认为这会有所帮助吗?我可以通过放置一个 dummy 请求映射模板来完成这项工作 - 只是一个硬编码的“操作”:“GetItem” - 因为这对订阅没有影响,只需要在那里为了保存响应映射。当然是黑客。我可以添加一个权限查找表——但我也不需要。 因此,AppSync 执行订阅用户、作者、帖子等操作的方式。带有订阅过滤器。这些是订阅功能的输入。我刚刚确认,那些申请的唯一方法是将突变的各个字段置于顶层,而不是输入形状。如果您不这样做并让它发挥作用,您是如何订阅单个用户的? AWS docs 中给出的订阅过滤示例(向下滚动到使用订阅参数)将突变的输入分开【参考方案2】:

在 Appsync 改进之前,我是如何使用我在上面发布的架构完成一个只允许用户订阅与他们自己的用户 ID 匹配的事件的订阅:

请求映射模板:


    "version": "2017-02-28",
    "operation": "GetItem",
    "key": 
        "userID": $util.dynamodb.toDynamoDBJson($ctx.identity.username),
        "eventID":  "S" : "0bfe0d7c-b469-441e-95f6-788fe300f76d" 
    ,

请求映射模板仅用于外观(Appsync Web 控制台不会让您在没有填充有效内容的情况下保存)每次有人提出订阅请求时都会进行硬编码查找。这只会成功,并且数据被丢弃。这就是 Appsync 中订阅的工作方式。

订阅响应映射模板:

#if($context.identity.username != $context.arguments.userID)
    $utils.unauthorized()
#else
##User is authorized, but we return null to continue
    null
#end

这就是魔法发生的地方。这基本上表示如果用户没有请求订阅与他们自己用户名相同的事件——返回unauthorized。如果用户确实请求订阅具有与登录帐户相同的 userID 的事件,null(null 是响应映射模板成功继续的方式(即不出错)。

为了完整起见,客户端请求如下所示:

const eventSub = `subscription eventSub($userID: String!) 
  onCreateEvent(userID: $userID) 
    userID
    email_hash
    eventID
    type
    data
    dateTime
  
`;

【讨论】:

以上是关于针对 Cognito 用户的 AWS AppSync 事件订阅过滤的主要内容,如果未能解决你的问题,请参考以下文章

AWS Cognito 用户身份验证

text [使用CLI注册Cognito用户] #aws #cognito#aws-cli

在 AWS Cognito 中管理用户

AWS Cognito:Cognito 用户池的元数据 URL 在哪里?

具有 Cognito 用户池授权方的 AWS SAM API

我是不是可以在 AWS 中创建仅对 Cognito 用户可用但对任何其他 AWS 用户不可用的密钥?