web-dev-qa-db-ja.com

Firestoreのセキュリティルール:更新時にrequest.resource.data。<prop>はどうなりますか?

私のチームは最近これについて話し合っており、実際の/意図された動作を確実に判断できないようです:

次のようなセキュリティルールがある場合:

match /categories/{document=**} {
    allow update: if request.auth.uid != null
    && request.resource.data.firstName is string
    && request.resource.data.lastName is string;
}

そして、次のデータを使用して、フロントエンドから/ categories /への更新ステートメントを作成します。

{
   firstName: 'A valid firstName'
}

次に、セキュリティルールは合格または不合格になるはずですか?

リファレンスドキュメント では、

開発者が提供するデータは、フィールドと値を含むマップであるrequest.resource.dataに表示されます。リソースに存在する、リクエストで提供されていないフィールドは、request.resource.dataに追加されます

関連する質問:

  1. これは、成功/失敗がノード内の既存のデータに依存することを意味しますか?
  2. 誰かがセキュリティルールで指定されていないデータで更新しようとするとどうなりますか、つまり{age: 28}
  3. 更新データを検証するための推奨される方法は何ですか?

質問3と詳細(スキーマの質問)次のようなモデルがあるとします。

interface Category {
  firstName: string;
  lastName: string;
  age?: int;
  groupId?: string;
}

次のようなセキュリティルールを作成します。

match /categories/{document=**} {
    allow update: if request.auth.uid != null
    && request.resource.data.firstName is string
    && request.resource.data.lastName is string;
    && request.resource.data.age is int;
    && request.resource.data.groupId is string;
}

次に、私が理解しているように、次のシナリオがあります。 enter image description here

これらのシナリオのどれもなしは、オプションのプロパティとうまく適合します。すべてのプロパティを提供する必要がある場合(シナリオ1のように)は、実際にはオプションのプロパティではないためです。そして、シナリオ2のようにそれらを提供しないと、失敗します。

たぶん私はここで何か、Firestoreに書き込まれているオプションのプロパティでデータを検証する方法の基本的なガイドを見逃しています

次のようなオプションパラメータのセキュリティルール:

match /categories/{document=**} {
   allow update: if request.auth.uid != null
   && request.resource.data.firstName is string
   && request.resource.data.lastName is string;
   && request.resource.data.age is int; // ignore if NOT provided
   && request.resource.data.groupId is string; // ignore if NOT provided
}
15
DauleDK

次に、セキュリティルールは合格または不合格になるはずですか?

更新されるドキュメントにすでにlastNameフィールドがあり、このフィールドがstringである場合、その更新は成功します。 (request.auth.uid != nullがtrueを返すように、認証中にこの更新を実行していると想定しています)

関連する質問への回答:

  1. はい、ノード上の既存のデータに依存する場合があります。
  2. そのドキュメントにすでにfirstNameおよびlastNameセットが含まれている場合、ageフィールドの追加は成功します。ルールは、これら2つの値が文字列であるかどうかのみをチェックすることに注意してください。文書に2つを超えるフィールドを含めることができないことは指定していません。
  3. その質問は少し広範に聞こえます(ちょっと XY問題 と思います)。実行しようとしている更新の検証の種類を説明してください。あなたの質問に基づいて、私はあなたが何をしようとしているのかすでにわかっていますが、100%確実にしたいと思います。

更新

更新された質問3からわかったことは、ユーザーが姓と名の両方を入力した場合にのみドキュメントを更新することです。 ageとgroupIdはオプションです。

これを行うには、request.resource.data.firstNameを使用して、このresource.data.firstName != request.resource.data.firstNameがデータベースにすでに存在しないかどうかを確認できます。したがって、セキュリティルールは次のようになります。

match /categories/{document=**} {
   allow update: if request.auth.uid != null
   && (request.resource.data.firstName is string && resource.data.firstName != request.resource.data.firstName)
   && (request.resource.data.lastName is string && resource.data.firstName != request.resource.data.firstName)
   && request.resource.data.age is int
   && request.resource.data.groupId is string
}

これらのルールにより、このデータでの更新は失敗します:

{
   firstName: 'A valid firstName'
}

これら3つは成功しますが、

{
   firstName: 'A valid firstName',
   lastName: 'A valid lastName'
}

{
   firstName: 'A valid firstName',
   lastName: 'A valid lastName',
   age: 20
}

{
   firstName: 'A valid firstName',
   lastName: 'A valid lastName',
   age: 20,
   groupId: 'groupId'
}

Update 2:ageおよびgroupIdをオプションのフィールドとして使用するには、OR演算子を使用しますhasAll()関数を使用して、リクエストに次のフィールドがあるかどうかを確認します。

match /categories/{document=**} {
   allow update: if request.auth.uid != null
   && (request.resource.data.firstName is string && resource.data.firstName != request.resource.data.firstName)
   && (request.resource.data.lastName is string && resource.data.firstName != request.resource.data.firstName)
   || (request.resource.data.keys().hasAll(['age']) && request.resource.data.age is int)
   || (request.resource.data.keys().hasAll(['groupId']) && request.resource.data.groupId is string)
}