😺

【AWS】Amazon Verified Permissions のワークショップを試す【AVP】

2023/11/05に公開

以下のワークショップを試しただけの記事。

https://catalog.workshops.aws/verified-permissions-in-action/en-US

Lab 1 - Amazon Verified Permissions Console Demo

ポリシーストア(箱)を作成する。

ポリシーストアは、Cedar ポリシー、スキーマ、およびプリンシパルソースを格納する論理的なコンテナです。承認リクエストはポリシーストアに対して行われ、承認決定の間にポリシーストアに存在するポリシーのみが考慮されます。

Cedar ポリシーを作成する

permit (principal, action, resource)
when { principal == resource.owner };

以下のポリシー例を見ると permit 部分で条件記載できそうな雰囲気であるが、
属性ベースのアクセス制御は When に記載が必要とのこと。

permit(
  principal == User::"alice", 
  action == Action::"view", 
  resource == Photo::"VacationPhoto94.jpg"
);

ポリシーの検証

テストベンチで検証できる

LIST#000002 は拒否

owner が bob だから。

{
  "identifier": {
      "entityType": "List",
      "entityId": "LIST#000002"
  },
  "parents": [],
  "attributes": {
      "listId": {
          "long": 2
      },
      "description": {
          "string": "Bob's most important tasks"
      },
      "name": {
          "string": "Bob's tasks"
      },
      "nextTaskId": {
          "long": 3
      },
      "owner": {
          "entityIdentifier": {
              "entityType": "User",
              "entityId": "bob"
          }
      }
  }
},

ポリシーストアスキーマの作成

typo ありえるよね。だからスキーマ定義しておきましょうと。

permit (
    principal == Uzer::"alice",  // Oops, should be "User", not "Uzer"
    action == Action::"readList", // Oops, should be "ReadList", not "readList"
    resource == Uzer::"bob" // Oops, "readList" isn't a valid operation on a User
);

ワークショップの JSON を利用してスキーマを定義します。

スキーマ定義しただけでは違反検知ができないので有効化しましょう。

typo ありのポリシーを作成するとエラーになるようになりました。
User や ReadList の間違いじゃない?と教えてくれます。

Unrecognized entity type Uzer, did you mean TinyTodo::User?; Validation error on policy M2SRS9EUgbF8T9cbQm46m3: Unrecognized action id Action::"readList", did you mean TinyTodo::Action::"ReadList"

ポリシーテンプレートの作成

プレースホルダを利用したポリシーが作成できる

テンプレートを使うときには「テンプレートにリンクされたポリシーの作成」を利用する

ポリシーテンプレートを変更すると、それを利用しているポリシーも同様の変更を受ける。
例えば以下では TinyTodo::Action::"ReadTask" を削除している。

ラップアップ

テンプレートで作成したポリシーを削除し、以下のポリシーを追加する。

permit (
    principal,
    action in [
        TinyTodo::Action::"CreateList",
        TinyTodo::Action::"ListLists",
        TinyTodo::Action::"ListSharedLists"
    ],
    resource
);

2 つのポリシーになっていれば OK

Lab 2 - Application Built on Amazon Verified Permissions

TinyTodo のアーキテクチャ概要

Cognito のアクセストークン(jwt)から ユーザープールID と sub を組み合わせた以下を principal として利用する

cognito_user_pool_id|user_sub

Lambda(TinyTodoApiLambda) の環境変数にポリシーストアとポリシーテンプレートのIDを設定する

Cognito の認証

Hosted UI を利用して alice と bob ユーザを作成する

ログインすると Cognito から取得したアクセストークンとIDトークンが表示される

タスクリストへのタスク追加

ユーザ追加時には Cognito の Post Confirmation Lambda で最初のタスクリストが追加されているので、それを確認する。

curl -X GET "$WS_API_ENDPOINT/list/task-lists" \
  -H "Authorization: Bearer $WS_ACCESS_TOKEN_ALICE" | jq
{
  "lists": [
    {
      "id": 1,
      "owner": "us-east-1_bjj5c7gXi|ceabef0d-49d0-463e-8d18-63ecbad3d79a",
      "name": "Starter task list",
      "description": "A list of tasks to get you started with your new project"
    }
  ]
}

タスクリストの中身を確認する

