Firebase rules are not applied as supposed to - firebase

I am having weird behaviors with my Firebase Firestore Rules recently.
I am protecting a collection like so:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function getRoles() {
return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role;
}
function isAdmin() {
return 'admin' in getRoles();
}
match /registrationsRequests/{document} {
allow create: if true;
allow read, update, delete: if false;
}
match /users/{document} {
allow read: if request.auth.uid == document || isAdmin();
allow write: if request.auth.uid == document || isAdmin();
}
}
}
After I log in (and I check if the user is available with its UID), I fetch the user. But I receive the following error (both checks should return true => isAdmin() and document)
FirebaseError: Missing or insufficient permissions.
#edit: The query used
const usrDoc = await this.db.collection('users').doc(user.uid).ref.get()

Related

Firestore document RBAC- FirebaseError: Missing or insufficient permissions

I'm trying to restrict access to a collection based on a users "role" on the document as well as whether they're signed in.
Just checking if they are signed in works:
rules_version = '2';
function isSignedIn() {
return request.auth != null;
}
service cloud.firestore {
match /databases/{database}/documents {
match /workspaces/{workspace} {
allow read: if isSignedIn();
}
}
}
But I want to make it a bit more granular, I'm following this Google guide. When I configure it exactly the same my frontend client errors with FirebaseError: Missing or insufficient permissions. However, testing the rule in the Firebase portal works(?).
rules_version = '2';
function isSignedIn() {
return request.auth != null;
}
function getRole(rsc) {
return rsc.data.access[request.auth.uid];
}
function isOneOfRoles(rsc, array) {
return isSignedIn() && (getRole(rsc) in array);
}
service cloud.firestore {
match /databases/{database}/documents {
match /workspaces/{workspace} {
allow read: if isOneOfRoles(resource, ["owner", "viewer"]);
}
}
}
As mentioned, testing the rule via Firebase works, however when querying from my frontend app it fails.
// VueJS
let workspace = []
async fetchWorkspaces() {
const db = firebase.firestore();
await db.collection('workspaces').get().then(response => {
response.forEach(snapshot => {
this.workspaces.push(snapshot.data());
})
})
}
Database document
I've also tried storing the RBAC for each user as a document in a subcollection and using the following rule
allow read: if isSignedIn() && exists(/databases/$(database)/documents/workspaces/$(workspace)/users/$(request.auth.uid));
Still doesn't work. I can only seem to grant broad access (is signed in)

Firestore Rules

I have the following rules in my Firestore database. But I still keep getting a notification from Firestore that the rules I set in my database are not secure. Please see the codes below. Any suggestions or recommendations to make the database more secure?
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if true;
allow write: if userIsAdmin();
}
match /Basket/{Basket} {
allow read, update, delete: if userOwnPost();
allow create: if request.auth.uid != null;
}
match /AllOrders/{AllOrders} {
allow read, create, update: if userOwnPost();
}
match /Items/{Items} {
allow update: if userOwnPost();
}
match /Voucher/{Voucher} {
allow update: if userOwnPost();
}
match /User/{User} {
allow read, update: if userOwnPost();
allow create: if request.auth.uid != null;
}
function userIsAdmin() {
return getUserData().userRole == 'Admin';
}
function getUserData() {
return get(/databases/$(database)/documents/User/$(request.auth.uid)).data;
}
function userOwnPost() {
return getUserData().objectId == request.auth.uid;
}
}
}
You have some overlapping match statements in your rules:
With
match /{document=**} {
allow read: if true;
allow write: if userIsAdmin();
}
you allow read access on all documents in your Firestore database.
As explained in the doc (section "Overlapping match statements"), "in the case where multiple allow expressions match a request, the access is allowed if any of the conditions is true".
So all your other security rules are just overlapped by this one.

How can I make firebase rules that allow auth users to read all documents but only create their own user document not giving themselves admin rights?

