TL;DR
firestoreのセキュリティルールはやっぱり難しい
ルールを変えたらクエリも考え直す必要が出てくる
RealTimeDatabaseのドキュメントも読むべき
やろうとしたこと
各documentの中に、閲覧許可を与えられている人のメールアドレスを保持しておき、
そこを変更することで、document毎に自由に閲覧権限を振れるようにしたかった。
例えば、以下のようにcollectionの中にdocumentが保存されていたとすると、
test1@test.comのユーザでアクセスした時には全てのドキュメントが見れて、
test2@test.comのユーザでアクセスした場合には"ドキュメント1"と"ドキュメント3"だけが見れるようにしたかった。
{
"name": "ドキュメント1",
"emails": ["test1@test.com", "test2@test.com"]
},
{
"name": "ドキュメント2",
"emails": ["test1@test.com"]
},
{
"name": "ドキュメント3",
"emails": []
}
公式のドキュメントを読むと、やりたいこと自体は達成できそうだったので、やって見た。
service cloud.firestore {
match /databases/{database}/documents {
function hasPermission(emails) {
return request.auth.token.email in emails || emails == [];
}
function isSignedIn() {
return request.auth != null;
}
match /{collection}/{documentId} {
allow read: if isSignedIn() && hasPermission(resource.data.emails);
allow write: if isSignedIn();
}
}
}
シミュレータで確認したところ、想定通りの動きをしてくれたので、ルールをデプロイして見た。
公開した変更が反映されるまでには最大 1 分ほどかかることがあります
すると、こんなメッセージが出て来たので、2,3分待ってからどうなったかを確認した。
Missing or insufficient permissions.
ん?シミュレータと言ってることが違う。。。?
色々とルールを見直したり、フロント側の実装を見直して見たものの
どこが間違っているのか分からない。。。orz
気分転換に昼寝をしてからリトライしてみると、何故か想定通りにデータがとれる。???
さっきとコードは何も変えてないぞ???
錯乱しながら調べているとこんなことが書いてある記事を見つけた。
firebase deploy --only firestore:rules を実行すると、反映されます。大抵は数十秒以内で反映されますが、仕様的には最大10分ほどかかるので、正確に書いたつもりなのに意図通り動かない場合は未反映も疑ってしばらく待つのも良いです🍵
もしかして、セキュリティルールが反映されるのは段階的なのか?
もしそうなら、hasPermissionとして書いた部分だけ反映が遅れて弾かれていた
と言われれば、何と無く納得できる。
今はルールを変えてもすぐに反映されているみたいなので、再現性がなくて困る。
複数のアカウントでデータを追加したり消したりして見たところ、再び、
Error: Missing or insufficient permissions.
が出て来た。
これはもしかして、自分が権限を持たないデータにアクセスした時に
そこだけ弾くのではなく、そのクエリ自体を拒否されてしまうのか!
ということでクエリを投げる段階で、自分が許可を持っている範囲に限定して飛ばしてみると
無事データが取れた!
まとめ
多分、自分が権限を持っていないcollection, documentを含む形でクエリを投げてしまうと、
クエリの実行自体が失敗してしまうので、whereで絞ってクエリを投げる必要がある。
今回の場合だと
.where("emails", "array-contains", email)
や
.where("emails", "==", [])
をクエリにつければ解決した。
あれ?ORって使えたっけ?
TODO
- emailsを編集できる人の権限もadminに設定されたユーザに限定しておきたい