firestore security ruleの知識を棚卸し
ドキュメント
- https://firebase.google.com/docs/rules?hl=ja
- Cloud Storage のセキュリティールールも一緒に見れる
- 属性ベースのアクセスとロールベースのアクセス
- https://firebase.google.com/docs/firestore/security/overview?hl=ja
- https://firebase.google.com/docs/firestore/solutions/role-based-access?hl=ja#data_structure
- 文書作成コラボレーションアプリ
- https://cloud.google.com/firestore/docs/security/get-started?hl=ja
- google cloudのドキュメント
- https://firebase.google.com/docs/reference/rules/rules
- API Reference
- The Firebase Blog
サンプルコード
- https://github.com/firebase/quickstart-testing/tree/master/unit-test-security-rules
- https://github.com/FirebaseExtended/codelab-rules/blob/main/final-state/firestore.rules
- https://github.com/firebase/friendlyeats-web/
roleの設計
カスタムクレームをつかう
get()などで発生するコストがかからない
でもfunctions(admin sdk)じゃないと変更できない
チャットアプリのような場合だと、ルームのメンバーの権限をそこそこ変えることがありそうだが、
反映させる場合はidtokenを取得しなおさないといけないから若干手間
https://firebase.google.com/docs/auth/admin/custom-claims?hl=ja#propagate_custom_claims_to_the_client
※クライアントへカスタム クレームを伝播する
ただ下記の様な方法もあるから全てをカスタムクレームで済ませる必要はなさそう
https://firebase.google.com/docs/firestore/solutions/role-based-access?hl=ja
カスタムクレームを使わない
firestoreのどこかに保存する
この場合は、roleを確認するのにget()しないといけないのでコストがかかる
アクセス呼び出しと料金 https://firebase.google.com/docs/firestore/security/rules-conditions?hl=ja#access_calls_and_pricing
案1. usersのフィールドにrolesを持たせる
users/user_abc { fcmTokens:[] roles : [commenter, editor] }
- アプリ側でrolesを使わないような場合は、モデルがアプリと管理画面で別々になる
- セキュリティールール
'editor' in get(/.../users/uid).data.roles
- アプリ側で間違ってsetした時エラーにしないといけない。そうしないとフィールドが消える
- flutter側。例えば管理画面で更新ボタンを表示・非表示する場合。後で記述するパターンと比べるとnullチェックが多め
isEditor() async { final snap = await FirebaseFirestore.instance.doc('users/user_abc').get(); final data = snap.data(); if (data == null) return false; // ここがnullなことはほとんどないと思うけど念の為 final List? roles = data['roles']; if (roles == null) return false; return roles.contains('editor'); }
上の様なコードを下記の様なボタンの出しわけで使う想定
if (user.isEditor()) ElevatedButton(onPressed: () => db.update, child: Text('更新'));
案2. usersのサブコレクションにrolesを持たせる
collectionGroup()しないならメリットが思いつかない。 collectionGroupでwhere検索する場合はインデックスを作成しないといけない(沢山作ると料金が発生)。 ただ、そんなケースがあるのか?
users/user_abc/roles/role { editor: true, reader: true }
- セキュリティルール
get(/.../users/uid/roles/role).data.editor
- クライアント側のコード
isEditor() async { final snap = await FirebaseFirestore.instance.doc('users/users_abc/roles/role').get(); return snap.data() != null && snap.data()!['editor'] == true; }
案3. usersのサブコレクションにrolesを持たせ、ドキュメントIDはrole名
users/user_abc/roles/editor:{ isAvailable: true } users/user_abc/roles/commenter:{ isAvailable: true }
いろいろやってみましたが、メリットなさそう。
案3. rolesコレクションを作って、ドキュメントIDはUID
roles/uid { editor: true, reader: true }
- セキュリティルール
get(/.../roles/uid).data.editor
- クライアント側
isEditor() async { final snap = await FirebaseFirestore.instance.doc('roles/user_abc').get(); return snap.data() != null && snap.data()!['editor'] == true; }
なかなか悪くない。usersが巨大化したら分けるのもあり。
その他
最近powershellから起動しているせいかセキュリティールールを更新しても読み込んでくれない時がある。 その場合は、一旦停止してから、再度起動。
rules-unit-testingが2.0.0から大きく変わる
initializeAdminApp()がなくなる。結局内部はadmin sdkなので自分で作りだせはする。
v1系ではまだ存在する