Firebase security rules for child items in arrays - firebase

I have the following data structure in my firebase:
{
"groups" : {
"-KEFQ7rTQscPX4hqn6ec" : {
"createdBy" : "b5cd3a86-108e-4ef3-9ab8-8a1e4da7491c",
"description" : "Test",
"isPublic" : true,
"title" : "T1"
},
"-KEFQao_Wd-Y-nLzIx2e" : {
"createdBy" : "b5cd3a86-108e-4ef3-9ab8-8a1e4da7491c",
"description" : "B",
"isPublic" : false,
"title" : "E"
}
}
and am trying to achieve the following:
Everybody can read all groups with "isPublic" == true
Only logged in users can see the groups that they have created
My first approach is:
{
"rules": {
"groups": {
".read": true,
"$id": {
".read": "data.child('isPublic').val() === true"
}
}
}
}
This stackoverflow post explains why it doesn't work, but I couldn't figure out how I can make it work.
Edit 1:
This post has a solution for the public/private problem (my 1. question) but not for the second question.
Edit 2:
Thanks to #VonD for the working solution for the public/private problem.
With this solution the problem with public/private is solved. Considering that a private group has many members and the user ids of them would be stored in another array "members" - how would I only allow access to the group if I am a member?
"privateGroups": {
"b5cd3a86-108e-4ef3-9ab8-8a1e4da7491c": {
"-KEFQao_Wd-Y-nLzIx2e" : {
"createdBy" : "b5cd3a86-108e-4ef3-9ab8-8a1e4da7491c",
"description" : "B",
"title" : "E",
"members": [userId1, userId2, userId3...]
}
}
}

The required security rules cannot be implemented with your document structure : if you want to express that a given user can read some of the child documents of a given node, the user will only be able to access them with their full path, for example "groups/-KEFQao_Wd-Y-nLzIx2e", but he will not be able to retrieve a list of groups matching the given criteria (unless of course you maintain at a different path a list of the groups the user can access, which means you would duplicate all public groups ids for each user).
A document structure that would better fit firebase security rules would be :
{
"publicGroups": {
"-KEFQ7rTQscPX4hqn6ec" : {
"createdBy" : "b5cd3a86-108e-4ef3-9ab8-8a1e4da7491c",
"description" : "Test",
"title" : "T1"
}
},
"privateGroups": {
"b5cd3a86-108e-4ef3-9ab8-8a1e4da7491c": {
"-KEFQao_Wd-Y-nLzIx2e" : {
"createdBy" : "b5cd3a86-108e-4ef3-9ab8-8a1e4da7491c",
"description" : "B",
"title" : "E"
}
}
}
}
Security rules would then be easy to implement:
{
"publicGroups": {
".read": true
},
"privateGroups": {
"$userId": {
".read": "auth.uid === $userId"
}
}
}
Hope it helps.

Related

Get list of items based on user id (from array of user id) firebase Realtime Database

I want to fetch the list of devices based on user id from the array of users list. data base shown below.
{
"devices" : {
"aglow6534" : { // Device ID
"icon" : "switch",
"name" : "AC",
"state" : {
"switch" : true
},
"type" : "RLY",
"users" : {
"rIXU5HucbVMTt3foI3AG7q5wBND3" : true // User ID
}
},
"aglow8728" : { // Device ID
"icon" : "bulb",
"name" : "bulb",
"state" : {
"intensity" : 45,
"switch" : true
},
"type" : "DIMR",
"users" : {
"YBJAlwc0vQSmd1TYe190Of8V57y1" : true, // User ID
"rIXU5HucbVMTt3foI3AG7q5wBND3" : true // User ID
}
}
},
"users" : {
"YBJAlwc0vQSmd1TYe190Of8V57y1" : { // User ID
"age" : 28,
"name" : "vipul garg"
},
"rIXU5HucbVMTt3foI3AG7q5wBND3" : { // User ID
"age" : 17,
"name" : "puneet"
}
}
}
These are my rules, i tried my best to fetch data, please help i almost spend 2 days on it.
{
"rules": {
"devices" : {
".read" : "root.child('users').child(auth.uid).child('devices').hasChild(data.child('/').val())",
}
},
}
Required Result:
Example 1: let User Id: YBJAlwc0vQSmd1TYe190Of8V57y1
"devices" : {
"aglow8728" : {
"icon" : "bulb",
"name" : "bulb",
"state" : {
"intensity" : 45,
"switch" : true
},
"type" : "DIMR",
"users" : {
"YBJAlwc0vQSmd1TYe190Of8V57y1" : true,
"rIXU5HucbVMTt3foI3AG7q5wBND3" : true
}
}
},
Example 2: let User Id: rIXU5HucbVMTt3foI3AG7q5wBND3
"devices" : {
"aglow6534" : {
"icon" : "switch",
"name" : "AC",
"state" : {
"switch" : true
},
"type" : "RLY",
"users" : {
"rIXU5HucbVMTt3foI3AG7q5wBND3" : true
}
},
"aglow8728" : {
"icon" : "bulb",
"name" : "bulb",
"state" : {
"intensity" : 45,
"switch" : true
},
"type" : "DIMR",
"users" : {
"YBJAlwc0vQSmd1TYe190Of8V57y1" : true,
"rIXU5HucbVMTt3foI3AG7q5wBND3" : true
}
}
},
I think you need to fully understand Firebase Realtime Database Rules first and other 8 links in Security & Rules section
Your rules is wrong on syntax. It should be something like this, maybe
{
"rules": {
"devices": {
"$devices_id": {
".read": "true",
".write": "false" // false if want to read only
}
},
"users": {
"$users_id": {
".read": "auth != null && auth.uid == $users_id" // this is for If you wanted to restrict access to this data such that only the logged-in user can see their own data
".write": "auth != null && auth.uid == $users_id"
}
}
}
}

Firebase realtime rules how to check if node with AutoID contains correct data

I am trying to write a firebase rule that checks to see if you have access to a child of one node by checking the data of another node. The issue I am having is accessing the other node's data because it is stored under an AutoId. How would I access the data under the AutoId?
Here is my database structure:
{
"products" : {
"Product001" : {
"ownerId" : "User002",
"productName": "Name"
},
"product002" : {
"ownerId" : "User001",
"productName": "Name"
}
},
"shares" : {
"share001" : {
"accepted" : true,
"ownerId" : "User002",
"productId" : "Product001",
"userEmail" : "example#email.com"
},
"share002" : {
"accepted" : true,
"ownerId" : "User001",
"productId" : "Product002",
"userEmail" : "email#exaple.com"
}
},
"users" : {
"User001" : {
"email" : "example#email.com",
"firstName" : "John",
"lastName" : "Smith"
},
"User002" : {
"email" : "email#example.com",
"firstName" : "John",
"lastName" : "Smith"
}
}
}
Here is the section of the rules that is causing me a problem:
"products":{
"$productId":{
".read":"root.child('shares').child($shareId).child('productId').val() === $productId && root.child('shares').child($shareId).child('ownerId').val() === auth.uid",
".write":"root.child('shares').child($shareId).child('productId').val() === $productId && root.child('shares').child($shareId).child('ownerId').val() === auth.uid"
}
}
Essentially, a user is only allowed to access a premises if they are in a share that contains their information.
Thank you for any advice.
There is no way in security rules to query or search another node, as the performance of that would be way too unreliable. So in your current data model it is not possible to allow the user access to a product if they have any shares for that product.
You'll need to have a list somewhere in a knowable path of the products the user can read. For example, say that you keep a list of the products the user is interested in:
"interests" : {
"User001" : {
"Product001": true,
"Product002": true
},
Now you can allow the user access to all products they follow with:
"products":{
"$productId":{
".read":"root.child('interests').child(auth.uid).child($productId).exists()",
".write": ...
}
}

Firebase security rules, data variable

I have a database schema like this:
Posts
unverified
post_1
post_2
...
And here is a shortened list of data in /posts/unverified path
{
"-L7xmY2HMeEImDZnZqTf" : {
"categorie" : 0,
"commonFields" : {...},
"fbKey" : "-L7YM7vEf8RpcxGUTpDE",
"images" : [...],
"specialFields" : {
"area" : "150",
"productPrice" : {
"currency" : "dollar",
"value" : "65000"
},
"requestType" : "sell",
"requesterType" : "personal",
"roomCount" : 3,
"suburbia" : "false"
},
"subCat" : 0,
"timestamp" : 1521011840178,
"uid" : "Fo5f6VonWgQVpsf6u80TPgoi2it2"
},
"-L7YNUZPL1-Dl7EhScEE" : {...},
"-L7YNUZPL1-fdfasfa" : {...},
"-L7YNUZPL1-ljljklfd" : {...},
"-L7YNUZPL1-lkjlkjfas" : {...},
}
And a firebase security rules defined as below
{
"rules": {
"posts": {
"unverified": {
".indexOn": "timestamp",
".read": "data.child('uid').val() === auth.uid"
As the rules show, I want to read unverified posts if the users id equal to the uid field in the post data
But every time i use the rules simulator ( or test the with the codes ) with the below configuration, its' giving Simulated read denied error
Thanks for your helps
Your .read rule is defined on /posts/unverified and checks a child called uid. So the total path is /posts/unverified/uid.
You're trying to read /posts/unverified/-L7xmY2HMeEImDZnZqTf. And the UID value is defined on /posts/unverified/-L7xmY2HMeEImDZnZqTf/uid.
The two paths don't match. You need to add a wildcard in your rules:
{
"rules": {
"posts": {
"unverified": {
".indexOn": "timestamp",
"$postid": {
".read": "data.child('uid').val() === auth.uid"

Firebase unable to write Security Rules for Unique Data

I want my vanNumber to be unique
//vanwithagent is after root "/vanwithagent"
vanwithagent : {
"-KSHJyDyI49RpZwSkdg1" : { //
"agentMobile" : "sdfs",
"agentName" : "sdfsdf",
"isAgentAssignedWithTask" : false,
"vanName" : "fsdf",
"vanNumber" : "sf",
"vanPresentLocation" : {
"currLattitude" : "N/A",
"currLongitude" : "N/A",
"pin" : "N/A"
}
}
}
Rules I have written:
{
"rules": { //Rules
".read": "auth != null",
".write": "auth != null",
"vanwithagent": {
"$vanwithagentId": {
"vanNumber":{
".validate":"!(root.child('vanwithagent').child(data.child('vanNumber').val()).exists())"
}
}
}
}
}
You cannot check if a value exists in a certain collection of values within Firebase's security rules. You can check if a certain key exists and if a key has a certain value. But this don't help you in this data model.
As usual the solution is to pick a data model that matches your requirements. If vans are unique and have an id, then store the list keyed under that id:
vanwithagent : {
"sf" : { // vanNumber
"agentMobile" : "sdfs",
"agentName" : "sdfsdf",
"isAgentAssignedWithTask" : false,
"vanName" : "fsdf",
"vanPresentLocation" : {
"currLattitude" : "N/A",
"currLongitude" : "N/A",
"pin" : "N/A"
}
}
}
This data structure guarantees that the van number is unique, without writing any security rules for it.

Firebase Realtime Database - Rules - Does two objects have at least one common child?

I am trying to check, if two objects have at least one common child. In the following example I want to be able to be control, if people can read org.money.value.
The right to read is determined by comparing the children of org.keys and users.{auth.uid}.keys. If there is a common key, reading would be allowed.
Database JSON:
{
"org" : {
"keys" : {
"red" : {
"value" : "..."
},
"blue" : {
"value" : "..."
}
},
"money" : {
"value" : "..."
}
},
"users" : {
"John" : { // in reality John == auth.uid of a user
"keys" : {
"red" : {
"value" : "..."
}
}
},
"Alice" : { // in reality Alice == auth.uid of a user
"keys" : {
"green" : {
"value" : "..."
}
}
}
}
}
Rules:
"rules:"{
"org" : {
"money" : {
// can read if "org.keys" and "users.auth.uid.keys"
// have at least one common child name.
// With the above data reading would be allowed for John,
// but not for Alice.
".read" : what to write here?
}
}
}
Is it possible to make this work?
By the way, the organization does not know the auth.uid of users.
I can't think of any way that you could do this determination in the JSON rules with your current database structure. I would suggest altering your structure to allow for this type of read determination. Here's a potential solution I came up with, which will require more filtering on client side:
When you create a new user key, loop through the org keys to see if it is already contained there. If so, add a BOOL to the user object, perhaps "canReadMoney" and set it to true. Then, your rule for money would look something like this:
"rules:"{
"org" : {
"money" : {
".read" : "root.child('users').child(auth.uid).child('canReadMoney').val==true"
}
}
}
another solution could be storing endpoint in the database like this
usersShareOrg
{
"John": { "Org" : true }
"Alice": {"Org": false}
}
and these values would be calculated and stored every time you added new user or org.keys entity.

Resources