Search code examples
flutterdartgoogle-cloud-firestorefirebase-security

Firestore rules: unable to verify role in subcollection


I'm having difficulties to setting up my security rules

Data Structure

-- books (collection)
   -- bookId (autogenerated Id - doc) 
      -- {chapter: text}, {chapter: text}, 

userBooks
   -- email (email of user logged in - doc)
      -- books (sub collection)
        -- bookId (referencing bookId doc on collection book)
           -- role: admin or read

The use case is as follow, a user with admin role is the only one permitted to share a book, allowing him to add an entry in books of another user.

created the function trying to achieve that but it does not pass successfully

I'm am logged in as user with email userWantsToShare@gmail.com. When user tries to share his book to addUser@gmail.com I make the following request:

userWantsToShare@gmail.com making the request to write under addUser@gmail.com collection.

final role = {'role': 'edit'};
await FirebaseFirestore.instance
        .collection('userBooks')
        .doc("addUser@gmail.com")
        .collection('books')
        .doc('9KHYZJVBY3BNAlYPYYoA')
        .set(role);

When coming to firebase

this should translate to /userBooks/addUser@gmail.com/books/9KHYZJVBY3BNAlYPYYoA Data {'role': 'edit'}.

match /userBooks/{emailId}/books/{bookId} {
      allow write: if isSharedEmail(bookId);
}

//here im verifying the user that wants to share has admin role and therefore is authorised to write in another user book subcollection
function isSharedEmail(bookId){
//this should translate to /userBooks/userWantToShare@gmail.com/books/9KHYZJVBY3BNAlYPYYoA
   get(/databases/$(database)/documents/userBooks/$(request.auth.token.email)/books/$(bookId)).data.role == "admin" ;
}

As userWantToShare@gmail.com has under that path admin role it should allow the insertion. But most likely I'm omitting something as it does not work at all.

What m I missing?

Thanks in advance


Solution

  • Ok, for anyone making the same dumb mistake as I did. When making the call to the function isSharedEmail was outside the context.

    Meaning that by moving the function to the scope inside the context was enough to make it work

    Before: not working

    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        
        match /userBooks/{email} {
          allow write: if ( isAppShared())
          allow read: if true;
        }
      } 
    }
      
    function isAppShared() {
      return  get(/databases/$(database)/documents/userBooks/$(request.auth.token.email)).data.bookId == request.resource.data.bookId; 
    }
    

    After: Working

    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        
        match /userBooks/{email} {
          allow write: if ( isAppShared())
          allow read: if true;
        }
        
        function isAppShared() {
          return  get(/databases/$(database)/documents/userBooks/$(request.auth.token.email)).data.bookId == request.resource.data.bookId; 
        }
      } 
    }