Firestore rules based on path component and other collection - firebase

I am trying to establish a rules, where user would only be able to perform operations on Chat messages after they have made a purchase of one of the services.
My database structure looks like this:
I have purchases collection: purchases/{purchaseId} which contains buyer_id field.
I also have messages collection: /channels/{purchaseId}/thread/{threadId}.
I want to allow CRUD operations if the users in thread are the same as buyer_id from purchases collection, in purchaseId document.
This is what I've tried, using current user's ID (auth) for now. This doesn't work, either.
Ideally, I would substitute request.auth.uid with the field from the document I am trying to access, but this would do for now.
match /channels/{purchaseId}/thread/{threadId} {
allow read, create, update, delete: if get(/databases/{database}/documents/purchases/{purchaseId}).data.buyer_id == request.auth.uid;
}
I am getting the standard permissions error. What am I missing?

You syntax is wrong when defining the path. Try this:
match /channels/{purchaseId}/thread/{threadId} {
allow read, write: if get(/databases/$(database)/documents/purchases/$(purchaseId)).data.buyer_id == request.auth.uid;
}
Substituting request.auth.uid with the field from the document:
match /channels/{purchaseId}/thread/{threadId} {
allow read, write: if get(/databases/$(database)/documents/purchases/$(purchaseId)).data.buyer_id == resource.data.buyer_id;
}

Related

How to add a Firebase security rule for checking if logged in users email is in a list

I try to add a security rule that should grant write access given that the logged in user is in a list within the record.
I.e. my "tournament" record has a List officials in where I put the emails of those that should be able to alter the tournament.
I don't get this to work so I guess that my rule has some error in it.
match /tournaments {
allow read: if request.auth.uid != null;
allow write: if request.auth.token.email in resource.data['officials'];
}
The rule you have written is has some missing syntax. The rule must be like mentioned below:
match /tournaments/{tournamentID} {
allow read: if request.auth.uid != null;
allow write: if request.auth.token.email in resource.data['officials'];
}
The /tournaments/{tournamentID} indicates that the rule will apply for all documents present in tournament collection and {doc_id} is a wild card to representing documents in that collection.

Firestore rules allow subcollection

My db structure is like this:
//Sub collections
/inventory/{inventoryId}/armor/chest/
/inventory/{inventoryId}/armor/head/
...
// Document
/inventory/{inventoryId}.ownerUID // ownerUID = firebaseID
/inventory/{inventoryId}.charName // Character name that owns this inventory, each user can own multiple characters, each character has one inventory linked to it
Probably not relevant:
/characters/{charName}.ownerUID
/characters/{charName}.charName
/characters/{charName}.inventoryID
I'm trying to write the rules so each user can only read/write inventories that belong to him, for the top document in inventory I can just write something like:
match /inventory/{inventoryID}/{document=**} {
allow read,write: if request.auth != null && resource.data.ownerUID == request.auth.uid
}
However, this will fail for nested collection as the resource.data.ownerUID only exists at the top level.
Is there a way I can get {inventoryID} from /inventory/{inventoryID}/{document=**} and check it against firebaseID or maybe somehow use the data from /character/
Is my only option adding ownerUID to every subcollection of /inventory?
If you need to use fields from other documents than the one that matches the match pattern, you can use get() to read that document and use its fields. For example:
match /inventory/{inventoryID}/{document=**} {
allow read, write: if
get(/databases/$(database)/documents/inventory/$(inventoryID)).data.ownerUID
== request.auth.uid;
}

Access userId without having it as a field

I'm writing Firestore security rules for my project. I want to allow users to edit information in their own user page, but not in anyone else's. Right now I don't save userId as a field in each user, only as the reference to the user document. I know how to access fields in each user, but not the reference to them. See picture:
match /Users/{document} {
allow update: if request.auth.uid == userId; //how do I reach the userId without having it as a field
}
I do not want to add userId as a field in each user, there must be an easy way of accessing the path.
As mentioned in the Firestore docs you get the document id from the match query.
In your case this would be document from match /Users/{document}. You could also rename this query to match /Users/{userId} to make it work.
Check the second example in the documentation on using authentication information in security rules:
Another common pattern is to make sure users can only read and write their own data:
service cloud.firestore {
match /databases/{database}/documents {
// 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;
}
}
}
So in your case that'd be if request.auth.uid == document.

firebase - setting specific fields as private

I want to set up certain fields to be private on my user profiles. I have a user documents with name, email, etc but I want to make the gold field read only as I plan to use a cloud function to update this value when a user makes an in app purchase. I've not done in app purchases before so this is the only way I can think of doing it.
I understand I can use wildcard vars in the path when using Firestore security rules, however as far as I'm aware, I can only use wildcard vars in place of the documents and collections.
You are correct that wildards can only be used to identify collections and documents, but not fields. However one option you could have is to create an additional 'private collection' which you could secure with the standard security rules. For example -
users
user1
email
name
gold
user1
goldValue
Then in your security could look something like -
service cloud.firestore {
match /databases/{database}/documents {
match /gold/{userId} {
allow read, update, delete: if request.auth.uid == userId
allow create: if request.auth.uid != null;
}
match /users/{document=**} {
allow read;
allow write: if request.auth.uid != null;
}
}
}

Configuring rules for Firestore so user only gets records they own

This is a followup to this question
Firestore permissions
I'm trying to set rules on my firestore
service cloud.firestore {
match /databases/{database}/documents {
match /analysis/{analysis} {
allow read, write: if request.auth.uid == resource.data.owner_uid;
}
}
}
My goal is
a. When doing a list operation only those documents belonging to a user are returned
b. only documents a user owns can be read or written by that user.
With the above configuration b. is accomplished.
how do I do accomplish a. ?
Remember that firestore rules are not filters, they're a server-side validation of your queries.
You should always make your queries match your rules, or else you'll get permission errors.
In your case you already made the rule to enforce reading/listing on user owned documents. Now you simply have to make the corresponding query with the right filters :
const userId = firebase.auth().currentUser.uid
db.collection("analysis").where("owner_uid", "==", userId)
Another thing.
With your current rules, your users won't be able to create a new document, only edit an existing one, here are the updated rules to allow that :
allow read: if request.auth.uid == resource.data.owner_uid;
allow write: if request.auth.uid == resource.data.owner_uid
|| request.auth.uid == request.resource.data.owner_uid;

Resources