I have a short question about Firestore security rules.
Let's say that I have a document "user" in my firestore database that contains "userID" field.
Now I want to write a security rule that allows user to delete this document only if their ID matches the "userID" field in the document they want to delete.
To do this I wrote the following rules:
match /users/{user} {
...
allow delete: if request.auth.uid == resource.data.userID;
...
}
And when I try to delete this document in the simulator, either if the rule should allow it (IDs match) or it shouldn't (IDs do not match) it gives me this error:
Error running simulation — An unknown error occurred
What did i do wrong here?
Here's my document
Those are rules i set
And this is how i try to delete it
Related
My firestore rules
match /fruits/{fruit} {
allow read: if request.auth.uid == resource.data.user;
}
Now on reading a single document, there is no problem
db.collection("fruits").doc('apple').get()
but when I try to get the collection, it gives me permission-denied
db.collection("fruits").get()
When you try to query all documents in 'fruits' collection, the security rules check if user field in those documents is equal to UID of user requesting data. If any one document doesn't match the condition, it'll throw a permission error. Instead you should add a where() clause in your query as shown below:
db.collection("fruits").where("user", ==, currentUserUID).get()
This query will find documents where user field matches user's UID and all matched docs will pass the security rule if request.auth.uid == resource.data.user;. Security rules are not filters and that means they won't filter out documents from collection based on the rule. You must write a query that way.
I have these rules:
match /suuntoAppAccessTokens/{userName} {
allow create: if request.auth.uid != null && request.auth.token.firebase.sign_in_provider != 'anonymous';
match /tokens/{userID} {
allow read, write, create, update, delete: if request.auth.uid == userID && request.auth.token.firebase.sign_in_provider != 'anonymous';
}
}
match /{path=**}/tokens/{userID} {
allow read, write, create, update, delete: if request.auth.uid == userID;
}
That means that for the path /suuntoAppAccessTokens/dimitrioskanellopoulos/tokens/{userID} the current user should have access.
However, when I query the collection group like so:
return this.afs.collectionGroup('tokens').snapshotChanges();
I get a permission error.
Getting directly the document under tokes/{userID} works as expected.
What can I do so that the current user can run a collectionGroup query and get the items he is permitted to get based on my rules?
Your rule is expecting that the security rule will filter all the documents from all of the tokens collection so that only the current user's documents will be read. This is not possible with security rules. Security rules are not filters. From the documentation:
When writing queries to retrieve documents, keep in mind that security
rules are not filters—queries are all or nothing. To save you time and
resources, Cloud Firestore evaluates a query against its potential
result set instead of the actual field values for all of your
documents. If a query could potentially return documents that the
client does not have permission to read, the entire request fails.
You will need to change your query to that the client is only requesting documents that are fully expected to be readable by the current user. Unfortunately, it's not possible for me tell if this is possible with your current schema. The ID of the document {userId} can't be used in a collection group query to filter the documents. So, both you must ensure that both of the following criteria are met:
You will need some field in the document that you can filter on to get this job done.
You will need to adjust your security rule to match exactly what the client is asking for.
I suggest storing the uid of the user in the document with the token, the same as {userId} in the rule. You can query it like this:
collectionGroup('tokens').where("uid", "==", uid)
Be sure that the client passes in the uid correctly
Also, you will need to make sure that the rule is granting access by the exact same criteria:
match /{path=**}/tokens/{userID} {
allow read, write, create, update, delete:
if request.auth.uid == resource.data.uid;
}
This will only allow access to the document if its uid field is the same as the auth uid, which is exactly what the client is asking for.
I'm building a contact manager where a user has a bunch of "contacts" in their address book. I only want the user who created the contact to be able to query that contact. I wrote a query below that says what I want it to do, but the query does not work and I do not know why.
All contacts are created with an owner_id field that corresponds to the uid of the user that created the contact.
service cloud.firestore {
match /databases/{database}/documents {
match /contacts/{contactId} {
// only allow read for contacts if the current user is the owner
allow read: if request.auth.uid == resource.data.owner_id // <-- this does not work
allow write: if request.auth.uid != null && request.resource.data.owner_id != null;
}
}
}
When I run the query, I get nothing back, and the simulator does not allow me to run queries on list queries for the entire collection, only get for a single document. The query is simply:
db.collections('contacts')
I've also tried limiting using a where clause:
db.collections('contacts').where('owner_id', '==', <hard-coded-owner-id>)
I should note that when I query for a single document, the syntax above does appear to work. It just appears to fail when I query a collection.
So my question is, how does one write a database rule such that I can list all items in the collection while only returning the items that are associated with the logged-in user?
https://firebase.google.com/docs/firestore/security/get-started
I would expect your first query to fail because it's essentially trying to access documents that it doesn't have permission to read. Your rules will not implicitly filter the results.
I'd expect your second query to work because it's only accessing documents that are allowed by permissions. However, it will only work when the effective UID as reported by Firebase Authentication is the same as the one you hard coded. That's what you're rule is verifying - that the logged in user is only trying to read documents where they are present in owner_id. If you're working in the console simulator, you will have to turn on Authentication and put the right UID in the form.
I'm trying to set a rule so that only authenticated users can update, create, write and delete their cat documents. My problem is that I can not create a new document.
When I try to add it I get the error message "Error: Missing or insufficient permissions."
The strange part is that I can delete existing documents with this rule.
service cloud.firestore {
match /databases/{database}/documents {
match /cats/{catId} {
allow read;
allow update, create, write, delete: if resource.data.ownerId == request.auth.uid;
}
}
}
Maybe you are missing request e.g. request.resource.data.ownerId == resource.auth.uid
When writing data, you may want to compare incoming data to existing data. In this case, if your ruleset allows the pending write, the request.resource variable contains the future state of the document. For update operations that only modify a subset of the document fields, the request.resource variable will contain the pending document state after the operation. You can check the field values in request.resource to prevent unwanted or inconsistent data updates
https://firebase.google.com/docs/firestore/security/rules-conditions#data_validation
The Database have a collection "Collection" and each document inside the collection have an object "members" which contains the "uid" of users who will have access to the document.
Collection--->document-->members = {"BZntnJO2PVS8OZ9wctwHiyxBytc2": true}
I have tried many different types of rules but none of these rules seems to work
service cloud.firestore {
match /databases/{database}/documents {
match /collection/{documentId} {
allow read: ****
}
}
1)
allow read: if get(/databases/$(database)/documents/collection/$(documentId)).members[request.auth.uid] != null
2)
allow read: if resource.data.members[request.auth.uid] != null
3)
allow read: resource.members[request.auth.uid] != null
4)
allow read: if request.resource.data.members[request.auth.uid] != null
5)
allow read: request.resource.members[request.auth.uid] != null
Can it be a Firestore bug?
You need to access the data property to get at any user-created properties, so rules 1, 3, and 5 won't work.
request.resource generally refers to the data that you're sending down to the database, typically in the case of a write operation, so rule #4 won't work, because request.resource.data will probably be empty in the case of a read.
Rule #2 does look right, but keep in mind this will only work in the case of fetching a single document. Queries are a little trickier.
Specifically, if you're running a general "Get every document in my collection" kind of query, Cloud Firestore doesn't have the time to search through every record in your database to ensure that your user has access, so it will reject this query. Instead, you'd need to run a query where Cloud Firestore can "prove" that all documents you'd retrieve will be valid. In your case, for example, you would want to make sure your query is something like "Get every document in my collection where members.(userID) != null". Cloud Firestore rules can then compare your query with its rules and feel satisfied that you'll only get documents you have access to.