toto11
toto11

Reputation: 1582

Firestore security rules with reference fields

I am a bit stuck here as there is no way to debug those rules. I'd appreciate help with below rules.

I want to access:

/modules/module-id/sessions/session-id/parts/

The comparison with null in the first part of hasCompletedPrerequisiteSession() works well, the second part doesn't!

The path /modules/moduleId/sessions/sessionId/prerequisite points to a reference field.

service cloud.firestore {
    match /databases/{database}/documents {

      function hasCompletedPrerequisiteSession(moduleId,sessionId) {
                // this part works well                                   
        return getPrerequisiteSession(moduleId,sessionId) == null ||
           // !!! this part does not work !!!
           hasCompleted(getPrerequisiteSession(moduleId,sessionId).id);
      }

      function getPrerequisiteSession(moduleId,sessionId) {
        return get(/databases/$(database)/documents/modules/$(moduleId)/sessions/$(sessionId)).data.prerequisite;
      }

      function hasCompleted(sessionId) {
        return exists(/databases/$(database)/documents/progress/$(request.auth.uid)/sessions/$(sessionId));
      }

      match /modules/{moduleId}/sessions/{sessionId}/parts/{partId} {
        allow read: if hasCompletedPrerequisiteSession(moduleId,sessionId);
      }
    }
  }

enter image description here

(If I store the session ID as a string instead of a reference to the session, it works fine.)

Edit

Questions

  1. Reference field in security rules. Assuming modules/moduleId/owner points to a field of the type reference. What is the proper way to get the id of the referenced document?get(../modules/moduleId).data.owner.data.id or get(../modules/moduleId).data.owner or something else?

Upvotes: 11

Views: 2138

Answers (2)

Be Kind
Be Kind

Reputation: 5182

It might be that around getPrerequisiteSession, after using get to pull the object by ref path, you had to use .data first before referencing the id field. Of course, id field needs to be stored as an object field.

For example, in my case I needed to allow user to add a message into a chat only if they're the owner of that chat room. There are 2 "tables" - chats and chat_messages, and chat_messages relate to a specific chat through chatId field. chats objects have ownerId field. The rule I've used goes like this:

    match /chat_messages/{itemId} {
      function isOwner() {
          return get(/databases/$(database)/documents/chats/$(request.resource.data.chatId)).data.ownerId == request.auth.uid;
      }
      
      allow read: if true;
      allow create: if isOwner();
    }

Upvotes: 0

toto11
toto11

Reputation: 1582

From Firebase support:

It seems that in your use case, you want to get the document name (sessionId) from the value of your reference field (prerequisite), unfortunately, this is not currently supported by Firestore security rules. I would suggest that you store only the sessionId as String on your prerequisite field, or you can also add String field for the sessionId. Keep in mind that the exists() and get() functions only allow you to check if a document exists, or retrieve the document at the given path.

Upvotes: 6

Related Questions