7

DynamoDB テーブルで定義している API/サービスがあります。いくつかのクエリをサポートするために、いくつかのインデックス (グローバル セカンダリ インデックスとして定義) があります。GSI 定義を使用してテーブルを設計し、正しいクエリのように見えます。ただし、クエリを作成すると、次の例外が発生します。

{ AccessDeniedException: User: arn:aws:sts::OBSCURED:assumed-role/chatroom-application-dev-us-east-1-lambdaRole/chatroom-application-dev-getRoomMessages is not authorized to perform: dynamodb:Query on resource: arn:aws:dynamodb:us-east-1:OBSCURED:table/messages-table-dev/index/roomIndex
at Request.extractError (/var/task/node_modules/aws-sdk/lib/protocol/json.js:48:27)
at Request.callListeners (/var/task/node_modules/aws-sdk/lib/sequential_executor.js:105:20)
at Request.emit (/var/task/node_modules/aws-sdk/lib/sequential_executor.js:77:10)
at Request.emit (/var/task/node_modules/aws-sdk/lib/request.js:683:14)
at Request.transition (/var/task/node_modules/aws-sdk/lib/request.js:22:10)
at AcceptorStateMachine.runTo (/var/task/node_modules/aws-sdk/lib/state_machine.js:14:12)
at /var/task/node_modules/aws-sdk/lib/state_machine.js:26:10
at Request.<anonymous> (/var/task/node_modules/aws-sdk/lib/request.js:38:9)
at Request.<anonymous> (/var/task/node_modules/aws-sdk/lib/request.js:685:12)
at Request.callListeners (/var/task/node_modules/aws-sdk/lib/sequential_executor.js:115:18)
message: 'User: arn:aws:sts::OBSCURED:assumed-role/chatroom-application-dev-us-east-1-lambdaRole/chatroom-application-dev-getRoomMessages is not authorized to perform: dynamodb:Query on resource: arn:aws:dynamodb:us-east-1:OBSCURED:table/messages-table-dev/index/roomIndex',
code: 'AccessDeniedException',
time: 2018-06-02T22:05:46.110Z,
requestId: 'OBSCURED',
statusCode: 400,
retryable: false,
retryDelay: 30.704899664776054 }

例外の上部には、getRoomMessages メソッドis not authorized to perform: dynamodb:Query on resource:の ARN が表示され、グローバル セカンダリ インデックスの ARN が表示されます。

グローバル セカンダリ インデックスへのアクセス権を付与するポリシーを定義する必要があることは明らかです。しかし、その方法はまったく明らかではありません。DynamoDB に関する他の StackOverflow の質問で、ドキュメントが断片化されていて、何かを見つけるのがいかに難しいかについて不平を言っているのを見てきました。私は同意しなければなりません。「断片化」という言葉は控えめすぎる。

サーバーレス フレームワークを使用しています。このproviderセクションは、このポリシー/ロールの定義を示しています。

provider:
  name: aws
  runtime: nodejs8.10
  stage: dev
  region: us-east-1
  iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
      Resource:
        - { "Fn::GetAtt": ["MessagesDynamoDBTable", "Arn" ] }
        - { "Fn::GetAtt": ["#roomIndex", "Arn" ] }
        - { "Fn::GetAtt": ["#userIndex", "Arn" ] }
  environment:
    MESSAGES_TABLE: ${self:custom.tableName}

このResourceセクションでは、許可が宣言されているリソースをリストすることになっていると思います。1 つ目は、テーブル全体を参照します。追加した最後の 2 つは、インデックスを参照します。

編集:実行するとserverless deploy、次のメッセージが出力されます。

The CloudFormation template is invalid: Template error: instance of Fn::GetAtt references undefined resource #roomIndex

これについていくつかのバリエーションを試しましたが、同じエラーが発生しました。要するに、Cloudfront 構文を使用して、インデックスの ARN を取得するにはどうすればよいかということですserverless.yml。例外に示されているため、ARN は存在します。

DynamoDB テーブル定義:

resources:
  Resources:
    MessagesDynamoDBTable:
      Type: AWS::DynamoDB::Table
      Properties:
        AttributeDefinitions:
          - AttributeName: messageId
            AttributeType: S
          - AttributeName: room
            AttributeType: S
          - AttributeName: userId
            AttributeType: S
        KeySchema:
          - AttributeName: messageId
            KeyType: HASH
        GlobalSecondaryIndexes:
          - IndexName: roomIndex
            KeySchema:
              - AttributeName: room
                KeyType: HASH
            Projection:
              ProjectionType: ALL
            ProvisionedThroughput:
              ReadCapacityUnits: 1
              WriteCapacityUnits: 1
          - IndexName: userIndex
            KeySchema:
              - AttributeName: userId
                KeyType: HASH
            Projection:
              ProjectionType: ALL
            ProvisionedThroughput:
              ReadCapacityUnits: 1
              WriteCapacityUnits: 1
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
        TableName: ${self:custom.tableName}

