Firestore 'arrayContains' does not work in flutter app [closed] - firebase

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I want to fetch the data from firestore. So, I created function to get data as follows. It works with 'isEqualTo' but does not fetch when I change the where clause to 'arrayContains'.
Query<Map<String, dynamic>> detailCollection =
_firestore.collectionGroup(collectionName);
return detailCollection
.where('title', arrayContains: 'cb1')
.snapshots();
my fiestore rule is as follows,
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if true;
}
}
}
I tried to get the data from 'posts' collection.
Appreciate if anyone can show me, what is the cause of the issue?

What you're trying to do will not work. array-contains queries only work with fields that are actually arrays. Your cb1 field is just a string. It's not an array, so it would never match an array-contains filter.
If you want to use array-contains, then you will need to use an array type field. You might want to review the documentation, which pretty clearly states that the field must be an array.

Related

How To DENY Access to entire Firestore database in security rules

Lets say I want to have a document in my Firestore having a key on whether to allow access to database or not.
Something like server_online = True
now in my firebase rules I want to check this rule first before going into rules for each collection and document.
I know that a complicated way of doing this is to put this thing in a function and then check this function along with other functions in my firebase rules for every access specifier, but that would be very long since i have a very long ruleset.
So how should i tackle this problem?
I wanted it to be something like.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
allow read: if get(/databases/$(database)/documents/server/status).data.status;
match /users/{userID} {
allow read: if request.auth.uid == userID;
allow create
}
Now first the above rule will check if the variable is True and if so only then proceed to the later ones.
I know that the above code wont work, as Firebase checks the bottom most rule first and doesnt overwrite access.
Any ideas on how to tackle this problem?
Since overlapping rules are OR'ed together, there's no easy way to enforce your AND condition on all rules in one place.
The shortest way I've found is to create a function (say isOnline) and then call that in all rules or in other *higher level) functions that my actual rules depend on.

