DynamoDB 属性レベル制御とちょっとだけ項目レベル制御

具体的によく知らなかったので調べた。

最近のデバイスはハイスぺなんで、一部の項目だけに絞ってサイズを小さくするってこともあんまりないし、特定アイテム(レコード)内でのデータの可視範囲を制御したいならそのデータはDynamoDBの外部に置いておく方が安全だし、あんまり使いどころがわからん。

あーでも実際にはアプリからのリクエスト時には(普通なら) API Gateway とか AppSync 経由でするように設計するんで API 開発はしやすいかもしれない。あとたぶんデータ転送量も抑えられるのでめちゃでかいシステムなら恩恵あるかも???

というのは個人的な貧相な経験からの感想だが、よくよく考えてみると、「DynamoDBのテーブル数は少ない方がよい。優れた設計はテーブル1つだ!」といった思想があった気がする。テーブル一つなら属性レベル制御絶対に必要だよね。

とりま試験のための勉強なんで概要だけをさらっと。

項目レベル制御と属性レベル制御

docs.aws.amazon.com

項目レベル制御: 項目レベルでアクセス可能・不可を制御。

属性レベル制御:属性レベルでアクセス可能・不可(可視・不可視)を制御。

利用イメージ(属性レベル制御)

docs.aws.amazon.com

docs.aws.amazon.com

GetItem, Query, Scan オペレーション実行時にプロジェクション式(projection expression)を指定することで特定の属性のみに絞ることができる。

例えば、以下の項目があったとする。★の部分のみが欲しい。

{
    "Id": 123,
    "Title": "Bicycle 123",
    "Description": "123 description",・・・★
    "BicycleType": "Hybrid",
    "Brand": "Brand-Company C",
    "Price": 500,
    "Color": ["Red", "Black"],
    "ProductCategory": "Bicycle",
    "InStock": true,
    "QuantityOnHand": null,
    "RelatedItems": [
        341,・・・★
        472,
        649
    ],
    "Pictures": {
        "FrontView": "http://example.com/products/123_front.jpg",
        "RearView": "http://example.com/products/123_rear.jpg",
        "SideView": "http://example.com/products/123_left_side.jpg"
    },
    "ProductReviews": {
        "FiveStar": [・・・★
                "Excellent! Can't recommend it highly enough! Buy it!",
                "Do yourself a favor and buy this."
        ],
        "OneStar": [
                "Terrible product! Do not buy this."
        ]
    },
    "Comment": "This product sells out quickly during the summer",
    "Safety.Warning": "Always wear a helmet"
 }

CLIで、--projection-expression を指定して実行する。

aws dynamodb get-item \
    --table-name ProductCatalog \
    --key file://key.json \
    --projection-expression "Description, RelatedItems[0], ProductReviews.FiveStar"

★の部分のみ取得できる。

{
    "Item": {
        "Description": {
            "S": "123 description"
        },
        "ProductReviews": {
            "M": {
                "FiveStar": {
                    "L": [
                        {
                            "S": "Excellent! Can't recommend it highly enough! Buy it!"
                        },
                        {
                            "S": "Do yourself a favor and buy this."
                        }
                    ]
                }
            }
        },
        "RelatedItems": {
            "L": [
                {
                    "N": "341"
                }
            ]
        }
    }
}

権限制御に使う(属性レベル制御)

repost.aws

こんなテーブルがあったとする。※上の例とは違うので注意。

GameScoresテーブルの情報がここしかなかった。ドキュメントやサポート回答で利用されるテーブル情報がまとまってるなら欲しい。。。

docs.aws.amazon.com

以下のポリシーでは、WinsLosses は見れないようになっている。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "LimitAccessToSpecificAttributes",
            "Effect": "Allow",
            "Action": [
                "dynamodb:GetItem",
                "dynamodb:Query",
                "dynamodb:BatchGetItem",
                "dynamodb:Scan",
                "dynamodb:TransactGetItems"
            ],
            "Resource": [
                "arn:aws:dynamodb:eu-west-1:123456789012:table/GameScores"
            ],
            "Condition": {
                "ForAllValues:StringEquals": {
                    "dynamodb:Attributes": [
                        "TopScoreDateTime",
                        "TopScore",
                        "UserId",
                        "GameTitle"
                    ]
                },
                "StringEquals": {
                    "dynamodb:Select": "SPECIFIC_ATTRIBUTES"
                }
            }
        }
    ]
}

CLIの例は以下。

# 全部の属性を取得しようとするため AccessDeniedException
aws dynamodb query --table-name GameScores --key-condition-expression "UserId = :useridval" --expression-attribute-values '{":useridval":{"S":"stefano_123"}}'
# Query 成功
aws dynamodb query --table-name GameScores --key-condition-expression "UserId = :useridval" --expression-attribute-values '{":useridval":{"S":"stefano_123"}}' --projection-expression "TopScore, TopScoreDateTime, GameTitle"
# GetItem 成功
aws dynamodb get-item --table-name GameScores --key '{"UserId":{"S":"stefano_123"},"GameTitle":{"S":"Game Zero"}}' --projection-expression "UserId, GameTitle, TopScore, TopScoreDateTime"

ユースケース

で、どんな時にこんな使い方できるのかな?とちょっと考えてみた。たぶん、「ゲームランキング一覧生成のために全員分のデータを取得する必要はあるが詳細な情報は取得しないようにしたい」みたいなケースかな。

となると、項目レベルの制御で「自分のレコードは全部読めるけど、他人のレコードは概要情報だけ」みたいな制御がしたくなってくる。

項目レベルの制御はここら辺に詳しく書いてあるが、詳細は必要になったら読もう。

docs.aws.amazon.com

docs.aws.amazon.com