Фильтрация подписок на события AWS AppSync для пользователя Cognito

У меня есть следующая схема:

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

Однако я не знаю, что добавить в шаблон сопоставления запросов.

2 ответа

Я думаю, что первый шаг для фильтрации подписок на основе пользователя проще всего сделать с небольшим обновлением вашей схемы, разбив входную форму на отдельные входы для мутации. В частности:

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, если это не прямой ввод в мутацию, если он внутри и форма ввода не будет работать.

Теперь, насколько это возможно, пользователь не может просто подписаться на чужое имя пользователя. Я полагаю, что вы смотрели на эту страницу документов. Это будет работать, полностью допустимо и может быть дополнено чем-то похожим на пример на этой странице документации, но оно основано на наличии таблицы поиска разрешений и распознавателя Dynamo. Если у вас его нет или вы предпочитаете избегать его использования, небольшая настройка должна помочь ему работать с не / локальным распознавателем. Без таблицы разрешений или чего-либо, с чем можно было бы проверить, я бы настоятельно рекомендовал локальный / без разрешения.

В частности, я считаю, что вы можете переместить то, что у вас есть в шаблоне отображения ответов, в новый шаблон отображения вашего нет / локального преобразователя...

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

... и если шаблон сопоставления ответов будет ответом по умолчанию, то вы получите его без ненужной инфраструктуры в таблице разрешений или мертвом коде, который устанавливает динамическое взаимодействие, которое не происходит. Вместо этого все, что нужно будет сделать, - это проверить имя пользователя во входных данных и имя пользователя в токене Cognito.

До тех пор, пока Appsync не улучшится, вот как я выполнил подписку, которая позволяет пользователю подписываться только на события, которые соответствуют его собственному userID, используя схему, которую я разместил выше:

Шаблон сопоставления запроса:

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

Шаблон сопоставления запросов предназначен только для внешнего вида (веб-консоль Appsync не позволит вам сохранить данные, не заполнив его чем-либо допустимым). Он выполняет жесткий код поиска каждый раз, когда кто-то делает запрос на подписку. Это ничего, кроме успеха, и данные выбрасываются. Так работает подписка в Appsync.

Шаблон отображения ответов подписки:

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

Вот где происходит волшебство. В основном это говорит о том, что если пользователь не запрашивал подписку на события с тем же именем пользователя, что и у себя, - return unauthorized, Если пользователь запросил подписку на события с тем же идентификатором пользователя, что и вошедшая в систему учетная запись, null (null - это способ успешного продолжения шаблона отображения ответов (т. е. без ошибок).

Для полноты, вот как выглядит запрос клиента:

const eventSub = `subscription eventSub($userID: String!) {
  onCreateEvent(userID: $userID) {
    userID
    email_hash
    eventID
    type
    data
    dateTime
  }
}`;
Другие вопросы по тегам