rules for rbac using firebase

Viewed 92

I'm trying to implement a role based access control using reactjs and firebase. My plan is to use the built-in authentication methods (email and password and google auth). The uuid will be saved in a collection called users.

I won't be using custom claims since I'll be storing a lot of data in users collection i.e profile picture, username, first name,last name etc.

So along with all this data I'm going to store another property called isAdmin which will be boolean value.

My users will be able to

  • register,
  • login
  • update profile
  • create posts
  • edit/delete own post
  • make post public/private
  • make collection of posts and make that collection private or public

My admin will be able to:

  • login
  • view user data
  • delete any user post/collection no matter it's visibility(private/public)
  • List item

Anyone not signed in will be able to:

  • View any public post or collection

This is the rules that I have written so far:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId} {
      allow create
      allow read
      allow write: if request.auth.uid == userId || request.auth.token.isAdmin == true
    }
    match /posts/{postId}{
        allow read
    }
    match /collection/{collectionId}{
        allow read
    }
  }
}

I've no idea if I can make public/private using firebase and if any of my rule is correct. Need some insight.

Update 1: This is my re-written rules for firestore:

service cloud.firestore {
  match /databases/{database}/documents {
    function isPublic(){
        return resource.data.visibility == "public";
    }
    function isAdmin() {
      return request.auth.token.role == "admin";
    }

    function isUser(userId) {
      return request.auth.token.role == "user" && request.auth.uid == userId;
    }

    match /posts/{post} {
      allow write: if isAdmin() || isUser(userId); // only admins and users themselves can write
      allow read: if isPublic(); // any body can read if the visibility is public
    }
    match /users/{userId}{
        allow read: if true; // anyone can see a user
      allow write: if isAdmin() || isUser(userId); // only the user themselves or the admins can write
    }
  }
}

As mentioned above only the user who created a post will be able to write his/her own post so I guessed I'll need to pass the userId as well but the line: allow write: if isAdmin() || isUser(userId); is throwing in errors. How can I pass the postId and the userId at the same time to the match function?

1 Answers

This is what I came up with. Up until now everything works fine, I'll change the answer if I see any problem as soon as I created my frontend.

I've also provided comments for better understanding:

service cloud.firestore {
  match /databases/{database}/documents {
    // anyone can view the user
    // only the user who created the account and admin can edit accout
    // only the user who created the account and admin can delete accout
    match /users/{userId}{
      allow create: if true
      allow read: if true
      allow write: if request.auth.uid == userId // check for admin
      allow delete: if request.auth.uid == userId // check for admin
    }
    
    // anyone can view the link if it's public
    // only the author of the link and admin can edit/update/delete the link
    match /posts/{postId} {
      allow create: if request.auth.uid != null
      allow read: if true && request.resource.data.visibility == "public"
      allow write: if request.resource.data.author_id == request.auth.uid // check for admin
      allow delete: if request.resource.data.author_id == request.auth.uid // check for admin
    }
    
    // anyone can view the collection if it's public
    // only the author of the link and admin can edit/update/delete the collection
    match /collections/{collectionId}{
      allow create: if request.auth.uid != null
      allow read: if true && request.resource.data.visibility == "public"
      allow write: if request.resource.data.author_id == request.auth.uid // check for admin
      allow delete: if request.resource.data.author_id == request.auth.uid // check for admin
    }
  }
}

Update: After testing the rules work. I strongly advise anyone who want to take help from this answer create individual functions like isAdmin() and isOwner() etc. Clears a lot of the clutter

Related