I am trying to figure out how should I write my firestore rule such that Only the matching uid in the key of the document's key-value pair can have permission to write.
More specifically:
my firestore structure is something like:
'Items LA' -> 'Pasadena' -> {'awjij53dHh3dnYAh': {itemInformation}}
collection -> document -> fields
where 'awjij53dHh3dnYAh' is the seller ID.
and I want to write a rule such that it gives awjij53dHh3dnYAh permission to write in that field.
Currently what I have is the following:
match /Items%20LA/{cityName}/{data}{
allow read: if request.auth != null;
allow write: if request.auth.uid == data.key; // This is where I am not sure how to write what I want to accomplish
}
Thanks
First thing - the match path for a rule only includes the path components of a document (collections and document IDs), and not fields. Also, it doesn't need any URL escaping. Just match on "/Items LA/{cityName}".
Second, if you want to refer to the names of fields in a document in a rule expression, you should know that request.resource.data is a Map of all the incoming fields, and you will have to use its API to deal with the those key/value pairs.
Third, if you want to find out which fields of a document changed, you will need to compare the state of the document before and after the change using the MapDiff API.
Putting all this together, you will need to check that only the field changed that equals the user's UID:
match /Items LA/{cityName} {
allow write: if request.resource.data.diff(resource.data).affectedKeys().hasOnly([request.auth.uid]);
}
What you want probably is
allow write: if request.auth.uid == $(data)
//or this
allow write: if request.auth.uid == resource.id
Related
I have a document whose name consists of two user keys, can I check whether the key of the querying user is included.
for example the user fvmAPXO4BYUVJYkeSxsgsgzjjs should have access to the document 2B0ABrxKgjkrefjCP8tuMgq12e4-fvmAPXO4BYUVJYkeSxsgsgzjjs
I need something like: allow read, write: if document name contains ("request.auth.uid") ;
You can use matches() as shown below:
match /collection/{docId} {
allow read: if docId.matches(".*"+request.auth.uid+".*");
}
This regular expression will simply check if request.auth.uid is present in the document ID.
I have a firestore collection whose documents contain two optional date fields (not all documents contain them). I am not sure how to use the security rules to enforce the fields content type although they may be missing.
Currently, these date fields are created with firebase.firestore.Timestamp.fromDate() . I think I can write a statement that looks roughly like this*:
allow write: if request.resource.data.get("RecordDate", ?) is timestamp;
but I am not sure what could be the second argument for get() in this case (the "?").
*(based on the documentation here:
https://firebase.google.com/docs/firestore/security/rules-fields#enforcing_types_for_optional_fields)
I might be changing the method for the creation of these fields to firebase.firestore.FieldValue.serverTimestamp(). In that case, how is it possible to use the following rule (or an equivalent), but still allow for missing fields?
allow write: if request.resource.data.recordDate == request.time;
Hope the questions are clear enough- pls bear with me, I am a newbe.
You can use a simple or condition like this:
if !("RecordDate" in request.resource.data.keys)
||(request.resource.data["RecordDate"] is timestamp)
Firebase Security Rules cannot modify or change the values requested, you can only allow or deny a request. If you want to ensure a certain field is of a timestamp, you can only compare it to the valid types that are listed in the reference documentation:
Firestore Security Rules: Types - Timestamp
To allow for missing fields you can use an OR operator || and wrap conditions inside () to group their logic together.
request.auth.uid != null || (resource.data.visibility == 'public' && request.auth.uid == resource.data.owner);
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 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've found that question relatively often asked here, but i still cant figure out how to manage a rule for unique properties. I have following document datamodel:
users/{usereId}/Object
users/usernames/Object
The first Object contains basic information about the user, like:
{
email: "example#hotmail.edu"
photoURL: null
providerId: null
role: "admin"
username:"hello_world"
}
meanwhile the usernames objects only contains the username as the property key and the uid as the value, for instance:
{
hello_world:"F3YAm8ynF1fXaurezxaQTg8RzMB3"
}
I set it up this way, because I want that every user has a unique username. And its less time consuming iterating through the second object than through the first ones.
But back to my issue. I need that hello_world is unique within the write operation. But my rules so far does not work. I have:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth.uid != null
}
match /users/{userID} {
allow create: if !exists(/databases/$(database)/documents/users/$(request.resource.data.username)) <== does not apply
}
}
}
The second match is, what should apply the unique property rule. Has anyone an idea how to set the rule correctly?
In the console the object model looks as follows
You created a document called usernames in your users collection, to track the names that are in use. But your rules are trying to find a document named after the current user's name, which will never exist in this structure.
In your current structure, you will need something like this:
allow create: if get(/databases/$(database)/documents/users/usernames).data[$(request.resource.data.username)] == request.auth.uid
So the above gets the document where you keep all the user names, and then checks if the current user name is in their for the current UID.
An alternative approach is to keep an additional colllection of all user names, and then make each document in there map a single user names to a UID. In this scenario your /usernames collection would be top-level, since it's shared between all users. The rules for this would be closer to what you currently have:
allow create: if !exists(/databases/$(database)/documents/usernames/$(request.resource.data.username))
Since your usersnames have to be unique, wouldn't it be an option to use their names as the document key?