Elle Bishop
Elle Bishop

Reputation: 461

How to encounter cloud firestore error despite adding security rules

I am a beginner in Flutter x Firebase and been stuck here for last three days. Pls help me resolve this, and explain the reason as well

/Firestore(30190): (25.0.0) [Firestore]: Listen for Query(target=Query( collectionGroup=leaves where status==pending order by name);limitType=LIMIT_TO_FIRST) failed: Status{code=PERMISSION_DENIED, description=Missing or insufficient permissions., cause=null}

my code:

import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';

class AdminDashboardScreen extends StatefulWidget {
  const AdminDashboardScreen({super.key});

  @override
  _AdminDashboardScreenState createState() => _AdminDashboardScreenState();
}

class _AdminDashboardScreenState extends State<AdminDashboardScreen> {
  bool isAdmin = false;
  Future<bool> _isAdmin(String uid) async {
    var doc = await FirebaseFirestore.instance.collection('admins').doc(uid).get();
    return doc.exists;
  }

  @override
  void initState() {
    super.initState();
    _checkIfAdmin();
  }

  Future<void> _checkIfAdmin() async {
    String uid = FirebaseAuth.instance.currentUser!.uid;
    bool admin = await _isAdmin(uid);
    setState(() {
      isAdmin = admin;
    });
  }

  @override
  Widget build(BuildContext context) {
    if (!isAdmin) {
      return Scaffold(
        appBar: AppBar(
          title: Text('Admin Panel'),
        ),
        body: Center(child: Text('You do not have admin privileges.')),
      );
    }
return Scaffold(
  appBar: AppBar(
    title: Text('Admin Panel'),
  ),
  body: StreamBuilder(
    stream: FirebaseFirestore.instance.collectionGroup('leaves')
        .where('status', isEqualTo: 'pending').snapshots(),
    builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
      if (snapshot.connectionState == ConnectionState.waiting) {
        return Center(child: CircularProgressIndicator());
      }
      if (snapshot.hasError) {
        return Center(child: Text('Error: ${snapshot.error}'));
      }
      if (!snapshot.hasData || snapshot.data!.docs.isEmpty) {
        return Center(child: Text('No pending leave requests.'));
      }

      var leaveRequests = snapshot.data!.docs;
      return ListView.builder(
        itemCount: leaveRequests.length,
        itemBuilder: (context, index) {
          var leave = leaveRequests[index];
          return ListTile(
            title: Text('${leave['studentName']} is requesting for a leave'),
            subtitle: Text('Reason: ${leave['reason']}'),
            trailing: Row(
              mainAxisSize: MainAxisSize.min,
              children: [
                IconButton(
                  icon: Icon(Icons.check),
                  onPressed: () => _updateLeaveStatus(leave, 'approved'),
                ),
                IconButton(
                  icon: Icon(Icons.close),
                  onPressed: () => _updateLeaveStatus(leave, 'rejected'),
                ),
              ],
            ),
          );
        },
      );
    },
  ),
);

}

void _updateLeaveStatus(QueryDocumentSnapshot leave, String status) async {
    try {
      await leave.reference.update({'status': status});
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Leave request $status')));
    } catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Error updating status: $e')));
    }
  }
}

my database security rules:

    rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Allow only authenticated users to read/write their own documents
    match /students/{studentId}/{documents=**} {
      allow read, write: if request.auth != null && request.auth.uid == studentId;
    }

    // Admin collection
    match /admins/{adminId} {
      allow read, write: if request.auth != null && request.auth.uid == adminId;
    }

    // Allow admins to read and write any student data
    match /students/{studentId}/{documents=**} {
      allow read, write: if request.auth != null && isAdmin(request.auth.uid);
    }
  }
}

Upvotes: 0

Views: 49

Answers (1)

Frank van Puffelen
Frank van Puffelen

Reputation: 599716

Your security rules are also calling an isAdmin function here:

match /students/{studentId}/{documents=**} {
  allow read, write: if request.auth != null && isAdmin(request.auth.uid);
}

That isAdmin isn't defined inside the security rules that you show, so that call will fail. Keep in mind that your security rules run somewhere else than your Flutter code, so the rules can't call the function in your Flutter code.

If you want the security rules to be able to test if somebody is an admin, you will have to write an isAdmin implementation in the rules too.

Upvotes: 0

Related Questions