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

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.

Related

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();
}
}
}

Cloud firestore rules - get document by it's field value

I have two collections worktimes and submissions. Submission document has uid field.
I want to be able to update/delete worktime document when it's not signed. I want something like this:
match /worktimes/{document=**} {
allow update, delete: if !isMonthSigned()
}
In my isMonthSigned() I want to look into submissions collection, choose the document which uid equals mine and than have access to it's field values. How do I get that document?
So far I'm stucked here:
get(/databases/$(database)/documents/submissions/{submission})
Thanks!
service cloud.firestore {
match /databases/{database}/documents {
function isSignedIn() {
return request.auth != null;
}
function isOwner(userId) {
return request.auth.uid == userId
}
match /submissions/{submission} {
allow update, delete: if isSignedIn()
&& isOwner(resource.data.userId);
}
}
}

Firebase - Cloud Firestore, Problems creating rule for admin

Im having a problem with creating firebase rules, I created a field "role" in my 'employees' collection, so there, I want to distinguish each type of users allowed to read/write, etc in the DB. My problem is that for some reason I cant verify if the user trying to read others information is really the admin, in the "isAdministrator" function I cant make the get's to work:
service cloud.firestore {
// Function: Check if an authenticated employee is admin
function isAdministrator(){
//return get(/databases/$(database)/documents/employees/$(request.auth.uid)).data.role == 'admin'
// || get(/databases/$(database)/documents/employees/$(request.auth.uid)).role == 'admin';
return false;
// (data.child('role').val() == 'admin')
}
// Function: Check if an employee is authenticated
// RULES
match /databases/{database}/documents {
// Allows only admins and 'owner' to acess his document
match /employees/{document=**} {
allow read, write, update: if request.auth.uid == resource.data.uid || isAdministrator();
allow create, delete, list: if isAdministrator();
}
}
}
This is how my collection looks like:
match /databases/{database}/documents must wrap everything, including the function, like this:
service cloud.firestore {
match /databases/{database}/documents {
// FUNCTIONS
// Function: Check if an authenticated employee is admin
function isAdministrator(){
return get(/databases/$(database)/documents/employees/$(request.auth.uid)).data.role == 'admin'
|| get(/databases/$(database)/documents/employees/$(request.auth.uid)).role == 'admin';
}
// RULES
// Allows only admins and 'owner' to acess his document
match /employees/{document=**} {
allow read, write, update: if request.auth.uid == resource.data.uid || isAdministrator();
allow create, delete, list: if isAdministrator();
}
}
}

Firestore rules objects

I am trying to setup some basic Firestore security rules in my database. I am having some trouble finding the relevant documentation to learn how to do this.
Currently my document is structured like this:
project(document): {
createdBy(string): chris#emailaddress.com,
users(object): {
{ graham#emailaddress.com(object): access: write },
{ paul#emailaddress.com(object): access: read }
}
}
I'd like to setup my rules so that:
Users must be signed in to read, write or delete anything
If a user is added to a project with 'read' access they can only read the document.
If a user is setup with write access they can update and read the document but not update the createdBy field.
If a user has created the document they can read, update and delete the document.
My security rules are setup like this:
service cloud.firestore {
match /databases/{database}/documents {
match /projects/{projectId} {
allow read: if existingData().users[getUser()token.email].access != null && isSignedInAndVerified()
allow read, update: if existingData().users[getUser()token.email].access != "write" && isSignedInAndVerified()
allow update, delete: if sameAsEmail(existingData().createdBy) && isSignedInAndVerified()
}
//my functions
function getUser(){
return request.auth
}
function existingData(){
return resource.data
}
function sameAsEmail(resource){
return resource == request.auth.token.email
}
function isSignedInAndVerified() {
return request.auth != null && request.auth.token.email_verified;
}
}
}
Incorrect use of syntax use: getUser().token.email instead.

Setting Firestore security rules with Collections generated at runtime

My Firestore database creates a new Collection whenever a new user Signs Up to my app. The name of the Collection is the username of the new user. I wanted to make the documents inside this collection to have restricted write access.
service cloud.firestore {
match /databases/{database}/documents {
match /User1/{info} {
allow read: if signedIn();
allow write: if isOwner(User1);
}
function signedIn() {
return request.auth != null;
}
function isOwner(userId) {
return request.auth.uid == userId;
}
}
}
This works if the current user is User1 but is not applicable to any new user that signs up. How do I add this Firestore Security Rule to every new user?
I notice that the first rule matches to /User1/{info}, meaning it will match any path in the collection User1. Instead, if you use brackets, this value becomes a wildcard, meaning the match will work for any value. Check out the examples in the guide for more information.
service cloud.firestore {
match /databases/{database}/documents {
match /{username}/{info} {
allow read: if signedIn();
allow write: if isOwner(username);
}
function signedIn() {
return request.auth != null;
}
function isOwner(userId) {
return request.auth.uid == userId;
}
}
}

Resources