So I am trying to give premission to read data from the parent but I am not sure what I am doing wrong, probably some syntax but I tryed a lot of combinations and I cant figure out why, what is the proper way to do it?
Thanks in advance!
tryed a lot of combinations with "}" but Its always says my syntax is wrong
"rules": {
"ca": {
"$date": {
"$game": {
///if($game.hasChild(auth.uid))
"$uid":{
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
}
}
}
if the user is from $game so is allowed to read data from there as well as read is own data.. Thanks!
Firebase security rules do not by themselves filter data.
If you want to retrieve only data that has a certain child, you'll need to query on the value of the child. E.g. ref.orderByChild("first_letter").startAt("A").endAt("Z"). Once you have such a query, you may be able to enforce it with query based security rules.
But your structure hints at a scenario that isn't well modeled. Your current data model makes it easy to find the users for a game, it does however not make it easy to find the games for a user. To make that equally easy, you'll need to add a secondary data structure where you store the game (IDs or data) for each user.
So something like:
"user_games": {
"$uid": {
"$gameid": true
}
}
Also see:
Firebase query if child of child contains a value
Firebase Query Double Nested
Related
Is there a way to add a firebase security rule that prevents certain items in a collection from being read based on a value within each child item?
My example:
JSON:
orders{
orderA: {
name: x,
company:a
isDeleted: true
}
orderB: {
name: y,
company:a
isDeleted: false
}
}
It would be great to restrict users to be only able to read all orders where isDeleted === false
My Rule as I currently have (NOT WORKING):
"rules": {
"orders": {
".indexOn": "companyId",
".read": "auth !== null && data.child('isDeleted').val() === false",
"$ord": {
".write": etc
}
},...
The above doesnt work because "data" doesnt represent the right object - I can only use data inside the "$res" area.
If I remove "&& data.child('isDeleted').val() === false" it works but of course brings back both records.
My request is something like this, so the $res doesn't apply - as I'm getting ALL orders by companyId
http://mysite.firebase.io/orders?auth="xyz"&orderBy="companyId"&equalTo="a"
Is it even possible for a "retrieve all" type REST call like this and to filter out certain values via the firebase security rules? Am I just as well to retrieve all and then filter them out once I get them back in the front end??
Firebase's server-side security rules don't filter data. I highly recommend checking out the documentation, and some previous questions on this topic, as it's a very common misconception.
Instead the rules merely ensure that any read (in this case) operation, adhere to your requirements. So for your ".read": "auth !== null && data.child('isDeleted').val() === false", rule that means that the server checks if the user is logged in (they are), and that the node they are reading has a child isDeleted that is false. And since /orders/isDeleted does not exist, the read gets rejected.
You can securely allow access to only undeleted data by combining a query that only selects undeleted nodes with security rules that validate this query. Based on the example in the documentation on query based rules that'd look something like:
"rules": {
"orders": {
".indexOn": "companyId",
".read": "auth !== null &&
query.orderByChild == 'isDeleted' &&
query.equalTo == false"
}
}
This will work to get only non-deleted nodes. But since you can only order/filter on one property, you can't then also filter on companyId. You could allow that by introducing a synthesized isDeleted_companyId property, as shown in my answer here: Query based on multiple where clauses in Firebase
I'm trying to implement a Firebase rules read restriction in a data model that has a few nested dynamic child nodes.
I have the following data model:
/groupMessages/<groupId>/<messageId>/
{
"senderId": "<senderId>",
"recipientId": "<recipientId>",
"body": "..."
}
groupId, messageId, senderId and recipientId are dynamic ids. I would like to attach a listener to the /groudId node to listen to new messages. At the same time I only want users to read the message where the senderId or recipientId matches a corresponding auth.token value.
Due to Firebase cascading rules, if I allow the read at the groupId level without restrictions, I can't deny them on the message level.
{
"rules": {
"groupMessages"
"$groupId": {
".read": "auth != null"
}
}
}
}
I also haven't found a way to restrict the read rule on the groupId level to check for sender/recipientId of a message.
Any suggestions greatly appreciated.
As you've found, security rules cannot be used to filter data. But they can be used to restrict what queries can be performed on the data.
For example, you can query for all messages where the current user is the sender with:
var query = ref.child("groupMessages").child(groupId).orderByChild("senderId").equalTo(uid);
And you can secure access to the group's messages to only allow this query with:
{
"rules": {
"groupMessages": {
"$groupId": {
".read": "auth.uid != null &&
query.orderByChild == 'senderId' &&
query.equalTo == auth.uid"
}
}
}
}
The query and rules now exactly match, so the security rules will allow the query, while they'd reject a broader read operation. For more on this, see query based rules in the Firebase documentation
You'll note that this only works for a single field. Firebase Database queries can only filter on a single field. While there are workarounds by combining multiple values into a single property, I don't think those apply to your scenario, since they only work for AND queries, where you seem to want an OR.
You also seem to want to query on /groupMessages instead of on messages for a specific group. That also isn't possible: Firebase Database orders/filters on a property that is at a fixed path under each child of the node where you run the query. You cannot query across two dynamic levels, as you seem to be trying. For more on this see: Firebase Query Double Nested and Firebase query if child of child contains a value.
The common solution for your problem is to create a list of IDs for each user, which contains just the IDs of all messages (and/or the groups) they have access to.
userGroups: {
uid1: {
groupId1: true,
groupId2: true
},
uid2: {
groupId2: true,
groupId3: true
}
}
With this additional data structure (which you can much more easily secure), each user can simply read the groups they have access to, and your code then reads/queries the messages in each group. If necessary you can add a similar structure for the messages themselves too.
Finally: this type of recursive loading is not nearly as inefficient as many developers initially think, since Firebase pipelines the requests over an existing connection.
I have sign up system where I want only users to sign up if they have a valid secret key which I shall provide to users who want to register. If key is in db, then proceed to sign up. Thus I have generated random non repeated 8 chars and stored them in the real time database in the following structure:
Secrets:
"x5f1n9v0":
"Status" : 1
"C8vT2xxY":
"Status" : 1
And so on
..
{
"rules": {
"secrets":{
"$secret": {
".read": true,
".write": false
}
}
}
}
First question regarding the aboves rules:-
In this case no one can add a new secret key ?
Also the read will only be valid if someone has a valid key from my list ? Nobody can read the whole list ? Any bugs in this ?
Now suppose another set of rules where I want to write to the child of each key iff the user knows the valid id.
If I change the rule for write to true, will this work and no bugs to hack it ?
"rules": {
"secrets":{
"$secret": {
".read": true,
".write": true
}
}
}
}
Thanks
In this case no one can add a new secret key?
With those first rules, only someone with administrative access can add keys.
Also the read will only be valid if someone has a valid key from my list? Nobody can read the whole list?
There is indeed no way to read the entire list with your first set of rules. Someone can only read a secret if they know its key.
With your second set of rules:
"secrets":{
"$secret": {
".read": true,
".write": true
}
}
Now anyone can write any secret. That is probably not what you want. If you want to only allow them to change the data that already exists under an existing key, you'll want to check if there is any data already:
"secrets":{
"$secret": {
".read": true,
".write": "data.exists()"
}
}
If you want them to not be able to change-but-not-delete the existing data, that would be data.exists() && newData.exists(). If you have additional requirements about the data formats users can write, you'll want to add those to a corresponding .validate rule.
Your last set of rules is both invalid and meaningless. It's invalid because $secrets is not defined. But even if it was defined $secrets === $secrets will always be true.
I am planning an app that would allow users to create posts which shouldn't be readable by other users until the date/time that the creator of the post has selected.
Is this possible using the Firebase Realtime Database? How would I implement something like this?
I guess that simply implementing it in client code would not be secure, since authenticated users could still GET all posts manually, even the "not yet published" ones? Can I use database rules to do it, even though each post would have their individual publish date/time?
Yes, it's possible with Firebase. All you need to do is to add a flag for each post with the default boolean value of false and a TIMESTAMP. This means that by default, the post cannot be readable by other users. Then you need to use a listener on that TIMESTAMP filed to see when the current date/time is equal with the date/time that the creator of the post has selected. If it's equal then just set the value of the flag to true. This means that the post can be readable by other users. That's it!
You can achieve this also using security rules like this:
//Ensure that data being read is less than or equal with the current date/time.
".read": "data.child('timestamp').val() <= now"
The only solution I can think of is creating an entirely new node scheduledPosts with it's own rules to only allow the creator to see/edit it before the publish date (if that's what you're aiming for).
{
"users": {
"uid_1": {
"someData": "data",
...
}
},
"scheduledPosts": {
"pid_1": {
"postData": "data",
"uid": "uid_1",
"publishDate": 1695840299, // must be in milliseconds since epoch
...
}
}
}
And your scheduledPosts's rules would look as follows:
{
"rules": {
"scheduledPosts": {
"$post_id": {
".read": "root.child('scheduledPosts').child($post_id).child("publishDate").val() < now || root.child('scheduledPosts').child($post_id).child("uid") === auth.uid",
".write": "root.child('scheduledPosts').child($post_id).child("publishDate").val() < now || root.child('scheduledPosts').child($post_id).child("uid") === auth.uid"
}
}
}
}
You can use read rule, with combination of now
The rule will look something like this:
".read": "(auth != null) && (data.child('publish_time').val() < now)"
So, I have an app that has a messaging feature, where two users can message each other. I am structuring my data in the "messages" node, where each node are the message threads between two users. Each node is named after the two uid's of the two users who are communicating, sorted alphabetically.
For example,
if user (dd77gg) and user (zz22ss) are in a conversation, the node would be named "dd77ggzz22ss". I know you can grant access in Security Rules by doing
{
"rules": {
"messages": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
}
}
But, in my case, the nodes are not simply named $uid, but rather two uid's merged together. So, my question is, how would I simply grant access, only if the current user's uid is found somewhere in the node name?
First of all do not sort your uids alphabetically, this may seem like a good idea but it is not deterministic. For example. Say the two uids were 'cat' and 'bca'. Sorting them alphabetically would give you 'aabcct'. Now suppose you have another two uids: 'cat' and 'cba'. If you were to sort them you will get the same concatenation of uids, you will get aabbcct. This will cause previous conversation data to be overwritten. Concatenating two uids is actually the best way to go. You do not need to sort them, you just have to have a determenistic way of concatenating them. Ie: who's uid should I place first.
Now to answer your question you could just use the contains method in the firebase rule.
{
"rules": {
"messages": {
"$uid": {
".read": "$uid.contains(auth.uid),
".write": "$uid.contains(auth.uid)"
}
}
}
}
You just check if it contains one of the users id, if it does then they can read it of course. I hope this helps.