curl -X GET "$WS_API_ENDPOINT/list/tasks?listId=1" \
  -H "Authorization: Bearer $WS_ACCESS_TOKEN_ALICE" | jq
{
  "tasks": [
    {
      "id": 1,
      "name": "Task 1 - Establish Project Scope And Metrics",
      "description": "The first step to writing a project plan is defining what, exactly, your project is—including the project's purpose, parameters, and goals"
    },
    {
      "id": 2,
      "name": "Task 2 - Identify Key Stakeholders",
      "description": "Once you've defined your project scope, purpose, and metrics, it's time to get clear on who you need to bring the project to life—or, in other words, identify the key stakeholders of the project"
    },
    {
      "id": 3,
      "name": "Task 3 - Outline Deliverables",
      "description": "You need to outline all your project's deliverables—the real, tangible outcomes that your project needs to produce in order to be considered a success"
    },
    {
      "id": 4,
      "name": "Task 4 - Develop Tasks",
      "description": "Once you've clearly defined your project deliverables, you can drill down those deliverables into actionable tasks—tasks you can then assign to your team"
    },
    {
      "id": 5,
      "name": "Task 5 - Assign Tasks And Deadlines",
      "description": "Once you've written out all of your tasks, it's time to assign those tasks out to your team—along with deadlines as to when each task needs to be completed"
    },
    {
      "id": 6,
      "name": "Task 6 - Share, Gather Feedback, And Adjust The Project Plan As Necessary",
      "description": "If you want your project plan to be as strong and complete as it can be, it's important to share it with your team—and get their input on how they think it can be improved"
    }
  ]
}

タスクを追加する

curl -X POST "$WS_API_ENDPOINT/task/create" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $WS_ACCESS_TOKEN_ALICE" \
  -d '{
        "listId": 1,
        "name": "Task 7 - Execute The Project Plan",
        "description": "Once you have got your project plan in place, it is time to execute it—and bring your project to life!"
      }' | jq

追加したタスクの ID が返却される

{
  "taskId": 7
}

bob が所有するリストにタスクを追加しようとするとエラーになる

curl -X POST "$WS_API_ENDPOINT/task/create" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $WS_ACCESS_TOKEN_ALICE" \
  -d '{
        "listId": 2,
        "name": "Task 7 - Execute The Project Plan",
        "description": "Once you have got your project plan in place, it is time to execute it—and bring your project to life!"
      }' | jq
{
  "message": "Access denied -- permissions check failed"
}

タスクを共有する

curl -X POST "$WS_API_ENDPOINT/share/create" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $WS_ACCESS_TOKEN_ALICE" \
  -d '{
        "listId": 1,
        "user": "bob",
        "role": "viewer"
      }' | jq

Task list viewer template に沿って以下のポリシーが作成される

permit (
    principal == TinyTodo::User::"us-east-1_bjj5c7gXi|bf3654ba-71fe-4d48-bb09-e32c043ae54d",
    action in [
        TinyTodo::Action::"ReadList",
        TinyTodo::Action::"ListTasks"
    ],
    resource == TinyTodo::List::"1"
);
curl -X POST "$WS_API_ENDPOINT/share/create" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $WS_ACCESS_TOKEN_ALICE" \
  -d '{
        "listId": 1,
        "user": "bob",
        "role": "editor"
      }' | jq

Task list editor template に沿って以下のポリシーが作成される

permit (
    principal == TinyTodo::User::"us-east-1_bjj5c7gXi|bf3654ba-71fe-4d48-bb09-e32c043ae54d",
    action in [
        TinyTodo::Action::"ReadList",
        TinyTodo::Action::"UpdateList",
        TinyTodo::Action::"ListTasks",
        TinyTodo::Action::"CreateTask",
        TinyTodo::Action::"UpdateTask",
        TinyTodo::Action::"DeleteTask"
    ],
    resource == TinyTodo::List::"1"
);

シェアしているリストの一覧

curl -X GET "$WS_API_ENDPOINT/list/shares?listId=1" \
  -H "Authorization: Bearer $WS_ACCESS_TOKEN_ALICE" | jq
{
  "shares": [
    {
      "user": "us-east-1_bjj5c7gXi|bf3654ba-71fe-4d48-bb09-e32c043ae54d",
      "role": "editor"
    },
    {
      "user": "us-east-1_bjj5c7gXi|bf3654ba-71fe-4d48-bb09-e32c043ae54d",
      "role": "viewer"
    }
  ]
}

ユーザと共有されているリストの一覧

curl -X GET "$WS_API_ENDPOINT/list/shared-lists" \
  -H "Authorization: Bearer $WS_ACCESS_TOKEN_ALICE" | jq

共有している側なのでなし

{
  "sharedLists": []
}

Bob 側のトークンをセットし、Bob で API を叩くと

curl -X GET "$WS_API_ENDPOINT/list/shared-lists" \
  -H "Authorization: Bearer $WS_ACCESS_TOKEN_BOB" | jq
{
  "sharedLists": [
    {
      "id": 1,
      "owner": "us-east-1_bjj5c7gXi|ceabef0d-49d0-463e-8d18-63ecbad3d79a",
      "name": "Starter task list",
      "description": "A list of tasks to get you started with your new project",
      "role": "editor"
    },
    {
      "id": 1,
      "owner": "us-east-1_bjj5c7gXi|ceabef0d-49d0-463e-8d18-63ecbad3d79a",
      "name": "Starter task list",
      "description": "A list of tasks to get you started with your new project",
      "role": "viewer"
    }
  ]
}

