Xamarinで作ったモバイルアプリなどのクライアントから直接 (みんな大好き) DocumentDB にアクセスしたいシナリオがあった場合、
クライアント側のアプリのプログラムに DocumentDB のマスターキーを入れておくのは非常に危険です(ガチのアプリでそんなことする人はいないですよね♪)。
そんな場合、一時的なトークンで認証して DocumentDB にアクセスするのがベターな一例となります。 そんな実装の話です。
Overview
- 準備1:DocumentDBのインスタンスとコレクション作成
- 準備2:ユーザー、パーミッションの作成
- コンソールアプリから動作検証(次回)
- Azure Functionsを使って動作検証(次回)
実装のイメージはこんな感じです(図はこちらから引用)。
1. クライアントは、"Mid-tier service"(いわば認証用のサーバー)にアクセス
(クライアントは、"Mid-tier service"にアクセス時には何らかのユーザー認証がされている前提)
2. "Mid-tier service"がDocumenDBへアクセスし、リソーストークンを取得
3. "Mid-tier service"が取得したリソーストークンをクライアントに渡す
4. クライアントは、リソーストークンを使ってDocomentDBにアクセス
リソーストークンは、DocumenDBの特定コレクションやパーティションキーに対して、有効期限と以下2種類の権限を付与することができます。
All : リソースに完全なアクセス許可
(と、本家サイトに書いていますが、コレクション自体の削除はできず、コレクション内のDocumentに対してCRUDができることを検証済みなので、まぁ安全)Read : 対象リソースに対して読み取りのみ可
リソーストークンは、DocumentDBのデータベースに紐づくユーザーに紐づくパーミッションに紐づいて設定することができます。
これについて詳しくは、Work with databases, containers, and items in Azure Cosmos DB | Microsoft Docs が参考になります。
Environment
- Visual Studio 2017 Enterprise (RC)
- DocumenDB Core (v1.0.0)
- .NETCore.App (v1.1.0)
準備1:DocumentDBのインスタンスとコレクション作成
最初に今回のDocumenDBの構成ですが、コレクション(RDBでのテーブル的なもの)はCol1とCo2の2つがあり、2ユーザーを用意して権限を持たせます。
- ユーザー1は、コレクション「Col1」のReadOnly権限を持っている
- ユーザー2は、コレクション「Col2」のAll権限を持っている
(自分の絵心の無さに衝撃を受けました...)
では、Azure Portal からDocumentDBのデータベースを作成します。
この操作はポータルをみてポチポチするだけなので、本家サイトのリンクのみで...(サイト内の手順 1: DocumentDB アカウントを作成するを行うと作成できます)。
Azure Portalで、作成したデータベースのブレードを開き、「キー」をクリックすると、URIやプライマリーキーが確認できます、そしてのちほど使います。
(なお、読み取り専用キーもできているので、保守用に全リソースに対する読み取り権限を与えるならこれを使うってはありかなと。)
構成に合わせてざっくりとstructで定義を作りました。
データベースとコレクションを作成していきます。作成すると課金が発生しますので、自己責任にて気を付けてください♪
(料金についてはこちら)
コレクションの作成
では、2つのコレクションを作りましょう。
CreateCollectionsAsync()
メソッドを実行すると、データベースインスタンスが作られ、その下にCol1
とCol2
というコレクションが2つできます。
価格レベルとスループットを指定していないので、Standardの400で作られます。
テスト用にデータも入れておきましょう。今回はシンプルにKey
とText
のドキュメントを定義して....(サンプルなので、Jsonを「キャメルケースにせんのかいっ!」って突っ込みはなしです)、
サンプルのデータをINSERTします。
AzureのPoralでDocumentDBのブレードを開き、「クエリエクスプローラー」を使って見てみましょう。ちゃんとデータが入ってますね♪
id
やその他諸々のinternal fieldsは自動で作られるんですが、その辺についてまとまったドキュメントがMSDNにない気がしますね。タイムスタンプとか、パーティション分割時用IDとか...今回は重要じゃないのでその話はとばします。
準備2:ユーザー、パーミッションの作成
流れとしては、ユーザーを作っておき、パーミッションを作るときにユーザーに紐づけます。
外部からは、1行目のCreateUsersAndPermissionsAsync
メソッドを呼ぶだけです。
8行目のCreateUsersAsync
メソッドでユーザーを作ります。ユーザーのIdは、事前にstructで登録したものです。その他諸々、インテリセンスを効かせながらパタパタと入力ができるので、structにしたってわけです(他でもこんなのを再利用しているので、書いただけなのですが)。
17行目のCreatePermissionsAsync
メソッドでパーミッションを作ります。
パーミッションは、Permission
クラスが用意されており、必要な値をセットしてあげます。
今回は、最初の構成で説明した通り、ResourceLink
と‘PermissionMode
を指定してあげることで、特定のコレクションに対して特定の権限を付与しています。
- ユーザー1は、コレクション「Col1」のReadOnly権限を持っている
- ユーザー2は、コレクション「Col2」のAll権限を持っている
44行目のRequestOptions
では、ResourceTokenExpirySeconds
プロパティで有効期限を設定することができます。(ただ、検証したところ、20秒とか数分で登録しても設定が有効にならないのは....バグってるんでしょうか....まー今度もう少し検証しよう...)
パーミッションの設定は、27行目、39行目にあるように、Databaseに紐づくユーザーに対して登録します。
(各メソッドで無駄にレスポンスを受け取っているのは、登録したレスポンスの中身をデバッグで見て検証してただけです♪)
さて、動作検証のコーナーに進みたいところですが.....思った以上に長くなったので、続きは次回に....
参考
Work with databases, containers, and items in Azure Cosmos DB | Microsoft Docs
Learn how to secure access to data in Azure Cosmos DB | Microsoft Docs