Kevin Shi
Kevin Shi

Reputation: 516

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.

Upvotes: 1

Views: 252

Answers (1)

Agung
Agung

Reputation: 13853

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.

Upvotes: 3

Related Questions