Lab 3 - Using Identity Sources

Identity Source の作成

Project Alpha

タスクリスト作成

curl -X POST "$WS_API_ENDPOINT/task-list/create" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $WS_ACCESS_TOKEN_ALICE" \
  -d '{
        "name": "Project Alpha",
        "description": "A task list for Project Alpha"
      }' | jq
{
  "listId": 3
}

タスク作成

curl -X POST "$WS_API_ENDPOINT/task/create" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $WS_ACCESS_TOKEN_ALICE" \
  -d '{
        "listId": 3,
        "name": "First task",
        "description": "A new task"
      }' | jq
{
  "taskId": 1
}

Project-Alpha にアサインされた charlie ユーザを作成する
Hosted UI の SignUp から。

カスタム属性の作成

サインアップエクスペリエンスに以下を追加

charlie にカスタム属性を追加

charlie で再ログインしてトークンを再取得
custom:project 属性が追加されている

{
  "at_hash": "lzy-bdlqjMvSTonHGswAHw",
  "sub": "371ddc9c-aa92-43d7-828b-dfbbad1a1114",
  "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_bjj5c7gXi",
  "cognito:username": "charlie",
  "origin_jti": "32c4ff7d-58cd-4f49-b2a8-92ee939e857e",
  "aud": "10tsrrpi8am3aaindatjil93ap",
  "custom:project": "Project-Alpha",
  "token_use": "id",
  "auth_time": 1699016319,
  "exp": 1699019919,
  "iat": 1699016319,
  "jti": "7580fe8b-3e97-469e-8445-4f6dbe8839ad"
}

Cedar ポリシーの作成

charlie でログインした ID トークンを確認する

{
  "at_hash": "twoiJ8ACYAJu4jf7Ad8RCg",
  "sub": "371ddc9c-aa92-43d7-828b-dfbbad1a1114",
  "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_bjj5c7gXi",
  "cognito:username": "charlie",
  "origin_jti": "227a3cdf-52ef-457a-93d0-94cf0c81723f",
  "aud": "10tsrrpi8am3aaindatjil93ap",
  "event_id": "40e99939-8477-4d79-9490-b110c25fd37e",
  "token_use": "id",
  "auth_time": 1699014552,
  "exp": 1699018152,
  "iat": 1699014552,
  "jti": "c2c9a21e-a86b-4a69-ac74-fc2356d731dc"
}

以下のページに Cognito の Claim の属性が Cedar でどのように利用できるかが記載されていました。

https://catalog.us-east-1.prod.workshops.aws/event/dashboard/en-US/workshop/50-lab3-connecting-idp/53-cedar-policy

permit (
    principal,
    action,
    resource in TinyTodo::List::"3"
)
when {
    principal has custom &&
    principal.custom has project &&
    principal.custom.project == "Project-Alpha"
};
      "User": {
        "memberOfTypes": [],
        "shape": {
          "type": "Record",
          "attributes": {
            "custom": {
              "type": "Record",
              "attributes": {
                "project": { "type": "String" }
              }
            }
          }
        }
      },

charlie で ListID 3 の読み取りは NG のまま
isAuthorized を利用しているから。Cogniton の ID トークンの情報を紐付けるには IsAuthorizedWithToken を使う必要があるっぽい。

curl -X GET "$WS_API_ENDPOINT/list/tasks?listId=3" \
  -H "Authorization: Bearer $WS_ACCESS_TOKEN_CHARLIE" | jq
{
  "message": "Access denied -- permissions check failed"
}

アプリケーションの更新

TinyTodoApiLambda を以下のように変更

更新されたアプリケーションのテスト

curl -X GET "$WS_API_ENDPOINT/list/tasks?listId=3" \
  -H "Authorization: Bearer $WS_ACCESS_TOKEN_CHARLIE" \
  -H "ID-Token: $WS_ID_TOKEN_CHARLIE" | jq
{
  "tasks": [
    {
      "id": 1,
      "name": "First task",
      "description": "A new task"
    }
  ]
}

ID トークンなしだとカスタム属性が AVP に渡らないので Access Denied

curl -X GET "$WS_API_ENDPOINT/list/tasks?listId=3" \
  -H "Authorization: Bearer $WS_ACCESS_TOKEN_CHARLIE" | jq
{
  "message": "Access denied -- no identity token provided"
}

以上

Discussion