FirebaseのCloud Storageにおいて、同一のファイルパスに対して上書きを禁止するのをルールで定義してみる。

どういうことか

例えば、 /foo ディレクトリ以下に bar.png を配置するとして、

  • /foo 以下に bar.png無い時 → アップロードができる
  • /foo 以下に bar.pngある時 → アップロードができない

という具合に、上書きを禁止したいケースがある。
その場合は、以下のようにルールを記述して適応する。

service firebase.storage {
  match /b/{bucket}/o {
    match /foo/{allPaths=**} {
      allow read: if ...;
      allow create: if request.auth != null && resource == null && request.resource.size < 100 * 1024 * 1024;
    }
  }
}

認証チェックしたり、ファイルの容量確認の条件も含みで書いているが、 要になるのはresource == null。指定のパスに既存のファイルがない場合はresource変数がnullになるので、nullであるならcreateを許可する。
既に既存のファイルが同じパスに存在している場合はresource変数がnullでなくなるので、この場合はルールに則って弾くことができる。

最初ハマった間違い

service firebase.storage {
  match /b/{bucket}/o {
    match /foo/{allPaths=**} {
      allow read: if ...;
      allow create: if resource.name == null; // ... ①
      allow create: if request.resource.name != resource.name; // ... ②
    }
  }
}

一見、①のものでも良さそうに見えるが、resource変数がnullだと、resource.nameundefinedになり、nullとの比較で期待通りにならなくなる。
そしてruleでは、

!resource.name
resource.name === undefined

のようにundefinedであるかどうかをjsのように判別ができないので、気をつけないとハマる。

ならば②であればいけるのでは、、と思うのだが、ruleだとundefinedになる変数にアクセスすると、その条件の評価がerrorとしてみなされるのか、
条件自体が結果的にfalseと解釈されるのでこれも失敗してしまう。
(このあたり、正しく且つ詳しく知っている人が居たら教えて欲しい…)

ので、結果的には最初に示した方法で、既存のパスにファイルが存在しているかをチェックするのが良さそう。

writeではなくcreate?

公式のリファレンスには記載されていないが、writeの権限はcreate,update,deleteに分けることができる。
※投稿時点でのものなので、将来的にリファレンスに追記される、あるいは記述できなくなる可能性があります。

updateではだめなのか?

ファイルをアップロードする場合、PUTとして送信されるため、ルールとして走るのはcreateになってしまう。。
updateで制御できるのは、既に配置されたファイルのmetadata等の情報を更新する場合になる。