

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

# Casos de uso de controle de acesso para proteger solicitações e respostas
<a name="security-authorization-use-cases"></a>

Na seção [Segurança](security-authz.md#aws-appsync-security) você aprendeu sobre os diferentes modos de Autorização para proteger sua API e uma introdução foi dada sobre mecanismos de Autorização refinada para entender os conceitos e o fluxo. Como AWS AppSync permite que você execute operações lógicas completas nos dados por meio do uso dos [modelos de mapeamento](resolver-mapping-template-reference-overview.md#aws-appsync-resolver-mapping-template-reference-overview) do GraphQL Resolver, você pode proteger os dados na leitura ou gravação de uma maneira muito flexível usando uma combinação de identidade de usuário, condicionais e injeção de dados.

Se você não estiver familiarizado com a edição de AWS AppSync resolvedores, consulte o [guia de programação](resolver-mapping-template-reference-programming-guide.md#aws-appsync-resolver-mapping-template-reference-programming-guide).

## Visão geral do
<a name="overview"></a>

Conceder acesso aos dados em um sistema é tradicionalmente feito por meio de uma [Matriz de controle do acesso](https://en.wikipedia.org/wiki/Access_Control_Matrix) em que a interseção de uma linha (recurso) e uma coluna (usuário/função) são as permissões concedidas.

AWS AppSync usa recursos em sua própria conta e insere informações de identidade (usuário/função) na solicitação e resposta do GraphQL como um [objeto de contexto](resolver-context-reference.md#aws-appsync-resolver-mapping-template-context-reference), que você pode usar no resolvedor. Isso significa que as permissões podem ser concedidas adequadamente em operações de leitura ou gravação com base na lógica do resolvedor. Se essa lógica estiver no nível do recurso, por exemplo, somente determinados usuários ou grupos nomeados podem read/write acessar uma linha específica do banco de dados, esses “metadados de autorização” deverão ser armazenados. AWS AppSync não armazena nenhum dado, portanto, você deve armazenar esses metadados de autorização com os recursos para que as permissões possam ser calculadas. Os metadados de autorização geralmente são um atributo (coluna) em uma tabela do DynamoDB, como um **proprietário** ou lista de usuários/grupos. Por exemplo, pode haver atributos **Leitores** e **Gravadores**.

Em um alto nível, isso significa que, se estiver lendo um item individual de uma fonte de dados, execute uma instrução `#if () ... #end` condicional no modelo da resposta depois que o resolvedor leu a fonte de dados. A verificação normalmente usará valores de usuário ou grupo em `$context.identity` para verificações de associação nos metadados de autorização retornados de uma operação de leitura. Para vários registros, como listas retornadas de uma tabela `Scan` ou `Query`, você enviará a verificação de condição como parte da operação à fonte de dados usando valores de usuário ou grupo semelhantes.

De maneira semelhante à gravação de dados você aplicará uma instrução condicional para a ação (como `PutItem` ou `UpdateItem`) para ver se o usuário ou grupo que faz uma mutação tem permissão. Novamente, o condicional muitas vezes usará um valor em `$context.identity` para comparar nos metadados de autorização daquele recurso. Para ambos os modelos da solicitação e da resposta, você também pode usar cabeçalhos personalizados de clientes para executar verificações de validação.

## Leitura de dados
<a name="reading-data"></a>

Conforme descrito acima, os metadados de autorização para realizar uma verificação devem ser armazenados com um recurso ou enviados para a solicitação do GraphQL (identidade, cabeçalho, etc.). Para demonstrar isso suponha que você tem a tabela do DynamoDB abaixo:

![\[DynamoDB table with ID, Data, PeopleCanAccess, GroupsCanAccess, and Owner columns.\]](http://docs.aws.amazon.com/pt_br/appsync/latest/devguide/images/auth.png)


A chave primária é `id` e os dados a serem acessados são `Data`. As outras colunas são exemplos de verificações que podem ser executadas para autorização. `Owner` seria uma `String` enquanto `PeopleCanAccess` e `GroupsCanAccess` seriam `String Sets`, conforme descrito na [Referência do modelo de mapeamento do resolvedor para o DynamoDB](resolver-mapping-template-reference-dynamodb.md#aws-appsync-resolver-mapping-template-reference-dynamodb).

Na [visão geral do modelo de mapeamento do resolvedor](resolver-mapping-template-reference-overview.md#aws-appsync-resolver-mapping-template-reference-overview) o diagrama mostra como o modelo da resposta contém não apenas o objeto de contexto, mas também os resultados da fonte de dados. Para consultas do GraphQL de itens individuais, você pode usar o modelo da resposta para verificar se o usuário tem permissão para ver esses resultados ou retornar uma mensagem de erro de autorização. Isso geralmente é indicado como um "Filtro de autorização". Para consultas do GraphQL que retornam listas, usando uma Scan ou Query, é mais eficiente realizar a verificação no modelo da solicitação e retornar dados somente se uma condição de autorização for atendida. A implementação é:

1. GetItem - verificação de autorização para registros individuais. Feita usando instruções `#if() ... #end`.

1. Operações Scan/Query – verificação de autorização é uma instrução `"filter":{"expression":...}`. As verificações comuns são a igualdade (`attribute = :input`) ou verificar se um valor está em uma lista (`contains(attribute, :input)`).

Em \$12 o `attribute` em ambas as instruções representa o nome da coluna do registro em uma tabela, como `Owner` no exemplo acima. Você pode transformar isso em alias com um sinal `#` e usar `"expressionNames":{...}`, mas não é obrigatório. O `:input` é uma referência ao valor que você está comparando com o atributo do banco de dados, que será definido em `"expressionValues":{...}`. Você verá esses exemplos abaixo.

### Caso de uso: o proprietário pode fazer leitura
<a name="use-case-owner-can-read"></a>

Usando a tabela acima, se apenas deseja retornar dados se `Owner == Nadia` para uma única operação de leitura (`GetItem`) o modelo terá a seguinte aparência:

```
#if($context.result["Owner"] == $context.identity.username)
    $utils.toJson($context.result)
#else
    $utils.unauthorized()
#end
```

Algumas coisas devem ser mencionadas aqui, que serão reutilizadas nas seções restantes. Primeiro, a verificação usa `$context.identity.username` que será o nome de login de usuário amigável se grupos de usuários do Amazon Cognito for usado e será a identidade do usuário se o IAM for usado (incluindo as Identidades federadas do Amazon Cognito). Existem outros valores a serem armazenados para um proprietário, como o valor exclusivo "identidade do Amazon Cognito", que é útil ao federar logins de vários locais, e você deve revisar as opções disponíveis na [Referência de contexto do modelo de mapeamento do resolvedor](resolver-context-reference.md#aws-appsync-resolver-mapping-template-context-reference).

Segundo, a verificação condicional else respondendo com `$util.unauthorized()` é totalmente opcional, mas recomendado como uma das melhores práticas ao projetar sua API GraphQL.

### Caso de uso: codificar acesso específico
<a name="use-case-hardcode-specific-access"></a>

```
// This checks if the user is part of the Admin group and makes the call
#foreach($group in $context.identity.claims.get("cognito:groups"))
    #if($group == "Admin")
        #set($inCognitoGroup = true)
    #end
#end
#if($inCognitoGroup)
{
    "version" : "2017-02-28",
    "operation" : "UpdateItem",
    "key" : {
        "id" : $util.dynamodb.toDynamoDBJson($ctx.args.id)
    },
    "attributeValues" : {
        "owner" : $util.dynamodb.toDynamoDBJson($context.identity.username)
        #foreach( $entry in $context.arguments.entrySet() )
            ,"${entry.key}" : $util.dynamodb.toDynamoDBJson($entry.value)
        #end
    }
}
#else
    $utils.unauthorized()
#end
```

### Caso de uso: filtrar uma lista de resultados
<a name="use-case-filtering-a-list-of-results"></a>

No exemplo anterior você pôde executar uma verificação em `$context.result` diretamente pois retornou um único item, no entanto algumas operações como uma verificação retornarão vários itens em `$context.result.items`, onde será necessário executar o filtro de autorização e retornar apenas os resultados que o usuário tem permissão para ver. Digamos que o campo `Owner` tinha o IdentityID do Amazon Cognito dessa vez definido no registro, então você poderia usar o seguinte modelo de mapeamento da resposta para filtrar a exibição somente dos registros de propriedade do usuário:

```
#set($myResults = [])
#foreach($item in $context.result.items)
    ##For userpools use $context.identity.username instead
    #if($item.Owner == $context.identity.cognitoIdentityId)
        #set($added = $myResults.add($item))
    #end
#end
$utils.toJson($myResults)
```

### Caso de uso: várias pessoas podem fazer leitura
<a name="use-case-multiple-people-can-read"></a>

Outra opção de autorização popular é permitir que um grupo de pessoas seja capaz de ler dados. No exemplo abaixo, o `"filter":{"expression":...}` retorna apenas valores de uma verificação de tabela se o usuário que executa a consulta do GraphQL estiver listado no conjunto de `PeopleCanAccess`.

```
{
    "version" : "2017-02-28",
    "operation" : "Scan",
    "limit": #if(${context.arguments.count}) $util.toJson($context.arguments.count) #else 20 #end,
    "nextToken": #if(${context.arguments.nextToken})  $util.toJson($context.arguments.nextToken) #else null #end,
    "filter":{
        "expression": "contains(#peopleCanAccess, :value)",
        "expressionNames": {
                "#peopleCanAccess": "peopleCanAccess"
        },
        "expressionValues": {
                ":value": $util.dynamodb.toDynamoDBJson($context.identity.username)
        }
    }
}
```

### Caso de uso: o grupo pode fazer leitura
<a name="use-case-group-can-read"></a>

Semelhante ao último caso de uso, pode ser que apenas as pessoas em um ou mais grupos tenham direitos para ler determinados itens em um banco de dados. O uso da operação `"expression": "contains()"` é semelhante, no entanto é um OU lógico de todos os grupos dos quais um usuário pode fazer parte, o que precisa ser considerado na associação definida. Nesse caso, acumulamos uma instrução `$expression` abaixo para cada grupo em que o usuário está e, em seguida, enviamos isso ao filtro:

```
#set($expression = "")
#set($expressionValues = {})
#foreach($group in $context.identity.claims.get("cognito:groups"))
    #set( $expression = "${expression} contains(groupsCanAccess, :var$foreach.count )" )
    #set( $val = {})
    #set( $test = $val.put("S", $group))
    #set( $values = $expressionValues.put(":var$foreach.count", $val))
    #if ( $foreach.hasNext )
    #set( $expression = "${expression} OR" )
    #end
#end
{
    "version" : "2017-02-28",
    "operation" : "Scan",
    "limit": #if(${context.arguments.count}) $util.toJson($context.arguments.count) #else 20 #end,
    "nextToken": #if(${context.arguments.nextToken})  $util.toJson($context.arguments.nextToken) #else null #end,
    "filter":{
        "expression": "$expression",
        "expressionValues": $utils.toJson($expressionValues)
    }
}
```

## Gravação de dados
<a name="writing-data"></a>

A gravação de dados em mutações sempre é controlada no modelo de mapeamento da solicitação. No caso de fontes de dados do DynamoDB, a chave é usar um `"condition":{"expression"...}"` adequado que executa a validação nos metadados de autorização nessa tabela. Em [Segurança](security-authz.md#aws-appsync-security), fornecemos um exemplo que pode ser usado para verificar o campo `Author` em uma tabela. Os casos de uso nessa seção exploram mais casos de uso.

### Caso de uso: vários proprietários
<a name="use-case-multiple-owners"></a>

Usando a tabela de exemplo do diagrama anterior, suponha que a lista `PeopleCanAccess`

```
{
    "version" : "2017-02-28",
    "operation" : "UpdateItem",
    "key" : {
        "id" : $util.dynamodb.toDynamoDBJson($ctx.args.id)
    },
    "update" : {
        "expression" : "SET meta = :meta",
        "expressionValues": {
            ":meta" : $util.dynamodb.toDynamoDBJson($ctx.args.meta)
        }
    },
    "condition" : {
        "expression"       : "contains(Owner,:expectedOwner)",
        "expressionValues" : {
            ":expectedOwner" : $util.dynamodb.toDynamoDBJson($context.identity.username)
        }
    }
}
```

### Caso de uso: o grupo pode criar um novo registro
<a name="use-case-group-can-create-new-record"></a>

```
#set($expression = "")
#set($expressionValues = {})
#foreach($group in $context.identity.claims.get("cognito:groups"))
    #set( $expression = "${expression} contains(groupsCanAccess, :var$foreach.count )" )
    #set( $val = {})
    #set( $test = $val.put("S", $group))
    #set( $values = $expressionValues.put(":var$foreach.count", $val))
    #if ( $foreach.hasNext )
    #set( $expression = "${expression} OR" )
    #end
#end
{
    "version" : "2017-02-28",
    "operation" : "PutItem",
    "key" : {
        ## If your table's hash key is not named 'id', update it here. **
        "id" : $util.dynamodb.toDynamoDBJson($ctx.args.id)
        ## If your table has a sort key, add it as an item here. **
    },
    "attributeValues" : {
        ## Add an item for each field you would like to store to Amazon DynamoDB. **
        "title" : $util.dynamodb.toDynamoDBJson($ctx.args.title),
        "content": $util.dynamodb.toDynamoDBJson($ctx.args.content),
        "owner": $util.dynamodb.toDynamoDBJson($context.identity.username)
    },
    "condition" : {
        "expression": $util.toJson("attribute_not_exists(id) AND $expression"),
        "expressionValues": $utils.toJson($expressionValues)
    }
}
```

### Caso de uso: o grupo pode atualizar um registro existente
<a name="use-case-group-can-update-existing-record"></a>

```
#set($expression = "")
#set($expressionValues = {})
#foreach($group in $context.identity.claims.get("cognito:groups"))
    #set( $expression = "${expression} contains(groupsCanAccess, :var$foreach.count )" )
    #set( $val = {})
    #set( $test = $val.put("S", $group))
    #set( $values = $expressionValues.put(":var$foreach.count", $val))
    #if ( $foreach.hasNext )
    #set( $expression = "${expression} OR" )
    #end
#end
{
    "version" : "2017-02-28",
    "operation" : "UpdateItem",
    "key" : {
        "id" : $util.dynamodb.toDynamoDBJson($ctx.args.id)
    },
    "update":{
                "expression" : "SET title = :title, content = :content",
        "expressionValues": {
            ":title" : $util.dynamodb.toDynamoDBJson($ctx.args.title),
            ":content" : $util.dynamodb.toDynamoDBJson($ctx.args.content)
        }
    },
    "condition" : {
        "expression": $util.toJson($expression),
        "expressionValues": $utils.toJson($expressionValues)
    }
}
```

## Registros públicos e privados
<a name="public-and-private-records"></a>

Com os filtros condicionais você também pode optar por marcar os dados como privado, público ou alguma outra verificação Booliana. Isso pode ser combinado como parte de um filtro de autorização dentro do modelo da resposta. Usar essa verificação é uma boa maneira de ocultar temporariamente os dados ou removê-los da visualização sem tentar controlar a associação de grupo.

Por exemplo, suponha que você adicionou um atributo em cada item na tabela do DynamoDB chamada `public` com um valor de `yes` ou `no`. O seguinte modelo da resposta pode ser usado em uma chamada `GetItem` para exibir os dados somente se o usuário estiver em um grupo que tem acesso E se esses dados estiverem marcados como público:

```
#set($permissions = $context.result.GroupsCanAccess)
#set($claimPermissions = $context.identity.claims.get("cognito:groups"))

#foreach($per in $permissions)
    #foreach($cgroups in $claimPermissions)
        #if($cgroups == $per)
            #set($hasPermission = true)
        #end
    #end
#end

#if($hasPermission && $context.result.public == 'yes')
    $utils.toJson($context.result)
#else
    $utils.unauthorized()
#end
```

O código acima também pode usar um OU lógico (`||`) para permitir que as pessoas façam leitura se tiverem permissão para um registro ou se for público:

```
#if($hasPermission || $context.result.public == 'yes')
    $utils.toJson($context.result)
#else
    $utils.unauthorized()
#end
```

No geral, os operadores padrão `==`, `!=`, `&&` e `||` serão úteis ao executar verificações de autorização.

## Dados em tempo real
<a name="security-real-time-data"></a>

Você pode aplicar Controles de acesso refinados em assinaturas do GraphQL no momento em que um cliente faz uma assinatura, usando as mesmas técnicas descritas anteriormente nessa documentação. Anexe um resolvedor ao campo de assinatura, no momento em que pode consultar dados de uma fonte de dados e realizar a lógica condicional nos modelos de mapeamento da solicitação ou da resposta. Você também pode retornar dados adicionais para o cliente, como os resultados iniciais de uma assinatura, desde que a estrutura de dados corresponda àquela do tipo retornado na assinatura do GraphQL.

### Caso de uso: o usuário pode assinar apenas conversas específicas
<a name="use-case-user-can-subscribe-to-specific-conversations-only"></a>

Um caso de uso comum para dados em tempo real com assinaturas do GraphQL é a criação de um aplicativo mensagens ou bate-papo privado. Ao criar um aplicativo de bate-papo com vários usuários, as conversas podem ocorrer entre duas pessoas ou entre várias pessoas. Elas podem ser agrupadas em "salas", privadas ou públicas. Dessa forma, você deseja autorizar apenas um usuário para assinar uma conversa (que pode ser um a um ou entre um grupo) à qual terão o acesso concedido. Para fins de demonstração, o exemplo abaixo mostra um caso de uso simples de um usuário que envia uma mensagem privada para outro. A configuração tem duas tabelas do Amazon DynamoDB:
+ Tabela de mensagens: (chave primária) `toUser`, (chave de classificação) `id` 
+ Tabela de permissões: (chave primária) `username` 

A tabela de Mensagens armazena as mensagens que realmente foram enviadas por meio de uma mutação do GraphQL. A tabela de Permissões é verificada pela assinatura do GraphQL para autorização no momento da conexão do cliente. O exemplo abaixo pressupõe que você está usando o seguinte esquema do GraphQL:

```
input CreateUserPermissionsInput {
    user: String!
    isAuthorizedForSubscriptions: Boolean
}

type Message {
    id: ID
    toUser: String
    fromUser: String
    content: String
}

type MessageConnection {
    items: [Message]
    nextToken: String
}

type Mutation {
    sendMessage(toUser: String!, content: String!): Message
    createUserPermissions(input: CreateUserPermissionsInput!): UserPermissions
    updateUserPermissions(input: UpdateUserPermissionInput!): UserPermissions
}

type Query {
    getMyMessages(first: Int, after: String): MessageConnection
    getUserPermissions(user: String!): UserPermissions
}

type Subscription {
    newMessage(toUser: String!): Message
        @aws_subscribe(mutations: ["sendMessage"])
}

input UpdateUserPermissionInput {
    user: String!
    isAuthorizedForSubscriptions: Boolean
}

type UserPermissions {
    user: String
    isAuthorizedForSubscriptions: Boolean
}

schema {
    query: Query
    mutation: Mutation
    subscription: Subscription
}
```

Algumas das operações padrão, como `createUserPermissions()`, não são abordadas abaixo para ilustrar os resolvedores de assinatura, mas são implementações padrão de resolvedores do DynamoDB. Em vez disso, vamos nos concentrar nos fluxos de autorização da assinatura com resolvedores. Para enviar uma mensagem de um usuário para outro, anexe um resolvedor ao campo `sendMessage()` e selecione a fonte de dados da tabela de **Mensagens** com o seguinte modelo da solicitação:

```
{
    "version" : "2017-02-28",
    "operation" : "PutItem",
    "key" : {
        "toUser" : $util.dynamodb.toDynamoDBJson($ctx.args.toUser),
        "id" : $util.dynamodb.toDynamoDBJson($util.autoId())
    },
    "attributeValues" : {
        "fromUser" : $util.dynamodb.toDynamoDBJson($context.identity.username),
        "content" : $util.dynamodb.toDynamoDBJson($ctx.args.content),
    }
}
```

Neste exemplo, usamos `$context.identity.username`. Isso retorna as informações do usuário AWS Identity and Access Management ou dos usuários do Amazon Cognito. O modelo da resposta é uma simples passagem de `$util.toJson($ctx.result)`. Salvar e voltar à página do esquema. Em seguida, anexe um resolvedor para a assinatura `newMessage()`, usando a tabela de **Permissões** como uma fonte de dados e o seguinte modelo de mapeamento da solicitação:

```
{
    "version": "2018-05-29",
    "operation": "GetItem",
    "key": {
        "username": $util.dynamodb.toDynamoDBJson($ctx.identity.username),
    },
}
```

Em seguida, use o seguinte modelo de mapeamento da resposta para executar as verificações de autorização usando os dados da tabela de **Permissões**:

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

Nesse caso, você está fazendo três verificações de autorização. A primeira garante que um resultado seja retornado. A segunda garante que o usuário não esteja assinando mensagens destinadas a outra pessoa. A terceira garante que o usuário tenha permissão para assinar qualquer campo, verificando um atributo do DynamoDB de `isAuthorizedForSubscriptions` armazenado como um `BOOL`.

Para testar as coisas, você pode entrar no AWS AppSync console usando grupos de usuários do Amazon Cognito e um usuário chamado “Nadia” e, em seguida, executar a seguinte assinatura do GraphQL:

```
subscription AuthorizedSubscription {
    newMessage(toUser: "Nadia") {
        id
        toUser
        fromUser
        content
    }
}
```

Se, na tabela de **Permissões**, houver um registro para o atributo chave `username` de `Nadia` com `isAuthorizedForSubscriptions` definido como `true`, você verá uma resposta bem-sucedida. Se tentar um `username` diferente na consulta `newMessage()` acima, um erro será retornado.