Edited at

firestoreのSecurity Ruleを設定して特定のuserのみが投稿を閲覧できるようにする

この記事はFirebase #2 Advent Calendar 2018の記事です。


はじめに

某webマーケティング会社でフロントエンドエンジニアとして働きながら、バンド活動もおこなっているtarooooooooooです。

バンド内での貸し借りを可視化するために「Cashica」というサービスを作っています。

詳細についてはこちらの記事からどうぞnuxt.js + firebase でPWA,SSRな借りパクを撲滅するアプリを作っている話

本記事では、上記サービスを作る際に学んだfirestore設計の知見を記載します。


設計

postsはドキュメントのIDはfirestoreによる自動割り当て、コレクションには貸し借りしているモノの詳細と、貸しているuser、借りているuserへの参照が入っています。

usersはドキュメントのIDにOauth認証の際に取得したuidを使っています。

ここでポイントとなるのが、usersのドキュメントIDを自動割り当てではなく、uidを用いているところになります。

こうしておくことで後々行うSecurity Ruleの設定が楽になります。

easypay – Database – Firebase console.png

easypay – Database – Firebase console (1).png


データの追加

データを追加するコードを見ていきましょう。(一部抜粋です)


userの追加

userにはfirebase.auth().onAuthStateChanged()というメソッドで取得した認証情報が入っています。


db.collection('users').doc(user.uid).set({
uid: user.uid,
name: user.displayName,
email: user.email,
icon: user.photoURL
})


postの追加

from,toはそれぞれ貸すユーザ、借りているユーザのuidが入っています。

db.collection('users').doc(from)のようにフィールドの値を設定すると参照型で保存がされます。

db.collection('posts').add({

from: db.collection('users').doc(from),
to: db.collection('users').doc(to),
description,
deadline,
createTime: firebase.firestore.FieldValue.serverTimestamp()
})


Security Ruleの設定

下記のテンプレートに追加する形でセキュリティルールを記載していきます。


  • postsのドキュメントはフィールド内のto,fromに参照型で保存されているユーザのみが読み取り・更新・削除できる

  • postsの投稿は認証されているユーザであればできる

  • user情報の読み取りは認証されていればできる

  • user情報の書き換えはuser本人のみができる

service cloud.firestore {

match /databases/{database}/documents {
}
}


postsのドキュメントはフィールド内のto,fromに参照型で保存されているユーザのみが読み取り・更新・削除できる



  • request.auth.uidは認証が通っているuserのID、


  • resource.data.toは読み書きしたいドキュメントのtoフィールドの値

usersのドキュメントIDを自動割り当てではなく、uidを用いているため以下のように、読み取りしたいドキュメントのto,fromの参照型の値と認証が通っているユーザへのpathとの比較が可能となります

service cloud.firestore {

match /databases/{database}/documents {
match /posts/{postId} {
allow read, update, delete: if resource.data.to == /databases/$(database)/documents/users/$(request.auth.uid) ||
resource.data.from == /databases/$(database)/documents/users/$(request.auth.uid);
}
}
}


postsの投稿は認証されているユーザであればできる

isAuthenticated()は認証されているかどうかを判断してくれるので下記のように追記します。

service cloud.firestore {

match /databases/{database}/documents {
function isAuthenticated() {
return request.auth != null;
}
match /posts/{postId} {
allow read, update, delete: if resource.data.to == /databases/$(database)/documents/users/$(request.auth.uid) ||
resource.data.from == /databases/$(database)/documents/users/$(request.auth.uid);
allow create: if isAuthenticated();
}
}
}


user情報の読み取りと、作成は認証されていればできる

allow get, create: if isAuthenticated();とすることで可能になります。

service cloud.firestore {

match /databases/{database}/documents {
function isAuthenticated() {
return request.auth != null;
}
match /posts/{postId} {
allow read, update, delete: if resource.data.to == /databases/$(database)/documents/users/$(request.auth.uid) ||
resource.data.from == /databases/$(database)/documents/users/$(request.auth.uid);
allow create: if isAuthenticated();
}
match /users/{userId} {
allow get, create: if isAuthenticated();
}
}
}


user情報の書き換えはuser本人のみができる

request.auth.uid == resource.data.uidで認証が通っているユーザと対象ドキュメントのuidとの比較ができ、同じ場合のみ許可します。

service cloud.firestore {

match /databases/{database}/documents {
function isAuthenticated() {
return request.auth != null;
}
function isUserAuthenticated(userId) {
return request.auth.uid == userId
}
match /posts/{postId} {
allow read, update, delete: if resource.data.to == /databases/$(database)/documents/users/$(request.auth.uid) ||
resource.data.from == /databases/$(database)/documents/users/$(request.auth.uid);
allow create: if isAuthenticated();
}
match /users/{userId} {
allow read: if isAuthenticated();
allow update, delete: if request.auth.uid == resource.data.uid;
}
}
}


おわりに

初めはセキュリティルールの設定のことを想定していなかったので、ルールを設定する段階でデータ構造の見直しが必要になってしまったので、初めからある程度想定しておいた方が幸せな気がしました。

サービスのDEMOとGitHubリポジトリは下記です。

GitHub: https://github.com/taroodr/cashica

Demo: https://easypay-dd04a.firebaseapp.com


参考

https://medium.com/google-cloud-jp/firestore2-920ac799345c

https://tech-blog.sgr-ksmt.org/2018/12/11/194022/

https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ja

https://firebase.google.com/docs/firestore/security/get-started?hl=ja