Firebase

【Firestore】 特定フィールドの更新だけ許可するセキュリティルール

2019/11/02

FirebaseのCloud Firestoreで「特定のフィールドの更新だけを許可したい」場合の、セキュリティルールの書き方を紹介します。

※基礎的なセキュリティルールの書き方は記載しないので、詳しく知りたい人は公式ドキュメントもチェックしてみてください。

Cloud Firestore セキュリティ ルールを使ってみる  |  Firebase

やりたいこと

以下の3つのフィールドを持つドキュメントがあるとします。

  • name
  • address
  • tel

このドキュメントに対して、「name」の更新だけは許可し、それ以外のフィールドの更新はセキュリティルールで弾くように設定したいと思います。

セキュリティルールの書き方

結論から言ってしまうと、以下のようにセキュリティルールを設定すればOKです。(ルールを関数化してますが、このあたりは状況に応じてお好みで)

// 対象を、仮に「users」コレクションとする。
service cloud.firestore {
  match /databases/{database}/documents {

    // 中略

    match /users/{user} {
      allow update: if canUpdateUser();
    }

    // 中略

  }
  function canUpdateUser() {
    return request.resource.data.address == resource.data.address
    	&& request.resource.data.tel == resource.data.tel;
  }
}

ポイントは request.resource.data の部分です。

  • request.resource.data:リクエストされている値を取得できる
  • resource.data:firestore内の値を取得できる

セキュリティルールでは、それぞれ上記のようなデータを参照することが可能です。

ここでは、「name」以外のフィールド(「address」と「tel」)が変更されていないことをチェックしています。

何故このような書き方をするかと言うと、

この「request.resource.data」には、実際に値を投げているかどうかに関わらず、ドキュメントに設定されている全てのフィールドが含まれているからです。

「request.resource.data」の中身

例として、以下のように更新処理行った場合、コード上では「name」以外の値を渡していません。

firebase.firestore().collection('users').doc(userId).update({
  name: newName
})

しかし、firebase側では、以下のような形で値が渡ってきた扱いになっているようです。(何故、このような扱いになるのかは分ってないですが・・・。)

  • name:新しい値
  • address:firestoreの値と同値
  • tel:firestoreの値と同値

なので、「更新を許可したくないフィールド」が変更されていないことをチェックしておけば良いわけです。

少し変な仕様にも思えますが、これで「特定のフィールドの更新だけを許可する」セキュリティルールを記述することができます。

まとめ

当初は「request.resource.data」の中身を分かっていなかったので、結構ハマってしまいました。Interface: List  |  Firebaseのようなメソッドでチェックしようとして、上手くいかなかったり・・・。

同じようなことがしたい方は、参考にしてもらえれば嬉しいです。

それでは、また。