どういうことか
以下のようなドキュメントの時
{
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行目で、update
とincrementするフィールド
以外を更新しようとするリクエストを弾いたつもりだった。
ドキュメントに何も状態が保存されていないときなら、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使えよって話はあると思うのですが、私クレカ持ってないので使えない。
セキュリティルール師みたいになってしまったと反省しております。