I am struggling with the firebase security rules, I can get parts of it to work, but when I try to connect it all together I am having issues.
I would like to have my rules do the following:
Allow read to all documents if authenticated
Allow create or update user document by authenticated only at document /databases/$(database)/documents/users/$(request.auth.uid) but not add admin to the roles array
!("admin" in getAfter(/databases/$(database)/documents/users/$(request.auth.uid)).data.roles)
Allow only getRole(“admin”) == true to create, edit, or delete any user document and any other document
function getRole(role) {
return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.roles.hasAny([role]);
}
Here is what I have that does not include the users being able to create their own user account. It works to allow only admin to write any document.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function getRole(role) {
return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.roles.hasAny([role]);
}
match /{document=**} {
allow read: if true;
allow write: if getRole('admin') == true;
}
}
}
Here is what I tried to add to allow users to create their user document. It seems to be not cascading to the next rule, it tries the getRole in the match /{document=**} path and finds that the user is not an admin so it fails. I have tried reordering and placing the /users/ path above and it goes thru that path fine then does the same thing and fails on the getRole in the /{document=**} path again. I also tried specifying the document names rather than using the wildcard and that seems to not allow any get or write. Can you please point me in the right direction?
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function getRole(role) {
return exists(/databases/$(database)/documents/users/$(request.auth.uid)) && get(/databases/$(database)/documents/users/$(request.auth.uid)).data.roles.hasAny([role]);
}
match /{document=**} {
allow read: if true;
allow write: if getRole('admin') == true;
}
match /users/{userId}{
allow read: if request.auth.uid != null;
allow create: if getRole('admin') == true || request.auth.uid == userId &&
!(getAfter(/databases/$(database)/documents/users/$(request.auth.uid)).data.roles.hasAny(["admin"]));
allow update: if getRole('admin') == true || request.auth.uid == userId && exists(/databases/$(database)/documents/users/$(request.auth.uid)) == true && !(getAfter(/databases/$(database)/documents/users/$(request.auth.uid)).data.roles.hasAny(["admin"]));
}
}
}
It turns out that I can't allow admin to dynamically create collections that are not defined in the security rules unless I use the match /{document=**} which applies those rules to every path and is not the desired result. I was able to get the rules setup to accomplish the other parts pretty easily as follows:
service cloud.firestore {
match /databases/{database}/documents {
function getRole(role) {
return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.roles.hasAny([role]);
}
match /users/{userId} {
allow read: if request.auth.uid != null;
allow create: if request.auth.uid == userId && request.resource.data.roles.hasAny(["admin"]) == false;
}
match /collectionName/{collectionNameId} {
allow read: if request.auth.uid != null;
allow write: if request.auth.uid != null && getRole('admin') == true;
}
}
}

Insufficient permissions clientside while simulator works

I have the following firestore rules setup where I want to let anyone create a document in the suggestions collection, but only a certain user can update it. The rules look like this:
service cloud.firestore {
match /databases/{database}/documents {
match /suggestions/{sugg} {
allow create, read;
allow update: if request.auth.uid == 'abc123';
}
}
}
When I test this in the provided simulator, it works fine; however after waiting 30 minutes I test in my deployed app and I get the error:
Error: Missing or insufficient permissions.
In my app I'm doing an add() call on the suggestions collection. So in my rules where I specify allow create, that should be sufficient. I added read in case the returned document would count as a read.
Client: (AngularFire)
this.afs.collection('suggestions').add(sugg).then(() => {
this.submitted = true;
}, err => console.error('Firebase error:', err));
The issue was that Firestore couldn't try to match a uid when request.auth was null. The solution was to add some functions:
service cloud.firestore {
match /databases/{database}/documents {
function isSignedIn() {
return request.auth != null;
}
function isAdmin() {
return isSignedIn() && request.auth.uid == 'abc123';
}
match /suggestions/{sugg} {
allow create, read;
allow update: if request.auth.uid == 'abc123';
}
}
}

Missing or insufficient permissions firestore database rules

I'm self studying firestore and I could not figure out a way to only allow a user to update, delete or read only the collections added by them.
This is the structure I'm using:
I use firebase auth for user handing. I save the currentUser.uid as user_id in the database for each collection.
These are the rules I'm using
service cloud.firestore {
match /databases/{database}/documents {
match /tasks{
allow read, update, delete: if request.auth.uid == resource.data.user_id;
allow create: if request.auth.uid != null;
}
}
When I try to read/get the data I get Missing or insufficient permissions error.
I'm using the web api (JavaScript) for firestore. This is the code I'm using to read data.
function read() {
db.collection("tasks").get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
var newLI = document.createElement('li');
newLI.appendChild(document.createTextNode(doc.data().task));
dataList.appendChild(newLI);
});
});
}
the error was in my JavaScript I was getting all without filtering by user
function read() {
let taskColletion = db.collection("tasks");
taskColletion.where("user_id", "==", firebase.auth().currentUser.uid).get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
var newLI = document.createElement('li');
newLI.appendChild(document.createTextNode(doc.data().task));
dataList.appendChild(newLI);
});
});
}
This is actually explained on the Firestore Documentation(I recommend reading it).
You're missing a wildcard after /tasks:
service cloud.firestore {
match /databases/{database}/documents {
match /tasks/{task} {
allow read, update, delete: if request.auth.uid == resource.data.user_id;
allow create: if request.auth.uid != null;
}
}
}

Resources