1
votes

Imagine we have Chat application and in this application, we have many rooms, some private and some for everyone. Every room has an admin who can manage users (can invite and remove). Only members of the room can read and write messages. An Admin is a person who created a room in this scenario.

I want to create security rules on room creation and update it on membersChange so only members can read and write the content of the message board.

In this case, that's how it could look like:

databse/rooms/
             private1 
                admin: memberX
                members: member1, member2
                //only admin can write into members fields
                messages
                    message1...
                    message2...
                    message3...
                //only members can write and read messages

             private2 
                admin: memberXY
                members: member1, member4
                //only admin can write into members fields
                messages
                    message1...
                    message2...
                    message3...
                //only members can write and read messages

So is it possible to create and update security rules from cloud function instead of manually updating them in firebase console? Or is there any way to automate this process?

I noticed that I can deploy security rules using CLI. What should be the process here? When do I call it? How can I get members from the database?

EDIT: for anyone who wants more information check How to Build a Secure App in Firebase

2
This sounds like a bad practice or maybe an XY-problem. Is it a possibility to change your database structure for example?André Kool
@AndréKool it's possible but how would you change it without losing some features? and why is it bad practice?svkaka
I suggest you look into something like member can read/write IF his UID is in /members for a possible solution. Personally I'm only familiar with the realtime database and I know for a fact thats possible there so I believe it's also possible for firestore.André Kool
You can also take a look at this questionAndré Kool
Unfortunatly @FrankvanPuffelen forgot to put an @ in front of my name so I didn't see it immediatly. But I will try to make some time for it later today. I won't mind if someone else beats me to it :)André Kool

2 Answers

3
votes

I would rethink this model. Instead of updating the security rules all the time, I see several viable approaches:

Option 1

You can save which users can access a specific room on Firestore, and then on the security rules you can access the document for the room and see which if the authenticated user is in the list of authorized users. The problem with this is cost, because this will fire an extra database read for every operation, which can get expensive.

Option 2

You can create custom claims for the user using a cloud function, like this:

admin.auth().setCustomUserClaims(uid, {"rooms": "room1,room2"})

Then on the security rules you can check if the user has the claims to a specific room:

match /rooms/{roomId} {
  allow read: if roomId in request.auth.token.rooms.split(',');
}

I believe you can also save the claim as an array directly, but I haven't tested it.

For this option you need to take into consideration the size of the token, which has a limit and can cause performance problems if it's too big. Depending on your scenario you can create a smaller set of permissions and then set those to the rooms and the users.

Option 3

You could save the uid of the users who can access each document, and then check if the authenticated user's uid exists on that document. But this can get out of hand if you have too many users.

I would go with option 2 if it makes sense for your scenario. Or you could combine more than one of these techniques. My idea was to show a few of the possibilities so that you can choose what works for you.

2
votes

Having different rules for each room and dynamicly updating your rules is a bad idea. Here are a couples problems that come to mind with this solution:

Instead you can, first of all, split you datastructure into public rooms and private rooms: database/rooms/public/... and database/rooms/private/....

For securing your private rooms you can take a look at rules conditions and do something like: member can read/write IF his UID is in /members (pseudo code, won't work like this).

You can take a look at this question for an example.