Validate map keys in Firestore rules - firebase

How can I validate all keys in map in Firestore rules? Each key in map is an identifier related to a document.
Example:
User creates an invitation document with other documents access like this:
{
"email": "foo#bar.com",
"documents": {
"document1": true,
"document2": true,
...
}
}
How can I check document1 exists in database (ie. in documents collection)?

You can't write loops in security rules, so if you have an unknown number of documents to work with, you won't be able to validate them. Instead, consider using Cloud Functions to check the values after it's been added, and delete the invitation if it's not valid.

Related

Query Firebase Realtime Database to find children which has a given key in it's data

I have around 2 million users at a branch in a database. Each user may contain a property called activeSessionId which tells me if this user has ever started a web session to my app. There is also an array called sessions that contains data for all the web sessions this user has ever started. I have found some bugs in the code which may cause the users who have started a web session to the app to have some invalid data causing the mobile app to become unresponsive. I want to traverse all the users who have started a web session and fix that data.
If I traverse over all the users, the code fails with firebase complaining about too much data to read. So, I am trying to think of a query that could just give me the users that have an activeSessionId defined and I just traverse over those. But I can't think of any such query. The equalTo method doesn't allow for an undefined value for a property.
Below is what a user looks like in firebase that has an activeSessionId.
Top level
userInfos: {
"-NL1sdfee29E7bQ53_rJTW": {
"activeSessionId": "-NL1aKF29E7bQ53_rJTW",
"alarmsCount": {
"ownAlarms": {
"Personal": {
"edit": 1244,
"new": 436
}
}
},
"contactsLastUpdatedAt": 1675435956007,
"deviceLocale": "en-US",
"deviceOs": "android",
"joinDate": 1649084020051,
"lastActiveAt": 1675435953311,
"maxAllowedAlarms": 5,
"rateTheApp": {
"done": true,
"lastAsked": 1675435981073
},
"release": "7.4.3",
"sessions": {
"-NL1aKF29E7bQ53_rJTW": {
"browser": "Chrome",
"id": "-NL1aKF29E7bQ53_rJTW",
"lastActive": "10 Jan 2023, 11:22 AM",
"platform": "Windows"
}
},
"timezone": "America/Denver",
"timezoneOffset": 420,
"totalNumberOfContacts": 282
}
}
Is it possible to create such a query for Firebase Realtime Database?
In the Realtime Database, there is no way you can query a node based on a field that doesn't exist. So if you want to perform a query that returns the users that have the activeSessionId field set to a particular value and the users that don't have the activeSessionId field at all, that isn't possible.
A possible workaround would be to denormalize the data and create a node that contains only the users that have already set the activeSessionId field or don't have the activeSessionId field at all.
However, if you consider at some point in time using Cloud Firestore, then please note that you can achieve something similar using whereIn() in a query that looks like this:
db.collection("products").whereIn("activeSessionId", Arrays.asList("someId", "defaultId"))
This means that you have to set a default value to all users who don't have set a specific ID.
Also, in the case of Firestore, a query like this:
db.collection("products").whereIn("activeSessionId", Arrays.asList("someId", null));
Won't work. Besides that, you cannot also query for non-existing fields. There is no "undefined" value for Firestore fields. Here are the supported data types. You can see that null is a supported data type but not undefined.
So we cannot query for fields that don't exist in Firestore. We can only search for fields that exist in the index, hence the presence of the defaultId in that array.

Conditional update all documents in all CosmosDB partitions

I have a CosmosDb container that have (simplified) documents like this:
{
id,
city,
propertyA,
propertyB
}
The partitionkey is 'city'.
Usually this works well. But there has been a bug in our code and now I have to update a lot of documents in a lot of different partitions.
I made a conditional update document like so:
{
"conditions": "from c where propertyA = 1",
"operations": [
{
"op": "set"
"path": "/propertyB",
"value": true
}
]
}
This document I send with the REST API to CosmosDB
Because I want to update all documents in all partitions that satisfy the condition I set the x-ms-documentdb-query-enablecrosspartition header to 'True'.
But I still need to supply a partition key in the x-ms-documentdb-partitionkey header.
Is there a way to use the REST API to update all the documents for which the condition is true, whatever the partitionkey is?
Yes, there is a way through which REST API can provide us with the ability to create, query, and delete databases, collection of documents, and documents through programmatically. To change a document partially, you can use the PATCH HTTP Method.
You will still need to supply a partition key in the x-ms-documentdb-partitionkey header.
If you have created the document based on partition key, you must need to provide partition key in header.
For more information regarding the above said partition key, you can refer the below documentation link: -
https://learn.microsoft.com/en-us/rest/api/cosmos-db/patch-a-document