上記の例外に対応して使用されているクエリ:

{
    "TableName": "messages-table-dev",
    "IndexName": "roomIndex",
    "KeyConditionExpression": "#roomIndex = :room",
    "ExpressionAttributeNames": {
        "#roomIndex": "room"
    },
    "ExpressionAttributeValues": {
        ":room": {
            "S": "everyone"
        }
    }
}

そして、クエリを生成する Lambda 関数のコード スニペット:

app.get('/messages/room/:room', (req, res) => {
    const params = {
        TableName: MESSAGES_TABLE,
        IndexName: "roomIndex",
        KeyConditionExpression: '#roomIndex = :room',
        ExpressionAttributeNames: { '#roomIndex': 'room' },
        ExpressionAttributeValues: {
            ":room": { S: `${req.params.room}` }
        },
    };
    console.log(`QUERY ROOM ${JSON.stringify(params)}`);
    dynamoDb.query(params, (error, result) => {
        if (error) {
            console.log(error);
            res.status(400).json({ error: 'Could not get messages' });
        } else {
            res.json(result.Items);
        }
    });
});
4

3 に答える 3

14

jake.lang が投稿したものよりも良い答えを見つけました。編集:彼が次のことを提案した2番目のコメントは見ませんでした。

彼が指摘したように、ARN は正当な理由で変更される可能性があるため、彼は正しくありませんでした。ただし、グローバルセカンダリインデックスの ARN はテーブルの ARN に「/INDEXNAME」を追加するため、解決策が生まれました。これは、ポリシー ステートメントが次のようになることを意味します。

iamRoleStatements:
  - Effect: Allow
    Action:
      - dynamodb:Query
      - dynamodb:Scan
      - dynamodb:GetItem
      - dynamodb:PutItem
      - dynamodb:UpdateItem
      - dynamodb:DeleteItem
    Resource:
      - { "Fn::GetAtt": ["MessagesDynamoDBTable", "Arn" ] }
      - { "Fn::Join": [ "/", [ 
          { "Fn::GetAtt": ["MessagesDynamoDBTable", "Arn" ] }, "index", "roomIndex" 
        ]]}
      - { "Fn::Join": [ "/", [
          { "Fn::GetAtt": ["MessagesDynamoDBTable", "Arn" ] }, "index", "userIndex" 
        ]]}

「Fn::Join」ビットは CloudFormation からのもので、「結合」操作です。文字列の配列を取り、最初の引数を使用してそれらを連結します。したがって、このポリシーステートメントで必要な ARN を計算するのは、かなり複雑で非常に複雑な方法です。

ドキュメントについては、https ://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-join.html を参照してください。

于 2018-06-05T02:08:00.097 に答える
9

Fn::Joinを使用する代わりに、!Sub "${MessagesDynamoDBTable.Arn}"より簡単です。さらに、すべてのインデックスにアクセスしたい場合 (通常は私の場合です)、/index/*必要なのはそれだけです。

例:

...
      Policies:
        - Version: "2012-10-17"
          Statement:
            - Effect: Allow
              Action:
                - dynamodb:Query
              Resource:
                - !Sub "${MessagesDynamoDBTable.Arn}"
                - !Sub "${MessagesDynamoDBTable.Arn}/index/*"
...
于 2019-05-17T07:29:21.723 に答える
1

サーバーレスで作業中の GSI アクセス許可を宣言した方法を次に示します。よくわかりませんが、問題は各リソースのアクションを個別に宣言する必要があることでしょうか?

iamRoleStatements: 
  - Effect: Allow
    Action:
      - dynamodb:Query
      - dynamodb:GetItem
      - dynamodb:PutItem
    Resource: "arn:REDACTED:table/TABLENAME”
  - Effect: Allow
    Action:
      - dynamodb:Query
    Resource: "arn:REDACTED:table/TABLENAME/index/INDEXNAME”

この arn のハードコーディングは、おそらく最善の方法ではありませんが、これまでのところ、私の開発ではうまく機能しています。

于 2018-06-02T23:04:19.843 に答える