3
votes

I want to give write/update access to only one field from my firestore database collection without requesting the auth service. My rules for firestore is:

service cloud.firestore {
  match /databases/{database}/documents {
    match /businesses/{businessId} {
      allow read;
      allow write: if request.auth.uid != null;
    }
  }
}

My database structure for businesses is:

addedBy: string;
address: string;
category: string;
claim: boolean;
city: string;
email: string;
id: string;
likes: number;
location: {
    _lat: number,
    _long: number
};
name: string;
ocHours: [{
    day: string,
    from: string,
    status: string,
    to: string
}];
phone: number;
sponsored: boolean;
state: string;
status: string;
verificationCode: string;

I want to update only the claim field without request.auth.uid != null.

Any suggestions?

5
Take a look at this questionAndré Kool

5 Answers

9
votes

rights assignments in firestore Rules as Ron says can only be applied to a whole document. Nevertheless there is a way of allowing to write/update/delete/create a specific file and that is by ensuring that the incoming change object has only the field you want to grant rights. And you do that by:

  1. Reading all keys in the incoming Object: request.resource.data.keys()
  2. Ensuring that the only key in the object is hasOnly(["claim"])

service cloud.firestore {
    match /databases/{database}/documents {
        match /businesses/{businessId} {
            allow read;
            allow write: if request.resource.data.keys().hasOnly(["claim"]); 
        }
    }
}

Hope it helps although the question was posted a while a go!!

6
votes

I don't know if this thread is still valid, but searching for a solution I stumbled upon the rules.MapDiff spec, that does exactly what you are looking for:

// Compare two Map objects and return whether the key "a" has been
// affected; that is, key "a" was added or removed, or its value was updated.
request.resource.data.diff(resource.data).affectedKeys().hasOnly(["a"]);

See here for details https://firebase.google.com/docs/reference/rules/rules.MapDiff

1
votes

As explained in this blog post, request.resource.data does not contain only the updated fields, but the entire future state if the write would be successful.

So you could simply check if the field in the resource differs from the one in the request and, as the author neatly suggests, create a function like this:

function notUpdating(field) {
 return !(field in request.resource.data) || resource.data[field] == request.resource.data[field]
}

In your case, you would probably want to break your write into create,update,delete and the update could look something like this:

allow update: if
     notUpdating(addedBy)
  && notUpdating(address)
  && notUpdating(category)
  // ... put all fields here except 'claim'
  && notUpdating(status)
  && notUpdating(verificationCode) ;
0
votes

Authorization is applied to the entire document. You can create a "claims" collection of documents identified by business-uid's and set it to write only so it is impossible for public to read the list but they can update the claim property. These documents could be boolean like

home/claimsCollection
    bizABC123
        claim:true
    bizXYZ123
        claim:false
0
votes

I have written the following function to be used to allow only one field to be editable:

function onlyField(field) {
    return request.resource.data.keys().hasOnly([field]);
}

This function can be used as follow:

match /someObject/{document=**} {
    allow update: if onlyField('someFieldOfYOurObject');
}