In continuation to my previous question I have below design for my application.
Design
A user who can login to the application
Logged in user can create customers which will be stored under node
whose value will be the current logged in userid
Here is how I add the data through my android application.
FirebaseInstance mFirebaseInstance = FirebaseDatabase.getInstance();
FirebaseDatabase mFirebaseDatabase = mFirebaseInstance.getReference("tbl-customers").child(FirebaseAuth.getInstance().getCurrentUser().getUid());
//This will create or fetch user id node under tbl-customers.
btnSave.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String name = inputName.getText().toString();
String email = inputCode.getText().toString();
String limit= inputLimit.getText().toString();
createUser(name, email,limit);
}
});
private void createUser(String name, String email,String limit) {
userId = mFirebaseDatabase.push().getKey();
Customer customer = new Customer(name, email,limit);
mFirebaseDatabase.child(userId).setValue(customer);
}
That's it.. In Database it is somehow represented as below:
tbl-customers
|___loggedInUserId1
|___customerID1
|___customerName
|___customerCode
|___customerLimit
|___customerID2
|___customerName
|___customerCode
|___customerLimit
|___customerID3
|___customerName
|___customerCode
|___customerLimit
|___loggedInUserId2
|___customerID4
|___customerName
|___customerCode
|___customerLimit
and I have rules defined as
{
"rules": {
"tbl-customers": {
".read": "auth != null",
".write": "auth != null",
"$custId": {
"customerName": {
".validate": "newData.isString() && newData.val().length < 100 && newData.val().length > 8"
},
"customerCode": {
".validate": "newData.isString() && newData.val().length<4 && !newData.exists() && newData.val().length>1"
},
"customerLimit": {}
}
}
}
}
Unfortunately, the data is inserted without considering any of the validation written for each properties. Even the empty data gets inserted.
I started thinking whether the rule written is of proper structure, because if I see the data inserted then it has 3 levels - tbl-customer-->loggedInUserId-->customerId but rules have been only written for tbl-customer-->customerId.
So I changed the rules as below.
{
"rules": {
"tbl-customers": {
".read": "auth != null",
".write": "auth != null",
"$user_id": {
".validate": "auth.uid===$user_id",
"$custId": {
"customerName": {
".validate": "newData.isString() && newData.val().length < 100"
},
"customerCode": {
".validate": "newData.isString() && newData.val().length<4 && !newData.exists()"
},
"customerLimit": {}
}
}
}
}
}
extending it to one more level by including,
"$user_id": {
".validate": "auth.uid===$user_id"
...
}
But now this throws Permission Denied Exception. Am out of ideas at this point of time. Could someone guide me in the right direction? I have referred lot of posts from my previous question but to my bad, I couldn't grab much information from it. Hope to find some clear explanation as on why rules validation are failing and why data gets inserted with above mentioned first rule.
I might be wrong here, but the .read and .write rules will always cascade down your tree regardless here, which the line ".write": "auth != null" is most likely where you're facing the problems.
It would be best to move this rule to where the ".validate": "auth.uid===$user_id" rule is and remove the validate rule. You would then change it to something like "auth.uid == $user_id". The 'validate' rule I believe is just for taking in a written input at that specific location and then accepting or rejecting.
This is how I believe it should look:
{
"rules": {
"tbl-customers": {
".read": "auth != null"
"$user_id": {
".write": "auth.uid === $user_id"
"$custId": {
"customerName": {
".validate": "newData.isString() && newData.val().length < 100"
},
"customerCode": {
".validate": "newData.isString() && newData.val().length<4 && !newData.exists()"
},
"customerLimit": {}
}
}
}
}
}
This structure will allow all logged in users to read the data, which you could restrict further to the $user_id location if you wish, similar to the write rule currently in place. This would then only allow users who match the $user_id read and write privileges.
CASE STUDY
To add to this, this is a structure I set up sometime ago that would allow an administrator the ability to add users:
{
"rules": {
"Administrator": {
".read": "auth != null"
},
"Users": {
"$user_id": {
".write": "$user_id === auth.uid",
".read": "$user_id === auth.uid"
}
}
}
}
Related
I made a chat application using firebase realtime database. Users can send private messages to each other. How should the rules part be? I keep getting emails from Firebase that the rules are not reliable.
This is my firebase Collections:
This is my firebase Rules:
{
"rules": {
".read": "auth.uid!=null",
".write": "auth.uid!=null",
}
}
Only an authenticated admin user can read and write their own admin-user data:
{
"rules": {
"admin-users": {
"$userId": {
".read" : "auth.uid === $userId",
".write": "auth.uid === $userId"
}
}
}
}
Or:
{
"rules": {
"admin-users": {
"$user": {
".read" : "data.child('userUid').val() === auth.uid",
".write": "data.child('userUid').val() === auth.uid"
}
}
}
}
An authenticated user can read all data under Users.
An authenticated user can can only update their own data except the 'userUid' field. Can only update the 'userUid' field if it is a new document creation ('!data.exists()') e.g when it is a signup/register.
An authenticated admin can read and write all the data in Users including the 'userUid' field.
{
"rules": {
"users": {
".read": "auth !== null",
"$userId": {
"userId": {
".write": "root.child('admin-users').child(auth.uid).exists() || !data.exists()"
},
"$other_fields": {
".write": "$userId === auth.uid || root.child('admin-users').child(auth.uid).exists()"
}
}
}
}}
Only Admins can read and write everything in under Chats
Authenticated users can read chat messages in which their userUid matches either 'senderId' or 'receiverId'
Authenticated users can write/update a message only if their userUid matches 'senderId'.
{
"rules": {
"chats": {
".read": "root.child('admin-users').hasChild(auth.uid)",
".write": "root.child('admin-users').hasChild(auth.uid)",
"$chat": {
"$message": {
".read": "auth !== null && data.child('receiverId').val() === auth.uid || data.child('senderId').val() === auth.uid",
".write": "auth !== null && !data.exists() || data.child('senderId').val() === auth.uid"
}
}
}
}}
These sample rules are just to give guidance on how security rules work. You can create different rules that can work as well with your use case. See link for more details https://firebase.google.com/docs/rules/insecure-rules
https://firebase.google.com/docs/database/security/core-syntax
I created my app with the following database structure
Root/
Users/
+12345566777
+12345667765
+43223456677
And here it is an example of my security rules
{
"rules": {
"$uid": {
"Users": {
".read": "auth != null",
".write": "auth != null && auth.uid == $uid"
}
}
}
}
My problem is that instead of verifiying the auth.uid inside of my security rules i want to verify the (identifiant) the UserPhoneId is there any way to do that, thank you.
My Firebase Database structures is as follows:
{
"accounts" : {
"API_Keys" : {
"Unique ID over here" : {
"api_key" : "None",
}
}
},
"users" : {
"Unique UID over here" : {
"email" : "abc#gmail.com",
"username": "abc",
"age": "18"
},
}
}
I have implemented the following security rules for this.
{
"rules": {
"accounts": {
"API_Keys":{
"$uid":{
".read": "$uid === auth.uid && auth != null ",
".write": "$uid === auth.uid && auth != null"
}
}
},
"users": {
"$uid":{
".read": "$uid === auth.uid && auth != null ",
".write": "$uid === auth.uid && auth != null"
}
}
}
}
The user is going to link in his personal details and also an API Key. No user should be allowed to access any one else's keys somehow. Are the above rules satisfactory for this? Am i missing something?
It looks OK to me, though usually you reverse the order of the test to "auth != null && auth.uid == $uid", as seen in the documentation. You can test for yourself using the rules simulator in the Firebase console.
I am trying to extend a social android aap, currently its rules are public, its data structure is something like this,
{
"posts" : {
"postId1" : {
"authorId" : "abcd",
},
"postId2" : {
"authorId" : "abcd",
},
"postId3" : {
"authorId2" : "wxyz",
},
"postId4" : {
"authorId2" : "wxyz",
}
}
}
I want to allow an authenticated user to create and delete his own post in "posts" node
I tried this,
{
"rules": {
".read":"auth.uid != null",
".write":false,
"questions": {
"$uid": {
".write": "$uid === auth.uid"
}
}
}}
But this does not allow a user to create a post although a user can edit or delete his pre-existing post in "posts" node, it seems that there is no write permission within the "posts" node.
But if i allow write permission for "posts" then due to cascading rules, every authenticated user can access other's data. How can I achieve my desired functionality?
First please see firebase-bolt for writing rules for real time database: https://github.com/firebase/bolt
This tool makes it easy to write rules.
And for your rules here is a sample which will allow only the author to update post:
{
"rules": {
".read": "auth.uid != null",
"posts": {
"$postId": {
".write": "data.val() == null && newData.child('uid').val() == auth.uid || data.val() != null && newData.val() != null && data.child('uid').val() == auth.uid || data.val() != null && newData.val() == null && newData.child('uid').val() == auth.uid"
},
".read": "auth.uid != null"
}
}
}
and the firebase bolt equivalent is below :
path / {
read() {auth.uid != null}
}
path /posts {
read() {auth.uid != null}
/{postId}{
create() { this.uid == auth.uid}
delete() { this.uid == auth.uid}
update() { prior(this.uid) == auth.uid}
}
}
in my Android app I want to implement real-time database by Firebase.
The rules are not very simply:
--AUTHOR: write periodicals strings
----ADMIN: create a "chat/room" and invite friends
------MEMBER: read periodicals strings and write own data.
Also:
ADMIN is also a MEMBER.
Every "chat/room" is about 10 MEMBERS.
MEMBER can write its own data and read periodical strings (by author) and the data of the
other 9 members.
Actually, my rules' code is:
{
"rules": {
"chats": {
"$chatID": {
"messages": {
".read": "data.parent().child('members').child(auth.uid).exists()",
".write": "data.parent().child('members').child(auth.uid).val() == 'owner' || data.parent().child('members').child(auth.uid).val()=='chatter'"
},
"members": {
".read": "data.child(auth.uid).val() == 'owner'",
".write": "data.child(auth.uid).val() == 'owner' ||(!data.exists()&&newData.child(auth.uid).val()=='owner')"
},
"pending": {
".read": "data.parent().child('members').child(auth.uid).val() === 'owner'",
".write": "data.parent().child('members').child(auth.uid).val() === 'owner'",
"$uid": {
".write": "$uid === auth.uid && !data.exists() && !data.parent().parent().child('members').child($uid).exists()"
}
}
}
}
}
}
I'm not able to work with code. Anyone can help me? Thanks.