Firestore の Security Rule を理解する

firebaseは気軽に使えるデータベースですが、セキュリティルールはRDSなんかとは全然違うので、改めて勉強しました。尚、ドキュメントの内容を要約しただけになってます。

firebase プロジェクト

firebase cli でプロジェクトをつくっていることが前提です。
すでにつくっている場合でも下記コマンドで今回必要な設定ファイルを作成してれたりします。

1
$ firebase init

色々聞かれますが override? と聞かれたらもちろん No で。
今回必要なファイルは firebase.rules になります。

作成した後に、

1
$ firebase deploy --only firestore:rules

とすれば、ルールをアップロードしてくれます。
firebaseの管理画面で設定することも可能ですが、 gitレポジトリで管理したいし、エディタで書いた方が間違いがないと思うので、この方法で。

  

基本

大事なのは match 以下 だけ。
この例では posts コレクションの読み書きを全員に許可します。

js
1
2
3
4
5
6
7
service cloud.firestore {
  match /databases/{database}/documents {
    match /posts/ {
      allow read, write: if true;
    }
  }
}

 
シンプルでそのままなのですが、
:if xxx 以降が true であれば、 read, write を許可するという意味です。

1
allow read, write: if true;

  
よくやるのは、ユーザーがログインしていれば、書き込み許可。

1
allow write: if request.auth.uid != null;

  
この部分はおまじないです。
将来、複数のDBを持てるようになるかもしれないので、こういう構造にしてるそうです。
現状では、1つのプロジェクトに持てるDBは1つだけなので、ただの約束事として覚えましょう。

js
1
2
3
4
5
service cloud.firestore {
  match /databases/{database}/documents {
  ...
  }
}

 

read, write 以外のルール

readgetlist を内包しており、
writecreate update delete を含んでいます。
それぞれ別々に書くことももちろんできます。

  

ワイルドカード

単一リソースのワイルドカード

1
match /posts/{post}

これは posts 以下のすべてのドキュメントにマッチしますが、
posts 以下にコメントなどがネストされている場合は、コメントのサブコレクションにはマッチしません。

1
match /posts/{document=**}

これだと posts 以下なんでもマッチします。

  

複数マッチする場合

以下の例だと、最初のルールで、全員に対して読み書きを禁止しているのですが、下のルールで許可しています。
こういうふうに複数マッチする条件が設定されている場合、ひとつでも条件が true であれば、許可されます。上書きできるので、便利かもしれませんが、あまりやりすぎると管理が大変かと。

js
1
2
3
4
5
6
7
8
9
10
service cloud.firestore {
  match /databases/{database}/documents {
    match /posts/{post} {
      allow read, write: if false;
    }
    match /posts/{document=**} {
      allow read, write: if true;
    }
  }
}

  

条件を書く

セキュリティルールは、ここがメインですね。
:if xxx 以下の部分です。
どういう条件の時に、指定したアクションを許可するのかを設定していきます。

ログインしている時、書き込みを許可

js
1
2
3
    match /posts/{post} {
      allow read, write: if request.auth.uid != null;
    }

自分のプロフィールページにアクセスすることを許可

js
1
2
3
    match /users/{userId} {
      allow read: if request.auth.uid == userId;
    }

公開中の記事のみアクセスを許可

resource.data は DBに保存されている記事が入っています。

js
1
2
3
4
5
  match /databases/{database}/documents {
    match /posts/{post} {
      allow read: if resource.data.isPublished == true';
    }
  }

投稿記事のタイトルが空白でなければ書き込みを許可

こちらは投稿中で、まだDB内に存在しない場合。
add() なんかでデータ書き込みを試みている時ですね。
request.resource.data に自分が投げているリクエストの記事データが入っています。

js
1
2
3
4
    match /posts/{post} {
      allow create: if request.resource.data.title != null
                    && request.resource.data.title != '';
    }

   

他のデータを参照して許可するかどうか決める

ちょっと複雑になりますが、よく使います。
request / resource / auth だけだと判断できない場合は、DB内を検索することができます。
下の例では、

  • create: users コレクションにログイン中のユーザーIDが存在したら許可
  • delete: users 該当するユーザーが管理権限を持っていたら許可

といった感じです。

js
1
2
3
4
    match /posts/{post} {
      allow create: if exists(/databases/$(database)/documents/users/$(request.auth.uid))
      allow delete: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true
    }

  

関数を定義してまとめる

1
if request.auth.uid != null;

なんかはとてもよく使うので、関数として定義しておくと便利です。

js
1
2
3
4
5
6
7
    function isSignedIn() {
      return request.auth.uid != null;
    }

    match /posts/{post} {
      allow read, write: if isSignedIn();
    }

ただし、関数は複雑なことはできません。
JavaScriptのように見えますが、別言語です。
先程の get() / exists() が使えたり resource / request にアクセスできます。
変数を定義したりはできません。ワンライナーでリターンしないといけないとのこと。

  

もっと複雑な権限管理をしたい場合

usersドキュメントにこんな感じのデータを用意して、
先程の関数を駆使してごにょごにょすると良いです。

js
1
2
3
4
5
6
7
  roles: {
    alice: "owner",
    bob: "reader",
    david: "writer",
    jane: "commenter"
    // ...
  }

詳しくはこちらを読むと、どういう処理なのか分かります。
https://firebase.google.com/docs/firestore/solutions/role-based-access

参考

今回のドキュメント
Secure Data in Cloud Firestore

セキュリティを変更したら、こちらも見ておくことをお勧めします。
じゃないとデータを検索した時にハマるかも。
Securely Query Data

resource の中身。
rules.firestore.Resource

  
デバッグがしづらく、ハマったら一度頭を冷やさないと永遠と時間が取られるのが難点ですね。良いデバッグ方法があったら是非教えてくださいー。

それでは。

何かありましたらコメントをどうぞ

comments powered by Disqus

人気の記事

950 Points チリ出身のギタリストが弾くドラゴンボールZがむちゃくちゃかっこいい…
774 Points Wordpress + Heroku + PostgreSQL + Amazon S3 = ¥0 / 無料でサイト運営
700 Points Rubyのチートシート 変数 / クラス / モジュール
524 Points Rubyのチートシート / アクティブサポート
451 Points 紙のデザイナーがウェブ開発できるようになるまでに必要なこと
435 Points Rails / Google Analyticsのデータを使って分析や管理画面のためのグラフをつくる
323 Points RailsとHerokuでノーティフィケーションをプッシュする / PusherとTurbolinksの兼ね合い
222 Points Rails / RSpec テスト書いたことない メンドクサイ(n´Д`)という時のチートシート
193 Points Rails / Ajaxを使って画面遷移しない一時保存機能をつける
193 Points Protractorでスクレイピングしてみた