why is firebase firestore rule denies write, indicating '...==...' and '&&' as false - google-cloud-firestore

Firestore security rules denying access.
Rules are:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{id} {
allow create, update, delete: if request.auth != null && request.auth.uid == id;
allow read: if request.auth != null;
}
}
}
playground rules indicate '...==...' and '&&' as false. What is wrong with this? Help appreciated very much!
Full rules and screenshot:

allow create, update, delete: if request.auth != null && request.auth.uid == id;
From your screenshot, you are running an update simulation.
request.auth != null will fail if user is not logged in.
request.auth.uid == id will fail if uid != id;
From your screenshot, we can see that authenticated is set to true. Therefore condition 1 will pass.
It is presently failing because of the 2nd condition. request.auth.uid == id
Presently, the id == 'user', but request.auth.uid != 'user';
Solution. Set your request.auth.uid to 'user'. To do that, go under authenticated, under firebase UID, put 'user' (as your firebase uid). It should work.
NB: it is possible that you are not struturing your data properly. Your present data structure is something like 'users/user', instead it should be 'users/{userId}' where userId is generated by firebase when user creates account.

Related

FireStore Security Rules for Content Owner only and Public

I have Security rules like below in my Firestore database
just to be sure I want to ask here for best practice of security rules firestore
So I have collection of userData and communityPost
user data only can be access by content owner that create it (content owner can create and update it)
for community post I want everyone auth and non-auth user can read the data (public)
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Allow only authenticated content owners access
match /userdata/{document} {
allow read, write: if request.auth != null && request.auth.uid == userId
}
match /communityPost/{document} {
allow read: if true;
}
}
}
is this correct rules ?? this is my first time dealing with collection based rules,
thanks for your time
You can check the properties within the document
match /userdata/{document} {
allow read, write: if request.auth != null && request.auth.uid == resource.data.owner
}
or you can match the document id to the user
You can check the properties within the document, notice the match path
match /userdata/{userID} {
allow read, write: if request.auth != null && request.auth.uid == userID
}
As for making it public, Firestore discourages purely open database so you need a few conditional statements
You can check the properties within the document
match /communityPost/{document} {
allow read: if resource.data.public == true;
}

Firestore Collection List operation fails with "missing or insufficient permission"; Works for individual documents

I'm trying to construct rules that allow a user to access all sub-collections and documents of a given account, if they are listed as a user of that account.
This works perfectly for retrieving individual documents, event nested ones. However, it fails when trying to list documents in a sub-collection.
This does not appear to me to be an instance of "rules are not filters": my query should categorically pass for every possible item queried, as it's based on their root ancestor document.
I've also read here (although I couldn't find it in the documents) that list operations also fail if you try and perform a get() for each queried document. I don't believe my rules violate this, either, as the get() command only needs to be run once, and will categorically return the same document for each queried document, as, again, it's based on a common ancestor.
Is my reasoning on the above rules wrong, or is there something else I'm not doing right?
My rules are as follows:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function hasAccountPermission(request, path, permission) {
let accountId = path[0];
let account = get(/databases/$(database)/documents/accounts/$(accountId));
let role = account.data.users[request.auth.uid];
return (
request.auth != null
&& role != null
&& (permission == null || permission in account.data.roles[role])
);
}
match /users/{user} {
allow read, update, delete: if request.auth != null && request.auth.uid == user;
allow create: if request.auth != null;
}
match /accounts/{account=**} {
allow read: if hasAccountPermission(request, account, null)
allow write: if hasAccountPermission(request, account, 'all')
}
}
}
Example database is as follows:
/accounts/bobstuff
roles: {
admin: ['all']
}
users: {
bob: 'admin'
}
/docs/adocument
field: value
A get operation on /accounts/bobstuff/docs/adocument succeeds
A list operation on /accounts/bobstuff/docs/ fails
Ok. It turns out it was was violating the second principle - or at least, Firestore thought it was.
By changing the rules to capture the first path segment, instead of capturing the whole path, and extracting the first segment manually, it appears Firestore was able to determine that only a single get() would be required, and therefore, the rules were valid.
This required splitting the rule into {account} and {account}/{doc=**}. The side-effect of this is that I could also make the rules more efficient by avoiding a get() call for the root case.
Complete (working) rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function hasAccountPermission(request, account, permission) {
let role = account.data.users[request.auth.uid];
return (
request.auth != null
&& role != null
&& (permission == null || permission in account.data.roles[role])
);
}
match /users/{user} {
allow read, update, delete: if request.auth != null && request.auth.uid == user;
allow create: if request.auth != null;
}
match /accounts/{account} {
allow read: if hasAccountPermission(request, resource, null)
allow write: if hasAccountPermission(request, resource, 'all')
allow create: if request.auth != null
}
match /accounts/{account}/{doc=**} {
allow read: if hasAccountPermission(request, get(/databases/$(database)/documents/accounts/$(account)), null)
allow write: if hasAccountPermission(request, get(/databases/$(database)/documents/accounts/$(account)), 'all')
}
}
}