How to set path to a field in Firestore security rule [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
My collection name is "users" and in that i have some documents created in random number and when i click that i get my fields as
name : john
swimming : true
uid : (auth.uid)
Since my document is created in random numbers i can't access that document directly as request.auth.uid
Now i want to know how can i write security rules to stop the user from updating his swimming field as false?
EDIT
What i have tried so far,
match/users/{document=**}
{
allow read;
}
match/users/{document=**}/swimming
{
allow write : if false;
}
And
match/users/{document=**}
{
allow read;
}
match/users/{document=**}/{swimming}
{
allow write : if false;
}
And
match/users/{document=**}
{
allow read;
}
match/users/{document}/swimming
{
allow write : if false;
}
For my 'users' collection, I create documents with the ID set to the UID of the user in question using the set() function instead of the add() function. An alternative would be to do as you have: use the add() and explicitly set a uid field.
There are many blog posts that answer what you are after. Googling for "firestore rules restrict updates of some fields" yields lots of results including:
https://medium.com/day4/firebases-firestore-rules-how-the-f-af5821513025
https://kushagra.dev/blog/ensure-missing-fields-in-firestore-security-rules/
Firestore rules for document field
Ultimately, in your rule you can compare the request.resource.data.XXX (field XXX in the uploaded data) with resource.data.XXX (XXX in the existing data in Firestore), and return FALSE if you want to block the user from updating field XXX. You can also use boolean operators to enforce a no-edit rule on multiple field.

Firestore per-field security rule

I have studied the answer to this question (which has an extremely similar title): Per field rules in Firestore Security Rules. The solution in that case was to make a field unmodifiable, I do not think that is what I am after here.
I have a posts collection with a data structure as follows.
{
uid: string, // this is the original poster's UID
title: string,
content: string,
likesCount: number,
likerUIDs: string[]
}
I would like to restrict writes to the title and content fields to users with an auth token UID that matches the post's uid field. However, any authenticated user should be able to increment the likesCount and add their own UID to the likerUIDs field.
It seems like per-field security rules are not really supported. Is the solution here to maintain a separate collection with different rules but the same keys as the posts, for example post-likes, that contains the likesCount and likerUIDs fields? Or is there a firestore security rule trick to achieving this?
EDIT
Thanks to Doug and Frank's comments (extremely helpful video series by the way), I was able to come up with a solution to my initial question. As suggested in the accepted answer, I'm going to do this with a callable function, since it is perfect for this case. For those who stumble upon this question and want to accomplish something similar, I've pasted the rules I ended up with here. These rules accomplish exactly what is described in the question, but a callable function was definitely the way to go here.
function isOwnerCurrent() {
return request.auth.uid == resource.data.uid;
}
function isOwnerIncoming() {
return request.auth.uid == request.resource.data.uid;
}
function isUnmodified(key) {
return request.resource.data[key] == resource.data[key]
}
match /posts/{post} {
function validateNonOwnerPostUpdate() {
return isUnmodified('title') && isUnmodified('content') &&
isUnmodified('created') && isUnmodified('updated');
}
allow read: if true;
allow create: if isOwnerIncoming();
allow update: if (isOwnerCurrent() || validateNonOwnerPostUpdate()) && isUnmodified('uid');
allow delete: if isOwnerCurrent();
}
For updates, I am checking if the user is either the owner of the post, or only updating the so-called "public" fields of likesCount and likerUIDs, and for both they must not be modifying the owner UID of the post. Like mentioned in the accepted answer, this isn't great because anyone will be able to edit these fields and mess up the numbers.
I think it is better to use cloud function to solve this. you can use callable cloud function when that other users (not document owner) like that post. https://firebase.google.com/docs/functions/callable . because cloud function can bypass security rules
I think it is safer you do it through cloud function unless that likesCount is not that important. because if someone can hack your client app than they can modify your code. you will update the document like this
db.doc(`posts/${postID}`).update({
likesCount: admin.firestore.FieldValue.increment(1)
})
if they hack your app, then they can change the increment from 1 to 100 for example. yes you can do the same via security rules but you have to add additional check , and it will be complicated and error prone IMO.

Super basic firestore security rule won't work

I can't get the literal simplest firestore security rule I can write to work in the play ground. Just for testing, I've made a Cloud Firestore database with a collection named users. It has one field stuff. In the playground, these are my rules:
service cloud.firestore {
match /databases/{database}/documents {
match /users/{user} {
allow read, write: if true;
}
match /{document=**} {
allow read, write: if false;
}
}
}
I'm simulating a get on location: /databases/(default)/documents/users, but it always fails due to the document=** match, and never matches /users/{user}. Why is this! Feels like I'm following the most basic examples from the docs.
Added a couple screenshots for clarify.
In the "Rules playground", in the location field, you don't need to enter /databases/(default)/documents/. This part of the path is already taken into account, as it is shown above the editable field with the pale grey (or greyed out) /databases/(default)/documents string.
So, by just entering users/C8YDk... it will work, since your rule allows reading the doc, due to an overlapping matching statement.
More info on how to use the playground is to be found here.

Allowing access based on mapped key

It's a simple and common use case of security rule, but cannot make it work.
I have a document orgs/fooOrg on my Firestore(not RTDB), and it contains an object
{
"members": {
"fooUser": true
}
}
and the rule applied is
service cloud.firestore {
match /databases/{database}/documents {
match /orgs/{orgId} {
allow read: if "fooUser" in resource.data.members;
}
}
}
I expect all the document in orgs collection should be able to be read; however, the server says Error: Missing or insufficient permissions as a result of running
firebase.firestore().doc('orgs/fooOrg').get()
on a browser (using v4.5.0 and v4.5.1). Even
allow read: if resource.data.members["fooUser"] == true;
fails, too. What went wrong?
In my understanding, this should work according to this document
https://firebase.google.com/docs/firestore/security/secure-data#evaluating_documents_currently_in_the_database
I believe that it was working like a week ago. All the sudden, my working code started to generate the error, so I wrote this MCVE and tested on several different projects.
In addition, I found similar issues below, but a bit different from them, so not sure if it's the same reason (a bug on Firestore)
Firestore security rules based on map values
(My case, even getting a simple document fails)
Firestore read rules with self condition
(This case uses a value of a map. My case, a key is used)
Now seems that the issue is solved without changing code. No announcement, but seems that something is fixed by Firestore side.

Resources