This is the schema I plan to have for my message board app where users can create their own message boards:
{
"boards" : {
"jane-board" : {
// meta information like who created this board
},
"john-board" : {
// meta information like who created this board
},
...
},
"jane-board" : {
// data
},
"john-board" : {
// data
}
}
What would be the rule to ensure that "boards" cannot contain two "jane-board"?
I tried writing a rule but it fails:
{
"rules" : {
".read" : true,
"boards" : {
".write" : true,
"$board_name" : {
".validate" : "!newData.parent().hasChild($board_name)"
}
}
}
}
Since you are using the board's name as the key to store it under, there is already a guarantee that each board name can exist at most once.
It is not entire clear what you're trying to accomplish. But if you are trying to prevent a board's data from being overwritten, you can accomplish that with:
{
"rules" : {
".read" : true,
"boards" : {
"$board_name" : {
".write" : "!data.parent().hasChild($board_name)"
}
}
}
}
Changes for your rules:
I removed the ".write": true from boards. With that rule in place everyone can read all boards, since you cannot take permissions away on a lower level.
I changed the rule to a ".write" rule, since it feels more like preventing a write than validating structure
I check whether the new board already exists in the current data. You were checking in newData, but that doesn't make any sense: the new board will always exist in the new data.
Related
I'm developing an app using Firebase's Realtime Database and need to allow multiple users to access the same data, but I'm having trouble figuring out a security rule that makes this work.
The database looks like this:
teams: {
teamID3ic3kic9w3jkck : {
userIDs: ["11111", "22222", "33333", "44444"]
teamData { ....}
}
}
where I want to allow users with an ID matching any of the IDs in the "userIDs" array to access "teamData". Would really appreciate help figuring this out.
Every time you're looking to do array.contains(), you're likely using the wrong data structure. For example, this seems more like a mathematical set to me: an unordered collection of unique items. In Firebase you'd model that as:
teams: {
teamID3ic3kic9w3jkck : {
userIDs: {
"11111": true,
"22222": true,
"33333": true,
"44444: true"
]
teamData { ....}
}
}
Now you can secure this with:
{
"rules": {
"teams": {
"$teamid": {
".read": {
".read": "data.child('userIDs').child(auth.uid).exists()"
}
}
}
}
}
This is the database for example:
"Messages" : {
"Message1" : {
"Uid" : "sampleid1"
"Text" : "hi"
},
"Message2" : {
"Uid" : "sampleid2"
"Text" : " hello"
}
}
I want only those users to read the messages whose uid is equal to the Uid field of Message#.
The structure of database given in firebase documentation(i.e. using user id based messages in the database where the node of each message represents the uid of the user who sent the message) doesn't achieve the goal of my project as I need to know the uid of the user who sent the message each time any user sends a message.
Therefore, please suggest the rules that would help me achieve my task as mentioned in this question
Also, when I applied certain rules on the above structure of database, I couldn't read any data because 'firebase rules are not filters'.
Please ignore the syntax and format of json written in above example as it is just for reference
Please help!
Structure your data so:
"messages" : {
"<receiver_uid>" : {
"msg_1" : {
"text" : "Hello world...",
"uid" : "<sender_uid>"
}
// more msgs for this receiver ...
}
}
the rules should be something like
{
"rules" : {
"messages" : {
"$receiver" : {
".read" : "auth.uid == $receiver", // only receiver shall read
".write" : "auth != false" // any authenticated user can write
}
}
}
}
I have a Firebase data structure that looks like:
-messages
-myFirstUsername
-5-2-16 04:02:23 AM
-message: 'messageOne',
-direction: 'outgoing'
-5-3-16 04:07:23 AM
-message: 'messageTwo',
-direction: 'outgoing'
-mySecondUsername
-5-4-16 04:02:23 AM
-message: 'messageOne',
-direction: 'outgoing'
-5-5-16 04:02:23 AM
-message: 'messageTwo',
-direction: 'incoming'
I would like to index all data using the message and direction nested child keys. Since the username keys under the messages object are dynamic, I'll need to set up my index on the messages object itself.
Will the following rule set up indices two levels deep?
{
"rules": {
"messages": {
".indexOn": ["message", "direction"]
}
}
}
You can specify dynamic .indexOn like this:
{
"rules" : {
"messages" : {
"$userId": {
".indexOn" : ["message", "direction"]
}
}
}
}
I can't seem to find it in the new docs, but here's the old docs description: https://www.firebase.com/docs/security/api/rule/path.html
This question already has answers here:
Restricting child/field access with security rules
(3 answers)
Closed 6 years ago.
I'm using the Objective-C API for Firebase to fetch data and am able to do so when my security rules (set via the Firebase online dashboard) don't utilize any wildcard paths, e.g.:
{
"rules": {
"user" : {
".read" : true,
".write" : true
},
"users" : {
".read" : true,
".write" : false
}
}
}
But when I try enact what should be identical security rules using wildcard paths and fetch objects, the completion handler never executes, e.g.:
{
"rules": {
"user" : {
".read" : true,
".write" : true
},
"users" : {
"$userId" : {
".read" : true,
".write" : false
}
}
}
}
I used the Firebase documentation at the following URL and can't figure out what I'm doing wrong: https://www.firebase.com/docs/security/quickstart.html
I don't think the problem is Objective-C specific, but just to be thorough I'm using the method -[FQuery observeSingleEventOfType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) { }] to fetch my data.
Update: Here's the output of po query for the particularly FQuery I'm using to debug:
(/users {
ep = 0;
i = hidden;
sp = 0;
})
Update 2: Here's my data structure, in case that is relevant:
{
"user" : {
"HhMeloQDY4" : {
"info" : {
"name" : "Anita Borg"
}
},
"QxnjCNOj3H" : {
"info" : {
"name" : "Charles Babbage"
}
},
"zeNalC4ktf" : {
"info" : {
"name" : "Beyoncé"
}
}
},
"users" : {
"HhMeloQDY4" : {
"hidden" : false
},
"QxnjCNOj3H" : {
"hidden" : false
},
"zeNalC4ktf" : {
"hidden" : true
}
}
}
Update 3: Here's my Objective-C code for how I create my FQuery object:
Firebase *firebase = [[Firebase alloc] initWithUrl:#"https://<my-app-name>.firebaseio.com"];
[[firebase childByAppendingPath:#".info/connected"] observeEventType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {
BOOL isConnected = [snapshot.value boolValue];
// broadcast whether app is connected to Firebase
}];
Firebase *directory = [firebase childByAppendingPath:#"users"];
FQuery *query = [directory queryOrderedByChild:#"hidden"];
query = [query queryEqualToValue:value];
[query observeSingleEventOfType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {
// data successfully retrieved from Firebase
}];
You have added read access at the path /users/specific_user_id/ but you're attempting to read at the path /users/, which has no read access allowed.
You'll need to provide access to the path you are attempting to read, not just a subset of its children. See security rules are not filters.
Edit: Just adding some ObjC code to clarify
With the query presented
Firebase *directory = [self.myRootRef childByAppendingPath:#"users"];
you are querying the nodes directly inside the users node. However, if you review the structure, what's inside the users node is not queryable as there are no rules directly under /users, where I have commented.
"users" : {
//OH NOES! There are no rules here!
"$userId" : {
".read" : true,
".write" : false
}
Your rules are inside the $userId, which represents and applies to that parent only
"$userId" : {
//these rules *only* apply inside each userId.
".read" : true,
".write" : false
}
So with your structure, this query would work and it would query the content inside users/HhMeloQDY4 only.
Firebase *directory = [self.myRootRef childByAppendingPath:#"users/HhMeloQDY4"];
So the end result is that you need to assign the rules directly under the /users node that will allow you to query for content within it's child nodes.
"users" : {
".read" : true,
".write" : false
"$userId" : {
}
This would allow you to read each node under users node ($userId and it's children) but not write to them.
I have created an firebase project witch has user and data endpoints and data has an author variable on which rule set is hanging.
The rules:
{
"rules": {
"data":{
"$data":{
".read":"auth.uid === data.child('author').val()"
}
},
"users": {
"$uid": {
".read":"auth.uid === $uid"
}
}
}
}
I was able to make that it is possible to access each object separately yet I do not know how to form a query that display all accessible objects under data.
The data structure:
{
"data" : {
"45464656" : {
"answers" : [ 0, 1, 5, 3, 2 ],
"author" : "twitter:5555"
},
"46456456" : {
"answers" : [ 5, 3, 2, 4, 2 ],
"author" : "twitter:5555"
}
},
"users" : {
"twitter:5555" : {
"name" : "Jhon",
"regDate" : 6546546546,
"snmae" : "Boom"
}
}
}
Is there some kind of filtering needed to do this or have I formed the access rules incorrectly?
You can't use Firebase queries/security rules as a filter. From the Firebase documentation on security rules:
Rules are applied in an atomic manner. That means that a read or write operation is failed immediately if there isn't a rule at that location or at a parent location that grants access. Even if every child path is accessible, reading at the parent location will fail completely.
Since you're not granting read access at the data level, your query (which presumably starts at data) will fail.