Firestore security rules get(). exists()

I have documents in Firestore like the following:
BID: "123"
From: "xxx"
Opn: true
I need to check if there are another document have BID == "123" and Opn == true before create the document, because it's not possible to have one more document where Opn is true with same BID.
I try to use get() and exists but it's not working with 2 data fields.
What I tried:
function checkIfThereOpenRoomForSameBBB(xxx) {
return !(
(get(/databases/$(database)/ChatRooms/$(ChatRoom)).data.BID == xxx) &&
(get(/databases/$(database)/ChatRooms/$(ChatRoom)).data.Opn == true)
);
}
is there any solution for this case?
Security rules can't search for data in the database, as that would be prohibitively slow and expensive. All they can do is check if a document exists at a specific path, or read a document as a specific path and check its contents.
This means that any time you want to check if something exists, you'll need to ensure that lives at a known path. So if the combination of BID and Opn=tru must be unique, you should create a collection where the key of each document consists of the BID value and Opn=true.
If this is a global requirement for your app, you could even use this key in your existing collection instead of the (likely auto-generated) key you currently use.
Also see:
Prevent duplicate entries in Firestore rules not working
firebase rule for unique property in firestore
I want to write a rule that will don't allow add same document second time

Secure and Filter certain data "Keys" inside the FireStore document

I am developing an application that needs to search all nearby users WITHOUT sharing their coordinates within 100 miles let's say. The example below I am using GeoHash to help me in calculating the distance.
In FireStore, I have the following document inside collection of users.
{
"userId" : "12345",
"displayName" : "username",
"geoHash" : "gbsuv",
"photoUrl" : "example.com/user.jpg",
"refId" : "0001"
}
The question is: How should I protect the "geoHash" from being retrieved within each document inside the collection?
Firestore security rules grant access on a document level. So either the user can read an entire document, or they can't read anything in that document. There's no way to grant users access to only part of a document.
This means that you can't query something that the client can't read. So in your current structure, if the user needs to query on geoHash, they will be able to read that field too.
The only alternative is to not let the client do the querying, but instead do that querying on a server (such as in Cloud Functions). For this you'd store the geohash for each user in a separate document (say in a collection called locations). The Cloud Function then queries this collection, and returns the real user document(s) (which doesn't contain the geohash anymore) to the user.

How to list all documents the user can read from a Firestore collection?

How do you get all documents in a collection, for which the current user has read permissions?
Trying to get all documents results in a permissions error, because it includes attempts to read documents where the user does not have permission (rather than returning the filtered list of documents).
Each user in this app can belong to multiple groups. Reads are locked down to the groups that they have been added to.
match groups/{group} {
allow read: if exists(/databases/$(database)/documents/groups/$(group)/users/$(request.auth.uid));
}
Here's how this would look with a hypothetical subcollection-contains-id operator.
firestore()
.collection("groups")
.where("users", "subcollection-contains-id", user.uid);
As a temporary workaround I've moved this logic to a cloud function. Here's a shorthand of how it works.
for (let group of firestore().collection("groups")) {
let user = firestore.doc(`groups/${group.id}/users/${uid}`);
if (user.exists) {
// Send this group id to the client
}
}
How can I keep these concerns together and move this logic to the client side without relaxing the security rules?
You could add owners field in the documents inside a collection
owners: ["uid1", "uid2"]
Then, you could get all the posts with uid by searching with array_contains
ref.where("owners", "array-contains", uid)
In rules, you could add sth like these:
allow read: if request.resource.data.owners.hasAny([request.auth.uid]) == true
allow update: if request.resource.data.owners.hasAny([request.auth.uid]) == true

Resources