Search code examples
swiftfirebasegoogle-cloud-firestorefirebase-security

Firebase Firestore rules for shared content


I'm developing a simple app that can upload photos. I'm using firebase firestore and storage. Every time I upload a new photo I create an object like this:

struct Item: Codable, Identifiable {
    var id: UUID // photo name
    var uidOwner: String // uid user owner
    var emailOwner: String // email user owner
    var path: String // photo path
    var shared: [String] = []
}

For now I'm just using this rules for checks that the user email is it contained in the "shared" data of every document.

enter rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /photos/{photo} {
    allow read if: resource.data.emailOwner in get(/databases/$(database)/documents/photos/$(photo)).data.shared;
}}}

In Swift project I take this query:

enter func getItems() async throws -> [Item] {
    do {
        let querySnapshot = try await photoCollection
            .whereField("emailOwner", arrayContains: user.email ?? "[email protected]")
            .getDocuments()
        let items = try querySnapshot.documents.compactMap { doc in
            return try doc.data(as: Item.self)
        }
        return items
    } catch {
        throw error
    }
}

I'm logging in with an email contained within the shared array in a document. Why doesn't that use have access?


Solution

  • Recognize that "Security Rules are not filters". The security rules do not look at the documents being returned by a request to determine the validity of the request. They look at the REQUEST itself to determine if the REQUEST is valid or not.

    Your rule (if using Puf's answer) says:

    allow read if: resource.data.emailOwner in resource.data.shared;
    

    but your query is:

    .whereField("emailOwner", arrayContains: user.email ?? "[email protected]")
    

    So your query is says "if the array emailOwner contains the following value (user.email ?? [email protected])"

    Your query is looking at in emailOwner but your rule is looking at in shared.