I have a collection of signatures where each signature has a few properties: public: fullname, city and then email.
I want to keep the email property private and I've been struggling with writing the correct rules to only return fullname and city. Here is what my rules.json looks like so far:
{
"rules": {
"signatures": {
"$signatureID": {
"public": {
".read": true
},
"email": {
".read": false
}
}
}
}
}
When I go to the /signatures end point, I would like to receive an array of signatures with the public data and not receive the email addresses.
So far I haven't had any luck getting this to work the way I want it to. Am I doing something wrong? Should I structure my data differently?
With respect to security rules, Firebase operations are all-or-nothing.
As a result, attempting to load all of the data at /signatures will fail because your client does not have permission to read all of the data at that location, though you do have permission to read some of the data there. Similarly, writing to a location behaves the same way, and full permission is required before your operation will continue.
To handle this use case, consider restructuring your data like this:
{
"rules": {
".read": false,
".write": false,
"signatures-public": {
".read": true,
"$signatureID": {
// ... public data here
}
},
"signatures-private": {
"$signatureID": {
// ... private data here
}
}
}
}
Related
I'm planning to create a realtime database for a chatting apps with private message channel, is there any specific rules that we can check the authenticated user has access to this room?
my database structure is mostly like this:
{
"channel": {
"unique_room_id": {
"participants": {
"uid1": 1537259273000,
"uid2": 1537259273000
}
"message": {
....
}
}
}
}
Is it possible to use hasChild like how it is used on .write rule or we need to manually validate the reference which means it's not really possible to add more participants to the room?
If possible I want to avoid the latter, thanks in advance
Note:
I'm also open to any alternative structures, and maybe some explanation why it is recommended
It's always easiest to use top-level lists instead of nesting multiple entity types in a single list. So remodel your data to:
{
"participants": {
"unique_room_id": {
"uid1": 1537259273000,
"uid2": 1537259273000
}
}
"messages": {
"unique_room_id": {
....
}
}
}
Now you can ensure that only participants in a room can read its messages with:
{
"rules": {
"messages": {
"$roomid": {
".read": "root.child('participants').child($roomid).child(auth.uid).exists()"
}
}
}
}
I am attempting to rewrite my database rules to only allow members of a specific 'collection' to access that collection only if that member is included in the teams list. Referring to the image attached below, this is what my rule currently looks like:
{
"rules": {
"collection": {
"$collection_id": {
"$teams" : {
".read": "data.child('id').val() === auth.uid"
}
}
}
}
}
However this doesn't seem to work and I believe it's incomplete. How can I update my rules to match this design structure? If any change in my structure is necessary to support this, do pitch that and I will attempt to update the current production data.
You could reach into the teams node like this:
{
"rules": {
"collection": {
"$collection_id": {
".read": "data.child('teams').child(auth.uid).child('id').val() === auth.uid"
}
}
}
}
Right now you have unnecessary redundancy in the teams node. The id of the child node is repeated in its own child id. You can simplify your database and rules if you simply set teams/{teamId} = true in the database, your rule could look like this instead to allow only users listed under teams to read the entire collection:
{
"rules": {
"collection": {
"$collection_id": {
".read": "data.child(auth.uid).val() === true"
}
}
}
}
I tried to set some important write persmissions but I can't solve my problem. I got told that, if I add a write-rule to room, then I overwrite my room/$roomID/ingame rule.
What I'm trying to do is
Creating a room by auth users.
Set/update ingame of a room only by the creator of the room. (That works)
Rules:
{
"rules": {
".read": true,
"user": {
".indexOn": "displayname"
},
"room": {
"$roomID": {
"ingame":{
".write": "data.parent().child('creatorUid').val() == auth.uid"
}
}
}
}
}
How I call to create a new room:
let user = firebase.auth().currentUser
dbRoomRef.push().then((room) => {
room.set({
creatorUid: user.uid,
ingame: false,
})
}).catch(function(err) {
console.log(err.message)
}
)
Error message (as expected):
FIREBASE WARNING: set at /room/-L572bnuRv0_vntko-Bd failed: permission_denied
Thank you.
The error messages says that you're trying to write /room/-L572bnuRv0_vntko-Bd and have no permission to write there. That is correct, since your rules only give permission to write to /room/-L572bnuRv0_vntko-Bd/ingame.
If creatorUid is already set when you create the room, you don't have to include it in your write statement and can just do:
room.child("ingame").set(false);
If you're trying to allow everyone to create a new room (or write to an existing room) as long as they are the owner, you need to set your rules one level higher:
"room": {
"$roomID": {
".write": "newData.child('creatorUid').val() == auth.uid"
}
}
We are building a platform using Firebase Realtime Database and I'm having a bit of a struggle to find the best way to structure our data for private and public access.
Today we have
database: {
items: {
$userUid: {
$itemUid: {
poster_link: "..."
format: "..."
title: "..."
}
}
}
}
All our items are stored under each user in order to make it fast and secure to load.
Our rules are set up like this
{
"rules": {
"items": {
"$userId": {
"$itemId": {
".read": "auth !== null,
".write": "auth !== null"
}
}
}
}
}
So only an authorised user can read and write the data. I could create something like this to allow items to be public if the value is true:
".read": "auth !== null || data.child('public').val() == true"
But this will still be under $userUid
So I was wondering if you have any suggestion on how to structure this example to allow items to be under a user and also seen publicly, not necessary under this user, a bit like Dropbox does when you share something.
You chosen data structure does not take advantage of the flat data principles of Firebase. This will make it very difficult for you to query items of multiple users. For example, how do you get all the public items without drilling into each user?
Similarly, a boolean called public is also not good because you can't extend it to other ACL scenarios. Much better is an ACL object that can be extended in the future.
For example:
items: {
itemsUid: {
[...],
userId: ...,
ACL: { public: true }
}
}
Now you can write the rule:
auth !== null && (root.child(items/ACL/public).exsists() || data.userId === auth.UID)
If in three months you add a concept of friends that can see you posts or followers that can see you items you can simply add friends: true, followers: true to the ACL object and adjust the rule.
You can structure like this
database: {
items: {
$itemUid: {
poster_link: "..."
format: "..."
title: "..."
user: "userid"
}
}
}
now set the rules as
{
"rules": {
"items": {
"$itemId": {
".read": "auth !== null || data.child('public').val() == true,
".write": "auth !== null"
}
}
}
}
I have a collection of signatures where each signature has a few properties: public: fullname, city and then email.
I want to keep the email property private and I've been struggling with writing the correct rules to only return fullname and city. Here is what my rules.json looks like so far:
{
"rules": {
"signatures": {
"$signatureID": {
"public": {
".read": true
},
"email": {
".read": false
}
}
}
}
}
When I go to the /signatures end point, I would like to receive an array of signatures with the public data and not receive the email addresses.
So far I haven't had any luck getting this to work the way I want it to. Am I doing something wrong? Should I structure my data differently?
With respect to security rules, Firebase operations are all-or-nothing.
As a result, attempting to load all of the data at /signatures will fail because your client does not have permission to read all of the data at that location, though you do have permission to read some of the data there. Similarly, writing to a location behaves the same way, and full permission is required before your operation will continue.
To handle this use case, consider restructuring your data like this:
{
"rules": {
".read": false,
".write": false,
"signatures-public": {
".read": true,
"$signatureID": {
// ... public data here
}
},
"signatures-private": {
"$signatureID": {
// ... private data here
}
}
}
}