Setting permissions for document and all its subcollections - firebase

I am getting the error:
FirebaseError: Missing or insufficient permissions when trying to view a document in a subcollection. I'm trying to get at the documents 'character' in subcollection 'settings'.
systemsCol()
.doc(localStorage.getItem("systemId"))
.collection('settings')
.doc('character')
How can i set all items and subdocuments for each 'system' document?
Here are my permissions
service cloud.firestore {
function authed (auth, data){
return auth.uid == data.owner.uid || auth.token.email in data.collaborators;
}
match /databases/{database}/documents {
match /systems {
allow read, write:
if request.auth.uid != null;
match /{systemId} {
allow list, create:
if request.auth.uid != null;
allow get, update, delete:
if authed(request.auth, resource.data);
}
match /{systemId}/{document=**} {
allow read, write:
if authed(request.auth, resource.data);
}
}
}
}
Also tried
service cloud.firestore {
function authed (auth, data){
return auth.uid == data.owner.uid || auth.token.email in data.collaborators;
}
match /databases/{database}/documents {
match /systems {
allow read, write:
if request.auth.uid != null;
match /{systemId} {
allow list, create:
if request.auth.uid != null;
allow get, update, delete:
if authed(request.auth, resource.data);
match /{document=**} {
allow read, write:
if authed(request.auth, get(/systems/{systemId}).data);
}
}
}
}
}

Related

Firebase rules are not applied as supposed to

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()

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.

Firebase security rules - can't get resources with get()

I'm trying to write a rule in firebase security console with get() but I can't get resource data anyhow... I want documents and their subcollections to be readable to user, if user uid is in document field, array or map.
My collection structure:
/boards/(boardId)/...much more
Fields in (boardId) document:
name: "Board name"
ownerId: "MYID12345"
guestsId(array): ["MYID12345"]
guestsMap(map): [MYID12345: true]
Security rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /boards/{boardId=**} {
// rules here
}
}
}
What I have tried so far:
allow read: if get(/databases/$(database)/documents/boards/$(boardId)).data.guestsMap[request.auth.uid] == true;
allow read: if request.auth.uid in get(/databases/$(database)/documents/boards/$(boardId)).data.guestsMap;
allow read: if request.auth.uid in get(/databases/$(database)/documents/boards/$(boardId)).data.guestsMap[true];
allow read: if get(/databases/$(database)/documents/boards/$(boardId)).request.data.guestsId[request.auth.uid];
allow read: if request.auth.uid in get(/databases/$(database)/documents/boards/$(boardId)).data.guestsId;
allow read: if request.auth.uid in get(/databases/$(database)/documents/boards/$(boardId)).request.data.guestsId;
allow read: if get(/databases/$(database)/documents/boards/$(boardId)).data.ownerId == request.auth.uid;
allow read: if get(/databases/$(database)/documents/boards/$(boardId)).data.ownerId == "MYID12345";
allow read: if get(/databases/$(database)/documents/boards/$(boardId)).resource.data.ownerId == request.auth.uid;
allow read: if get(/databases/$(database)/documents/boards/$(boardId)).request.data.ownerId == request.auth.uid;
None of these worked, always getting:
ERROR FirebaseError: Missing or insufficient permissions.
allow read: if true, makes the application run normally.
I'm sticking to the documentation but it's not working for me...
#update
match /boards/{boardId=**} {
allow read: if resource.data.ownerId == request.auth.uid;
}
Also can't use it like this, because then, every subcollection is looking for ownerId field in it's documents, and ownerId or friend list array are only in board document.
#update
I tried to do like so, but it didn't help:
match /boards/{boardId} {
allow read, write: if request.auth.uid in resource.data.guestsId || request.auth.uid == resource.data.ownerId;
allow create: if exists(/databases/$(database)/documents/users/$(request.auth.uid));
function passResource() {
return request.auth.uid in resource.data.guestsId || request.auth.uid == resource.data.ownerId;
}
match /categoryList/{categoryId} {
allow read, write: if passResource();
}
...
}
What am I missing here?
Okay I found a working solution:
match /databases/{database}/documents {
match /boards/{boardId} {
allow read: if request.auth.uid in resource.data.guestsId || request.auth.uid == resource.data.ownerId;
allow write: if request.auth.uid == resource.data.ownerId;
allow create: if exists(/databases/$(database)/documents/users/$(request.auth.uid));
function isAllowed() {
return request.auth.uid in get(/databases/$(database)/documents/boards/$(boardId)).data.guestsId || request.auth.uid == get(/databases/$(database)/documents/boards/$(boardId)).data.ownerId;
}
match /categoryList/{category} {
allow read, write: if isAllowed();
match /taskList/{task} {
allow read, write: if isAllowed();
}
}
...
}
Also funny it didn't want to work like this:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /boards/{boardId=**} {
allow read, write: if request.auth.uid in get(/databases/$(database)/documents/boards/$(boardId)).data.guestsId || request.auth.uid == get(/databases/$(database)/documents/boards/$(boardId)).data.ownerId;
allow create: if exists(/databases/$(database)/documents/users/$(request.auth.uid));
}
}
because:
Error: simulator.rules line [5], column [49]. Property guestsId is undefined on object.

Getting 'FirebaseError: Missing or insufficient permissions.' from onsnapshot error when user has the correct permissions

I am trying to create a document reference, set up an onsnapshot listener, save the document and then upload a file which will trigger a cloud function which will make updates to the document i am listening to. But onSnapshot gives a permissions error of 'FirebaseError: Missing or insufficient permissions.' after the snapshot has run once (I guess for the initial state).
I have tried running simulations of accessing and writing the data in the firebase console and it works without any errors
const db = window.firebase.firestore()
const newBaseRef = db.collection('base').doc()
newBaseRef.onSnapshot(doc => {
console.log('Current data: ', doc.data())
}, function (error) {
throw error // THIS ALWAYS GETS HIT
})
newBaseRef.set({
uid: window.firebase.auth().currentUser.uid,
createdAt: window.firebase.firestore.FieldValue.serverTimestamp()
})
here are my security rules
service cloud.firestore {
match /databases/{database}/documents {
match /printset/{document=**} {
allow read, update, delete: if request.auth.uid == resource.data.uid
allow create: if request.auth.uid != null;
}
match /file/{document=**} {
allow read, update, delete: if request.auth.uid == resource.data.uid
allow create: if request.auth.uid != null;
}
match /base/{document=**} {
allow read, update, delete: if request.auth.uid == resource.data.uid
allow create: if request.auth.uid != null;
}
}
}
I dont expect the error callback to run
newBaseRef.set() return Promise.
So when newBaseRef.onSnapshot() is called, newBaseRef.data().uid doesn't set yet.
See:
https://firebase.google.com/docs/reference/js/firebase.firestore.DocumentReference#set
You should call newBaseRef.onSnapshot() after Promise.resolve().
const db = window.firebase.firestore()
const newBaseRef = db.collection('base').doc()
newBaseRef.set({
uid: window.firebase.auth().currentUser.uid,
createdAt: window.firebase.firestore.FieldValue.serverTimestamp()
}).then(() => {
newBaseRef.onSnapshot(doc => {
console.log('Current data: ', doc.data())
}, function (error) {
throw error // THIS ALWAYS GETS HIT
})
})
And more.
If you want to only Insert then you should use newBaseRef.add({}).
If you want to Insert or DeleteInsert(Replace All Data) then you should use newBaseRef.set({}).
If you want to InsertUpdate or Update then you should use newBaseRef.set({}, {merge, true}).
If you want to only Update then you should use newBaseRef.update({}).
If you want to InsertUpdate or Update then change your security rules to the following setting.
service cloud.firestore {
match /databases/{database}/documents {
match /printset/{document=**} {
allow read, update, delete: if request.auth.uid == resource.data.uid
allow create: if request.auth.uid != null;
}
match /file/{document=**} {
allow read, update, delete: if request.auth.uid == resource.data.uid
allow create: if request.auth.uid != null;
}
match /base/{document=**} {
allow read, delete: if request.auth.uid == resource.data.uid
allow update: if resource == null || request.auth.uid == resource.data.uid
allow create: if request.auth.uid != null;
}
}
}

Firestore rules on subcollection

I have a "game" collection on firestore with a "levels" sub-collection. I'm trying to set-up the security rules so that you can only access game or level you created.
All documents (games or levels) have an authorId field with the uid of the user that created them. I have try this rule but still got an Missing or insufficient permissions error:
service cloud.firestore {
match /databases/{database}/documents {
match /games/{document=**} {
allow read, write: if document.data.authorId == request.auth.uid;
}
}
}
What am I missing?
I have tried the following rules too with no success:
service cloud.firestore {
match /databases/{database}/documents {
match /games/{game}/levels/{level} {
allow read, write: if level.data.authorId == request.auth.uid;
}
}
}
service cloud.firestore {
match /games/{game} {
allow read, write: if game.data.authorId == request.auth.uid;
match /levels/{level} {
allow read, write: if level.data.authorId == request.auth.uid;
}
}
}
According to the reference documentation, resource is the object that contains the document data that the user is trying to write. You use its data property to get a hold of its field values.
service cloud.firestore {
match /databases/{database}/documents {
match /games/{document=**} {
allow read, write: if resource.data.authorId == request.auth.uid;
}
}
}
match /databases/{database}/documents {
match /users/{uid} {
allow read, write: if request.auth.uid == uid;
}
match /data/{uid}/temperature
/{document=**}{
allow read, write: if request.auth.uid == uid;
}
match /data/{uid}/blood_pressure
/{document=**}{
allow read, write: if request.auth.uid == uid;
}
}
}
I did this to access subcollections "blood_pressure" and "temperature" for only authenticated users. It works fine for me.

Resources