Why firebase_firestore security rules not worked for document read

I am currently working on a a app and in that user needs to make a new account. Your Enters first name and last name then the app automatically suggest a username which is unique and it will be the document name of that user. I had set the firestore secutity rules as follows,
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth != null;
}
}
after user enters the username it checks that the username used or not before move to the next screen.
Future<bool> checkUsernameExist(String name)async{
bool usernameExistSate;
await firestore.collection('users').doc(name).get().then((docSnapShot){
if(docSnapShot.exists){
usernameExistSate = true;
}else{
usernameExistSate = false;
}
});
return usernameExistSate;
}
Currently above system works fine without any problem. But I have a problem, With the firebase security rules sets to below condition how users able to read the documents to check the similar document names are present?
allow read, write: if request.auth != null;
First, I would not use the usernames to store your data in firestore but the uid provided when you are authenicated with google auth. This will allow you much safer access to the database with security rules like this:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId}/{document=**} {
allow read, write, update, delete: if request.auth != null && request.auth.uid == userId;
allow create: if request.auth != null;
}
}
}
For your second problem I would just create a second collection in the root of the firebase project named for example usernames with all usernames stored in a big list so you can query them safely via the firebase API. For that to be possible you have to give the authenticated device access to this collection too via for example adding this under
match /users/...
match /usernames/{document=**} {
allow read, write, update, delete, create: if request.auth != null;
}
Of course then, you have to keep track of both lists when making changes. But this way an authenticated user has only access to his data and all usernames in the worst case.

How to set different rules for different collection of users for accessing same firestore collection?

I have a collection of data, say, products and two different set of users merchants and users. How do I grant read write access to merchants on products and only read access to users?
match /users/{userId} {
allow read: if request.auth.uid == userId;
}
The above rule only defines access for a single users.
Starting point:
match /users/{userId} {
allow read: if request.auth.uid == userId;
}
match /products/{productid} {
// Anyone authenticated can read
allow read: if request.auth.uid != null;
// Only merchants can write
allow write: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.type == 'merchant'
}
Assuming users have field type with possible values ['user', 'merchant']
More info here

Firestore rule: match top level collection

Setup:
My top collection is named users
Each user is named for their unique ID, uid
I want to make a rule so that no matter what document or sub-collection is being accessed, if it will compare uid to the name of current user in users to allow
Current attempt:
Note that this WORKS for top level documents, but as soon as I try to work with a sub-collection within that user, it fails
If it matters, there will be 7 named sub-collections that are always the same between users
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{user} {
function isSignedIn() {
return request.auth.uid != null;
}
allow read, write: if isSignedIn() && request.auth.uid == user
}
}
}
Any help would be appreciated. I think I need to add some ** somewhere?
You will want to use a recursive wildcard to match all documents in all subcollection under the top-level collection.
match /users/{user}/{everything=**} {
function isSignedIn() {
return request.auth.uid != null;
}
allow read, write: if isSignedIn() && request.auth.uid == user
}
In rules version 2, recursive wildcards match 0 or more path segments.