Skip to content

debug() statement in firestore rules does not write out to firestore-debug.log #8407

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Turburlar opened this issue Apr 6, 2025 · 6 comments

Comments

@Turburlar
Copy link

Turburlar commented Apr 6, 2025

[REQUIRED] Environment info

Firebase CLI 13.31.2

Running on Flutter web in Chrome
cloud_firestore: ^5.6.6

Framework • revision c236373904 (3 weeks ago) • 2025-03-13 16:17:06 -0400
Engine • revision 18b71d647a
Tools • Dart 3.7.2 • DevTools 2.42.3

[REQUIRED] Test case

To reproduce, run the following flutter app main...

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';

void main() async {
  await Firebase.initializeApp(
    options: const FirebaseOptions(
      apiKey: "your-cred",
      projectId: "your-cred",
      storageBucket: "your-cred",
      messagingSenderId: "your-cred",
      appId: "your-cred",
    ),
  );
  FirebaseFirestore.instance.useFirestoreEmulator('localhost', 8080);
  await FirebaseAuth.instance.useAuthEmulator('localhost', 9099);
  final querySnapshot = await (FirebaseFirestore.instance.collection('invites').where('recipientUid', isEqualTo: "anything")).get();
}

With the following rules...

service cloud.firestore {
  match /databases/{database}/documents {
    // Default deny all
    match /{document=**} {
      allow read, write: if false;
    }
    
    // Check if user is authenticated
    function isAuthenticated() {
      return request.auth != null;
    }
    
    // Check if user is accessing their own data
    function isOwner(userId) {
      return request.auth.uid == userId;
    }
    
    // Public user data collection
    match /publicUserData/{userId} {
      allow read: if isAuthenticated();
      allow create: if isAuthenticated() && isOwner(userId);
      allow update, delete: if isAuthenticated() && isOwner(userId);
    }
    
    // Sports leagues
    match /sportsLeagues/{leagueId} {
      allow read: if isAuthenticated();
      allow write: if isAuthenticated() && isAdmin();
      
      match /teams/{teamId} {
        allow read: if isAuthenticated();
        allow write: if isAuthenticated() && isAdmin();
      }
    }

    // Memberships collection
    match /memberships/{membershipId} {
      allow read: if isAuthenticated();
      allow create, update: if isAuthenticated() && request.resource.data.uid == request.auth.uid;
      allow delete: if false;
    }

    // Invites collection
    match /invites/{inviteId} {
      allow read, delete: if isAuthenticated() && 
       debug(/databases/$(database)/documents/memberships/$(inviteId));
      allow create: if isAuthenticated() && isPickGroupAdmin(buildMembershipIdFromInvite(request));
      allow update: if false;
    }

    function isInviteRecipient(request) {
      return request.auth.uid == request.resource.data.recipientUid;
    }

    function buildMembershipIdFromInvite(request) {
      return request.resource.data.senderUid+'-'+request.resource.data.groupId;
    }

    // Pick groups collection
    match /pickGroups/{groupId} {
      allow read: if isAuthenticated(); // this cannot be made more strict as long as PickGroupService checks if name is unique
      allow create: if isAuthenticated();
      allow update: if isAuthenticated() && isPickGroupAdmin(request.auth.uid+'-'+request.resource.data.groupId);
      allow delete: if false;
    }

    function isPickGroupAdmin(id) {
      return exists(/databases/$(database)/documents/memberships/$(id)) &&
        get(/databases/$(database)/documents/memberships/$(id)).data.role == 'admin';
    }

    function isAdmin() {
      return exists(/databases/$(database)/documents/admins/$(request.auth.uid));
    }
  }
}

In this case the expected behaviour is that the path object would be written out in the firestore-debug.log.

@google-oss-bot
Copy link
Contributor

This issue does not seem to follow the issue template. Make sure you provide all the required information.

@aalej
Copy link
Contributor

aalej commented Apr 7, 2025

Hey @Turburlar, thanks for the detailed report! The firestore.rules file seems to be a bit complex, but reading through it there’s only 1 debug.

// Invites collection
    match /invites/{inviteId} {
      allow read, delete: if isAuthenticated() && 
       debug(/databases/$(database)/documents/memberships/$(inviteId));
      allow create: if isAuthenticated() && isPickGroupAdmin(buildMembershipIdFromInvite(request));
      allow update: if false;
    }

Let me know if I may be wrong here, but did you mean to first get the data in /databases/$(database)/documents/memberships/$(inviteId) to evaluate it in the rules? If so, could you try updating this section of your rules to something like:

// Invites collection
    match /invites/{inviteId} {
      allow read, delete: if isAuthenticated() && 
       debug(get(/databases/$(database)/documents/memberships/$(inviteId)).data) != null;
      allow create: if isAuthenticated() && isPickGroupAdmin(buildMembershipIdFromInvite(request));
      allow update: if false;
    }

After updating the rules, could you try running your app again to see if debug() would now work? If it does not work, could you share your firestore-debug.log?

Also, using the code snippet you provided, it doesn’t seem like the && condition in allow read, delete: if isAuthenticated() && debug(/databases/$(database)/documents/memberships/$(inviteId)); would run, since the user is never authenticated. Consider authenticating the user first, or temporarily removing the isAuthenticated() condition to see if debug() would work.

@aalej aalej added the Needs: Author Feedback Issues awaiting author feedback label Apr 7, 2025
@Turburlar
Copy link
Author

@aalej I will consider the example you gave. But my goal of using the debug() function is to print out the path inside the debug function to troubleshoot. Alternatively, I could just try printing out the value of inviteId.

@google-oss-bot google-oss-bot added Needs: Attention and removed Needs: Author Feedback Issues awaiting author feedback labels Apr 7, 2025
@aalej
Copy link
Contributor

aalej commented Apr 9, 2025

Thanks for clarifying your use case @Turburlar. Since permission statements expect a boolean value, if you're trying to print out the path, you could try something similar to:

      allow read, write: if debug(/databases/$(database)/documents/memberships/$(inviteId)) != null;

This will write an output to the firestore-debug.log

path_value {
  segments {
    simple: "databases"
  }
  segments {
    simple: "(default)"
  }
  segments {
    simple: "documents"
  }
  segments {
    simple: "memberships"
  }
  segments {
    simple: "random-invite-id-1234"
  }
}

Could you give this a try? Let me know in case this is not what you're looking for.

@aalej aalej added Needs: Author Feedback Issues awaiting author feedback and removed Needs: Attention labels Apr 9, 2025
@google-oss-bot
Copy link
Contributor

Hey @Turburlar. We need more information to resolve this issue but there hasn't been an update in 7 weekdays. I'm marking the issue as stale and if there are no new updates in the next 3 days I will close it automatically.

If you have more information that will help us get to the bottom of this, just add a comment!

@google-oss-bot
Copy link
Contributor

Since there haven't been any recent updates here, I am going to close this issue.

@Turburlar if you're still experiencing this problem and want to continue the discussion just leave a comment here and we are happy to re-open this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants