Does anyone have their heads around this auth business? I have nurses (users) and patients and I want to give the nurses permission on some patient records.
Now (discarding everything i know about relational databases) I've put permissions under each patient, where each permission has ID of the user/nurse
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write:
if get(/databases/$(database)/documents/
patients/89QL8XXXXFf/
permissions/KZztXXXXXRf1)!=null;
}
}
}
..ok got it...
service cloud.firestore {
match /databases/{database}/documents{
//define the database variable, use it later as $(database)
match /patients/{patientId}{
//define variable patientId, use later as $(patientId)
allow read, write:
// if exists(/databases/$(database)/documents //WORKS
// /patients/$(patientId)
// /permissions/$(request.auth.uid))
if get(/databases/$(database)/documents //WORKS
/patients/$(patientId)
/permissions/$(request.auth.uid)).data.write == true;
//you have to get(...).data.myProperty
}
// match /{document=**} {
// allow read,write: if false;
// }
}
}
Related
In Firestore, I have a collection "form1"
In my client app, when I create a document in a collection that doesn't exist, say "form2"
db.collection("form2").addDocument(data: data)...
I see from Firestore console, "form2" was created automatically to hold the new document. I hope addDocument() would return error in this case.
How to do it with Security rules? or with other method?
Here is my current Secuirty rules:
rules_version = '12';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} { // only logged-in user can access
allow read, write: if request.auth.uid != null;
}
}
}
Why can't following work? (insufficient permission even if the root collection exist)
service cloud.firestore {
match /databases/{database}/documents {
match /{collection} {
allow read, write: if false;
}
match /{collection}/{document} {
allow read, write: if exists(/databases/$(database)/documents/$(collection));
}
}
}
You can disallow writing to all documents as default and then write rules to allow the only ones you decide:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;// This disallow write and read for all documents
}
match /admin_/** {
allow read, write: if request.auth.token.admin == true;
}
}
}
This will not allow writes to any collection or document except to admin
i have the following sample app here: Github repo
It uses vuefire in ChatList.vue
// vuefire firestore component manages the real-time stream to that reactive data property.
firestore() {
return {
chats: db.collection('chats').where('members', 'array-contains', this.uid)
}
},
I now wrote security rules to secure the data, but can't seem to get the combination of vuefire and security rules to work:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;
}
// THIS IS THE PART I'D LIKE TO REMOVE
match /chats/{chatId=**} {
allow read: if request.auth.uid != null;
}
// THIS WORKS AS INTENDED, AND I'D LIKE TO INCLUDE "READ"
match /chats/{chatId}/{documents=**} {
allow write: if chatRoomPermission(chatId)
}
function chatRoomPermission(chatId) {
return request.auth.uid in get(/databases/$(database)/documents/chats/$(chatId)).data.members;
}
}
}
So the goal is: make the individual chats only readable and writable to users that are in the members array in firestore. (Currently i achieved this partially, since all chats are readable to anyone, but only writable to users in the members array.)
Do i have to rewrite the vuefire component so i can have the following security rule? (It gives an error message: listing of chats not possible due to missing permissions)
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;
}
match /chats/{chatId}/{documents=**} {
allow read, write: if chatRoomPermission(chatId)
}
function chatRoomPermission(chatId) {
return request.auth.uid in get(/databases/$(database)/documents/chats/$(chatId)).data.members;
}
}
}
For completeness, the working solution is (credits to Renaud Tarnec):
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;
}
match /chats/{chatId=**} {
allow read: if request.auth.uid in resource.data.members;
}
match /chats/{chatId}/{documents=**} {
allow read, write: if chatRoomPermission(chatId)
}
function chatRoomPermission(chatId) {
return request.auth.uid in get(/databases/$(database)/documents/chats/$(chatId)).data.members;
}
}
}
Since you want to check, in your Security Rules, if a given value (the user uid in this case) is contained in a field of type Array in your document, you can use the in operator of the List type.
So, the following should do the trick:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;
}
// THIS IS THE PART I'D LIKE TO REMOVE
match /chats/{chatId=**} {
allow read: if request.auth.uid in resource.data.members;
}
// ....
}
}
I have the following rule in my Firestore
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId}/{documents=**} {
// Only the authenticated user who authored the document can read or write
allow read: if request.auth.uid == userId;
allow write;
}
}
}
which doesn't seem to work and i am using Rest API to get the data
For authentication I call:
https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key=[API_KEY]
Once authenticated we get the idToken and pass as Authorization header for the next URL
https://firestore.googleapis.com/v1beta1/projects//databases/(default)/documents/users
The users collection has the id as the document name and the value is just a bunch of dummy keys.
When I run the client the error I get is
{u'status': u'PERMISSION_DENIED', u'message': u'Missing or insufficient permissions.', u'code': 403}
If i hardcode the value of the userid it works. So the value returned in {userid} does not seem to match the UID for some reason.
Can someone please help decode why this is happening?
Thanks
Rams
you don't need the document=** selector
service cloud.firestore {
match /databases/{database}/documents {
// dissallow all access
match /{documents=**} {
allow read, write: if false;
}
// Make sure the uid of the requesting user matches name of the user
// document. The wildcard expression {userId} makes the userId variable
// available in rules.
match /users/{userId} {
allow read, update, delete: if request.auth.uid == userId;
allow create: if request.auth.uid != null;
}
}
}
https://firebase.google.com/docs/firestore/security/rules-conditions
I use a collection called "admin" in Firestore to define which users can write new documents (image below).
At moment, it is controled just by software. I would like to add rules to Firestore. I tried the rule below but it didn't work. What would be the correct rules in that case ?
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if request.auth != null;
allow write: if get(/admin/{anyDocument}).data.userId == request.auth.uid;
}
}
}
I'd recommend instead having a users collection with an admin field that can be set to true/false. Then you can do something like:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if request.auth != null;
allow write: if get(/users/${request.auth.uid}).data.admin == true;
}
}
}
As far i know this is not possible with your current database structure. Because the push key is not accessible in firestore rules unless it is with in the admin node.
One way is to save the admin with their uid as key like admin/userID/data...
now you can access it
allow write: if get(/databases/$(database)/documents/admin/$(request.auth.uid)).data.userId == request.auth.uid;;
I have the next firestore rule:
service cloud.firestore {
match /databases/{database}/documents {
match /{usuarios=**} {
allow write: if get(/usuarios/$(request.auth.uid)).data.level == 0 || get(/usuarios/$(request.auth.uid)).level == 0;
}
}
}
And I get "permission-denied" when I tried this query:
firebase.firestore().collection('usuarios').doc(uid).set({...});
This is my DB actually:
pd: I want to add info about new users in my db (by his UID)
That's going to be very expensive and inefficient since you pay per get request. Instead, you should write your rules as if you're defining the database's structure. Here's what I mean:
service cloud.firestore {
match /databases/{database}/documents {
match /usuarios/{uid} {
// Give write access to any field within the document who's id is the uid
// Add other validation as needed.
allow write: if uid == request.auth.uid
// Give admin access to anyone who's `level == 0`
// Make sure to add the `databases...` boilerplate
|| get(/databases/$(database)/documents/usuarios/$(request.auth.uid)).data.level == 0;
// If necessary, give access to sub-collections.
// (We can't do it for uid or it will become a path object that we can't use as a string.)
/{sub=**} {
allow write: if uid == request.auth.uid;
}
}
}
}
If you'd like to see fully flushed-out rules that are doing something very similar, I have an open source exemple. Cheers!