1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Firestoreのセキュリティルールで1づつ加算することだけを許可する

Last updated at Posted at 2022-08-04

どういうことか

以下のようなドキュメントの時

{
  aaa: 0,
  xxx: 12,
  nnn: 42,
}

それぞれのフィールドを1づつ加算することのみを許可したい。

どうやるか

ルールに、どのフィールドをアップデートするかを伝えるために、updateフィールドを追加する。
上のxxxに1加算するのであれば、

{
  aaa: increment(1),
  update: "aaa",
}

というデータを送信することにすることで、どのフィールドが更新されるかをルールに伝える。

失敗した例

私は、request.resourceには、update関数に渡したものが入ってくるのだと勘違いしていた。
公式ドキュメントにも書いてある通り、request.resourceには、
オペレーション後の保留中のドキュメントの状態が含まれます、とのことである。

これを知らずに、私はこのように書いた。

allow update: if request.resource.data.size() == 2 &&
    request.resource.data[request.resource.data.update] 
      == (resource!=null 
      && resource.data.keys().hasAny([request.resource.data.update])
      ? resource.data[request.resource.data.update] : 0) + 1;

1行目で、updateincrementするフィールド以外を更新しようとするリクエストを弾いたつもりだった。

ドキュメントに何も状態が保存されていないときなら、request.resource.dataのサイズは2だが、
ドキュメントにすでに何らかのデータが保存されている状態だと、サイズは「保存されているデータ数」または「それ+1」となる。
なので、このルールでは、2つ目以降のデータを保存しようとすると、すべてリジェクトされる。

どうするか

そこで、何かいい方法がないかと探していると、こんなものを見つけた。

どうやら、マップの差分を返してくれるらしい。これを使うしかなさそうだ。

長くなってきたので、関数にしてしまった。

    function allowUpdate(request,resource){
      let safeResource = resource != null ? resource : {"data":{"update":""}};
      let diff = request.resource.data.diff(safeResource.data);
      let affected = diff.affectedKeys();
      let allowedCount = diff.removedKeys().size() == 0 && 
                           (request.resource.data.update == safeResource.data.update
                           ? affected.size() == 1 : affected.size() == 2);
      let allowedDocument = request.resource.data[request.resource.data.update] 
            == (safeResource!=null 
            && safeResource.data.keys().hasAny([request.resource.data.update])
            ? safeResource.data[request.resource.data.update] : 0) + 1;
      return allowedCount && allowedDocument;
    }

updateフィールドは、前回と同じものになる場合と、ならない場合があると思われたので、
同じになる場合はaffectedの大きさが1、そうでない場合はaffectedの大きさが2であればよい。
よって、こうなった。

最後に

こんな複雑なことするなら最初からFunctions使えよって話はあると思うのですが、私クレカ持ってないので使えない。
セキュリティルール師みたいになってしまったと反省しております。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?