Read subset of firebase realtime database with rules restrictions - firebase

I have a firebase real-time database structured like this:
{
"USERS": {
"user-1": { ... },
"user-2": { ... }
}
"GROUPS": {
"group-1": {
"id": "group-1",
"AUTH": {
"user-1": true,
}
},
"group-2": {
"id": "group-2",
"AUTH": {
"user-2": true
}
},
"group-3": {
"id": "group-3",
"AUTH": {
"user-1": true,
"user-2": true
}
}
}
}
I know that I can give read permissions that would give read access to users that only belong to a specific group, like this:
{
"rules": {
"GROUPS": {
"$groupId": {
".write": false,
".read": "auth != null && data.child('AUTH').child(auth.uid).exists()"
}
}
}
}
I'm trying to find a query + rules combination that would return me all the groups that the logged in user belongs to.
For example:
A query and firebase rule change that for logged in 'user-1' returns a snapshot of:
{
"group-1": {
"id": "group-1",
"AUTH": {
"user-1": true,
}
},
"group-3": {
"id": "group-3",
"AUTH": {
"user-1": true,
"user-2": true
}
}
}
OR
[
{
"id": "group-1",
"AUTH": {
"user-1": true,
}
},
{
"id": "group-3",
"AUTH": {
"user-1": true,
"user-2": true
}
}
]
querying for the "GROUPS" reference now give me a 'permissions denied' error.
adding '.read=true' to the GROUPS:
{
"rules": {
"GROUPS": {
".read": true,
"$groupId": {
".write": false,
".read": "auth != null && data.child('AUTH').child(auth.uid).exists()"
}
}
}
}
or any combination for '.read' that passes the condition, gives everyone access to all the data.
Is there a way (in the current structure) to query the database and get from the "GROUPS" branch all the groups that I belong to by adding some restriction to the rules?
or do I need to maintain under each user the list of the group that he belongs to?

Related

Can I apply write rule for anyone for updating a particular item inside the object stored in firebase rt database?

I would like to add firebase database rule to allow anyone to increment the "count" inside the message object. Is it possible to do so? If so, how should I write the rule for updating the count? Thanks!
{
"messages": {
"message0": {
"content": "Hello",
"count": 5
},
"message1": {
"content": "Goodbye",
"count": 10
},
...
}
}
I tried below rule, but it doesn't seem to work:
{
"rules": {
"messages": {
"$message": {
"count": {
".read": true,
".write": true,
}
}
}
}
}

Security rule strange behaviour?

allowed access with blank UID
DATA
{
"roles": {
"admins": {
"123456789": {
"name": "Max"
}
}
},
"meta": {},
"events": {
"3423556": {
},
"meta": {
}
},
}
}
RULES
"rules": {
"events": {
".read": "root.child('roles').child('admins').hasChild(auth.uid)",
}
}
it returns faile if I'm trying
Type read Location /events/ Data null Auth { "provider": "anonymous",
"uid": "1" } Admin false
and true if UID is blank
Type read Location /events/ Data null Auth { "provider": "anonymous",
"uid": "" } Admin false

How to structure my data in Firebase

I'm new to the data structure with Firebase.
I read this: https://firebase.google.com/docs/database/ios/structure-data
I understood that it was necessary to use a flattened structure.
But I'm not sure how to do that in real world.
And I think that to use the Firebase rules correctly I need a suitable structure (but I have no experience with that).
I have 2 types of data:
A list of parts :
parts: {
part1: {
chapter1: {
lesson1,
lesson2,
...etc
},
chapter2: {
lesson1,
lesson2,
...etc
},
chapterX: {
...etc
},
},
part2: {
chapter1: {
lesson1,
lesson2,
...etc
},
chapterX: {
...etc
},
},
partX: {
...etc
}
}
And a list of users :
users: {
user1: {
name,
email,
...etc
},
user2: {
name,
email,
...etc
},
user3: {
...etc
},
etc...
}
Each user can validate a lesson and its progress must be saved.
If the user validates all the lessons: he validates the chapter.
And if he validates all chapters: he validates the part.
I am thinking about to structure my Firebase database like this:
{
users: {
$uid: {
name,
email,
...
}
},
checked: {
user1: {
parts: {
part1: true
},
chapters: {
chapter1: true,
chapter2: true
},
lessons: {
lesson1: true,
lesson7: true,
lesson15: true,
}
},
user2: {
parts: {
part3: true
},
chapters: {
chapter1: true
},
lessons: {
lesson18: true,
lesson57: true,
}
}
}
}
And for Firebase rules:
{
"rules": {
"users": {
"$uid": {
".write": "$uid === auth.uid"
}
},
"checked": {
"$uid": {
".write": "$uid === auth.uid"
}
}
}
}
Is this the right way to go?
Are the rules OK?
Thanks!
Based on the information you provided, you are doing the right way. And the rules are OK. The structure of your database fits the way you want to read them so you are doing the right way.

