Firestore security rule get() not work - firebase

The solution is in the end of the post. Check it out.
Решение проблемы в конце поста. Дочитайте.
just a simple question: whats wrong with this and why this is not working?
Trying to get access with user who has role 'admin' in users section to the /titles/{anyTitle} but still get
Missing or insufficient permissions.
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow write: if false;
allow read: if false;
}
function userCanWrite () {
return get(/databases/{database}/documents/users/$(request.auth.uid)).data.role == "admin";
}
match /titles/{anyTitle=**} {
allow read: if request.auth != null;
allow write: if userCanWrite();
}
}
}
Here is my database structure
P.S.
I tried another rule from official documents
get(/databases/{database}/documents/users/$(request.auth.uid‌​)).data.isAdmin == true;
and this is not working too
UPDATE: CORRECT WAY TO DO IT
Support helped me find the solution
this is how you should do:
db structure:
users -> {{ userid }} -> { role: "admin" }
database rule settings:
get(usersPath/$(request.auth.uid)).role == "admin" || get(usersPath/$(request.auth.uid)).data.role == "admin";

I contacted to the Firebase support to report that bug and they gave me a temporary solution on this. It seems that they are having a bug in their systems on the security rules side. They say that the documentation is ok, but for now we should workaround this way:
get(path).data.field == true || get(path).field == true;
Because the bug is that data object isn't populated, you should check both properties. There's no ETA for launching a solution on this bug, so I asked they if they could give me an advice when they solved this issue, so I'll keep this answer up-to-date with their information.

So the way I've solved it is I've created another Collection Called admins
Then I've just added the uid of the user I needed there as such -
Here is my database structure - https://i.imgur.com/RFxrKYT.png
And here is the rules
service cloud.firestore {
match /databases/{database}/documents {
function isAdmin() {
return exists(/databases/$(database)/documents/admins/$(request.auth.uid));
}
match /tasks/{anyTask} {
allow read: if request.auth != null;
allow create: if request.auth != null;
allow update: if request.auth != null && isAdmin();
allow delete: if request.auth != null && isAdmin();
}
}
}
You can view my full Open Source project here:
https://github.com/metaburn/doocrate

You should use $(database) instead of {database} in your code:
get(/databases/{database}/documents/users/$(request.auth.uid)).data.role == "admin";

What worked for me was moving the userCanWrite function above my rules. It appears that the function has to be defined before any of the match rules that call it. Maddening :-)

This is the Firestore rule I use to check if the user is admin.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if true;
allow write: if userIsAdmin();
}
function userIsAdmin() {
return getUserData().userRole == 'Admin';
}
function getUserData() {
return get(/databases/$(database)/documents/User/$(request.auth.uid)).data;
}
}
}

Related

Firestore: Prevent write/overwrite/update field once it is added

I have a collection in which I am storing user requests in documents having documents ID as user's email. In the document, I am creating fields the key for which is being generated at client side.
Now, the problem that I am facing is that user can overwrite the existing field/request in the document if the key matches which I don't want to happen.
What I tried was to use this rule which unfortunately does not work
resource.data.keys().hasAny(request.resource.data.key();
So how can I achieve this?
Below are the screen shot of the firestore data and the current security rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /roles/{userId}{
allow read: if isSignedIn() && hasId(userId);
}
match /requests/{email} {
allow read, update: if isSignedIn() && hasMail(email)
}
//functions//
function hasMail (email) {
return request.auth.token.email == email;
}
function hasId (userId) {
return request.auth.uid == userId;
}
function isSignedIn () {
return request.auth != null;
}
function getUserRole () {
return get(/databases/$(database)/documents/roles/$(request.auth.uid)).data.role
}
}
}
You can check if a resource already exists. Here an example:
allow write: if resource == null // Can create, not update
Use that to restrict any edit or update of the data. If you have additional rules you can granulate them to update, delete and create.

How to set Firestore security rules? resource.data: Null value error

