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.
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 an 'owner' field in my documents to entitle only the owner to read the document, and only a new document that its 'owner' field is the uid of the user, can be written:
allow read: if request.auth.uid == resource.data.owner;
allow create, write : if request.auth.uid == request.resource.data.owner;
The creation and update rule works as expected! I tested and saw that if the 'owner' field of the record in the new data is not the user's UID then it doesn't work!
The problem is the 'read' section. I wasn't able to read records. Only when I changed to allow read: request.auth.uid != null, I was able to read.
I triple checked that the records has an 'owner' field that is exactly the same as the UID, also in debug.
I'm have experience with Firebase, and I have no idea what is the problem here.
Since you indicate that "[You weren't] able to read records" (with an S at records), your problem most probably comes from the fact that security rules are not filters, as explained in the documentation:
Once you secure your data and begin to write queries, keep in mind
that security rules are not filters. You cannot write a query for all
the documents in a collection and expect Cloud Firestore to return
only the documents that the current client has permission to access.
You don't show the query used to read the Firestore documents, but let's imagine your documents are in a collection named collection.
With a query like
query = db.collection("collection")
query.get().then(....);
you are querying independently of the User ID, hence the problem.
You need to adapt your query with the where() method, as follows:
//get the value of uid
query = db.collection("cities").where("owner", "==", uid)
query.get().then(....)
I am writing a chat application and am done apart from the security rules section. I am currently creating two documents for each message (one each for each user) I am okay with writing a document to my user Id but the database isn't allowing for a write in the other paired user Id.
I have tried by allowing the write if the userId is in the resource.data of the other file
match /message/{user}/{chatRoomID}/{messageId} {
allow read, write: if request.auth.uid == user || request.auth.uid in resource.data;
}
How can I make it so whenever a message is sent to the database it is only read and can be written by the specific user Ids??
Each message object has reference to who sent it (each user's object Id). Thanks in advance !!
While in is indeed an operator in security rules, this won't work:
request.auth.uid in resource.data
The in operator checks if a key exists in a map, where it is much more likely that you store the UID of the other user in the value of a field.
To check whether a certain field has a specific value, use something like this:
request.auth.uid == resource.data.senderID
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.