Firebase complex rules

Let's assume I have the following data structure in my Firebase database:
{
"allProjects": {
"foo": true,
"bar": true,
"baz": true
},
"allUsers": {
"user1": true,
"user2": true,
"user3": true
},
"projects": {
"foo": {
"name": "foo",
"members": {
"user1": true
}
},
"bar": {
"name": "bar",
"members": {
"user2": true
}
},
"baz": {
"name": "baz",
"members": {
"user1": true,
"user3": true
}
}
},
"users": {
"user1": {
"name": "user1"
},
"user2": {
"name": "user2"
},
"user3": {
"name": "user3"
}
}
}
Problem
I'm trying to write a few rules so I can protect my data. Here's a few requirements:
Users should only see projects they are part of (i.e. /project/:id/members/:userId === true)
Users should only see users that belong to the same projects as them.
Here's what I was able to come up with:
{
".read": false,
".write": false,
"allProjects": {
".read": "auth !== null"
},
"allUsers": {
".read": "auth !== null"
},
"projects": {
"$projectId": {
// requirement 1
".read": "data.child('members').hasChild(auth.uid)"
}
},
"users": {
"$userId": {
// requirement 2
".read": "???",
}
}
}
The typical approach would be to keep a list of users that each user can see.
user_friends: {
"user1": {
"user3": true
}
"user3": {
"user1": true
}
}
Then as users get added to/removed from projects, you'll need to keep this derived list up to date. Alternatively you can calculate this derived list in a schedule maintenance operation.
Duplicating data like this is one of the big differences between SQL databases and most NoSQL databases. In Firebase we often store extra data to allow for our use-cases. For a great introduction I recommend reading NoSQL data modeling and watching Firebase for SQL developers.
By the way, I noticed that you've combined metadata and the list of users under /projects/$projectid. This is not recommended. For best results, split them into two top-level lists.
"projects": {
"foo": {
"name": "foo",
},
"bar": {
"name": "bar",
},
"baz": {
"name": "baz",
}
},
"members": {
"foo": {
"user1": true
},
"bar": {
"user2": true
},
"baz": {
"user1": true,
"user3": true
}
},

Firebase: How to add an index for a nested field?

I have a locations-orders table that looks like this:
{
"0BW3H9T3R7HJB" : {
"orders" : {
"01750ea0-4980-4bc4-58b2-988c02324e671478636582396" : {
"created_at" : 1478636560000,
},
"01750ea0-4980-4bc4-58b2-988c02324e671478636582483" : {
"created_at" : 1478636560000,
}
}
}
Each location-orders node has an orders node with multiple keys/objects. Those objects have a created_at field on it.
I added this to my database Rules:
{
"rules": {
".read": true,
".write": true,
"users": {
".indexOn": "merchantId"
},
"merchants": {
".indexOn": "locations"
},
"locations-orders": {
".indexOn": ["orders/created_at"]
}
}
}
However, Firebase is still complaining that I'm missing an index:
Using an unspecified index. Consider adding ".indexOn": "created_at" at /locations-orders/1JS53G0TT5ZQD/orders to your security rules for better performance
Am I supposed to run something to create the index? Or is it written incorrectly?
=== UPDATE ===
I changed my file to look into:
{
"rules": {
".read": true,
".write": true,
"users": {
".indexOn": "merchantId"
},
"merchants": {
".indexOn": "locations"
},
"locations-orders": {
"$location_id": {
".indexOn": ["orders/created_at", "orders/status_updated_at"]
}
}
}
}
but I still get the same warning:
Using an unspecified index. Consider adding ".indexOn": "created_at" at /locations-orders/1JS53G0TT5ZQD/orders to your security rules for better performance
If you look at your data structure, there is no orders/created_at under /location-order/$orderId.
{
"rules": {
".read": true,
".write": true,
"users": {
".indexOn": "merchantId"
},
"merchants": {
".indexOn": "locations"
},
"locations-orders": {
"$someid": {
".indexOn": ["orders/created_at"]
}
}
}
}
Try this:
{
"rules": {
".read": true,
".write": true,
"users": {
".indexOn": "merchantId"
},
"merchants": {
".indexOn": "locations"
},
"locations-orders": {
"orders": {
"$orderid": {
".indexOn": "created_at"
}
}
}
}
}

Resources