I need some help making my security rules for firestore work.
These are my firestore rules:
service cloud.firestore {
match /databases/{database}/documents {
match /orders/{orderID} {
allow read, update: if request.auth.uid == resource.data.buyerId || request.auth.uid == resource.data.sellerId;
}
}
}
my orders collection:
orders: {
sellerId: 'some-id',
createdAt: timestamp,
buyerId: 'some-id'
}
It should return all documents from orders collection which has either buyerId or sellerId equal to authorised user (request.auth.uid).
but the above rule is not working as expected.
firestore collections screenshot
firebase simulator output
That error message is suggesting that the requested document was not actually present in the database. You entered "orders/{orderId}", which looks like you put a wildcard in the Location field in the simulator. That's not going to work. You need to enter the path to an actual document that exists if you want to test your rule that uses its field values.
resource.data: Null - this error happens when you try to create a new entity.
Split write rule, on create and update.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /user/{userId} {
allow read: if request.auth.uid == userId;
function authed() {
return request.auth.uid == userId;
}
allow create: if authed() && request.resource.data.keys().hasOnly(['name']);
allow update: if authed() && request.resource.data.diff(resource.data).changedKeys().hasOnly(['name']);
allow delete: if authed();
}
}
}

Firebase security rules syntax with arrays/list property

Within a Firebase Firestore collection with path 'organizations' each document contains a list of string userID's of users who can update or delete that document.
export interface Organization{
name?: string,
owners: string[]
}
I would like to create a Firebase security rule that ensures that only a logged in user with a uid that is in this list can edit or delete the object. Unsure of the appropriate syntax.
service cloud.firestore {
match /databases/{database}/documents {
match /organizations/{organization} {
allow read: if true;
allow create: if request.auth != null;
/// What should be the syntax here?
allow update, delete: if request.auth != null && (request.auth.uid in resource.data.owners); // <--------- What should be the syntax for this line?
}
Ok, answering my own question here in case it's useful for anyone else.
It looks like the 'in' syntax above actually works even-though it was a complete guess and I wasn't able to find any documentation for it in the firebase security roles documentation.
Final code:
service cloud.firestore {
match /databases/{database}/documents {
match /organizations/{organization} {
allow read: if true;
allow create: if request.auth != null;
allow update, delete: if (request.auth != null) && (request.auth.uid in resource.data.owners);
}

Struggling Firestore rules

I'm new to Firestore Rules. All I want is simply check if user id exists within the object. Im reading data using FirestoreRecyclerAdapter like so (query):
Query query = FirebaseFirestore.getInstance()
.collection(EVENTS_INFO)
.whereEqualTo("users." + user.getUid(), true);
Here is how structure looks in Firebase Console (screenshot):
Firestore rules here:
service cloud.firestore {
match /databases/{database}/documents {
function isSignedIn() {
return request.auth.uid != null;
}
match /events/{event} {
function isUser() {
return isSignedIn() && resourse.data.users[request.auth.uid] == true;
}
allow read: if isUser();
}
}
As there is no simulator I cant fast get the issue. allow read: if true works as desired. Any clues?
There is an issue with this line...
resourse.data.users[request.auth.uid] == true;
resourse should be written resource

Firestore security rule using reference to another document

I am trying to base a security rule on a reference to another object.
I have a collection of users and collection of roles. A user object has a field called "role" that is a reference to a particular document in the roles collection.
users
id
name
role <-- reference to particular role
roles
id
name
isSuperUser
The goal here is to allow a user with a particular role (the role with isSuperUser == true) to edit any other role or it's sub-collections;
Here are my rules that I would have thought would have worked:
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId=**} {
allow read, write: if request.auth.uid == userId;
}
match /roles/{roleId=**} {
function isSuperUser() {
return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role.isSuperuser == true;
}
allow read: if request.auth.uid != null;
allow write: if isSuperUser();
}
}
I have confirmed the following works, but it's not really that useful...
get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role != null;
If there is a better way to accomplish role base security, I am all ears.
The lack of any debugging tools makes this quite frustrating.
I know it's been a while since the original question but I've had a similar issue and I hope this could help you or others.
Your condition is:
get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role.isSuperuser == true;
But role is a reference, which (apparently) means you need to get it as well. Try this:
get(get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role).data.isSuperuser == true;
Have you tried to move the wildcard to a nested path?
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read, write: if request.auth.uid == userId;
match /{role=**} {
allow read: if request.auth.uid != null;
allow write: if isSuperUser();
}
}
}
}

Resources