YSNHatenaBlog

主にアプリやWebサービス開発について

メタデータを使ってFirebase Storageのデータを特定のユーザ間でシェアする方法

ここのグループ非公開のファイル メタデータを使用する方法。

ユーザーデータを保護する  |  Firebase

1対1で相手に画像を送信したい場合

送信者と受信者のAuth uidがクライアント側で分かる前提。

iOSの場合

let metadata = StorageMetadata()
metadata.contentType = "image/jpeg"
metadata.customMetadata = [
    "sender": <送信者のuid>,
    "receiver": <受信者のuid>
]
let storage = Storage.storage()
let ref = storage.reference().child("share/photo.jpg")
ref.putData(data, metadata: metadata)

Storageのルールはこんな感じ。

service firebase.storage {
  match /b/{bucket}/o {
    match /share {
        match /{allPaths=**} {
        allow write: if request.auth.uid != null && request.resource.metadata["sender"] == request.auth.uid
        allow read: if request.auth.uid != null && (resource.metadata["sender"] == request.auth.uid || resource.metadata["receiver"] == request.auth.uid)
      }
    }
  }
}

Firestoreで管理しているグループ内で共有したい場合

Storageのオブジェクト作成時にCloud Functionsを実行してメタデータを付けてあげる。 例えば送信者の属するグループがFirestoreで管理されており、そのグループのメンバーのみで画像を共有したいとする。

iOSアプリ側のコード。

let metadata = StorageMetadata()
metadata.contentType = "image/jpeg"
metadata.customMetadata = [
    "sender": <送信者のuid>
]
let storage = Storage.storage()
let ref = storage.reference().child("share/photo.jpg")
ref.putData(data, metadata: metadata)

Cloud Functionsのコード。

import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';

admin.initializeApp();

export const storagePermission = functions.storage.bucket().object().onFinalize(async (object, context) => {

    // <必要に応じてパスのチェックなどをする>

    const sender = object.metadata && object.metadata['sender'];

    if (sender && object.name) {

        const uidsInGroup = ... // Firestoreから共有したいユーザのuid配列を取得

        const file = admin.storage().bucket(object.bucket).file(object.name);
        await file.setMetadata({
            metadata: {
                uids: uidsInGroup.join(',')
            }
        }) // カスタムメタデータをセットする際setMetadataの引数オブジェクトにmetadataのキーが必要。
    }
    return
}

Storageのルール。

service firebase.storage {
  match /b/{bucket}/o {
    match /share {
        match /{allPaths=**} {
        allow write: if request.auth.uid != null && request.resource.metadata["sender"] == request.auth.uid && resource.metadata["uids"] == null
        allow read: if request.auth.uid != null && resource.metadata["uids"] != null && request.auth.uid in resource.metadata["uids"].split(",")
      }
    }
  }
}

メタデータにひたすら <uid>: "true" をセットする方法もありそうだが散らかりそう。

追記

このルールだと他のユーザが上げたデータ上書きできちゃうからStorageのパスは考えた方がいいかも。