I'm trying to add security rules to my firebase application. But auth.uid is not working..
Here is two of my security rules..
"groups": {
"$groupid":{
".read" : true,
".write" : "root.child('groupmembers').child($groupid).child('admins').child(root.child('users').child(auth.uid).child('username').val()).val()==true"
}
},
"groupchat":{
"$chatid":{
".read" : "root.child('groupmembers').child($chatid).child('members').child(root.child('users').child(auth.uid).child('username').val()).val()==true",
".write" : "root.child('groupmembers').child($chatid).child('members').child(root.child('users').child(auth.uid).child('username').val()).val()==true"
}
}
On the first rule auth.uid is working.. But in case of second and rest other rules, auth.uid is not working at all. I added my original uid in place of auth.uid and all rules are working properly.
Client side operation example..
Map<String, Object> map = new HashMap<String, Object>();
map.put("username", userid);
map.put("message", userid + " Created This Group.");
map.put("timestamp", ServerValue.TIMESTAMP);
map.put("zcode",chat_key);
map.put("type", "action");
FirebaseDatabase.getInstance().getReference()
.child('groupchat')
.child(groupid)
.push()
.updateChildren(map);
Users Structure
"users":{
"my_uid" : {
"email" : "example#gmail.com",
"username" : "user1"
}
}
groupmembers structure
"groupmembers":{
"groupid":{
{
"admins" : {
"user1" : true,
"user3" : true
},
"members" : {
"user1" : true,
"user2" : true,
"user3" : true,
"user4" : true
}
}
}
}
So how can I solve this problem?
I think it was because of the version mismatch or different packages used for it so it can't can the auth.uid
Related
In android app I create a firebase user authenticated by phone number and it works fine then I update this user adding display name and this is fine too; in the end I want to save user's data into firebase database so I wrote this rule:
"users" : {
"$user_id" : { // firebase user uid
".read" : "$user_id === auth.uid",
".write" : "$user_id === auth.uid",
".validate": "newData.hasChildren(['enabled', 'group', 'name'])",
"enabled" : { ".validate" : "newData.isBoolean()" },
"group" : { ".validate" : "newData.isString()" },
"name" : { ".validate" : "newData.isString()" },
}
}
This doesn't work and I get returned "permission denied" altough the simulator shows everything is fine (in the simulation I use custom authentication and set firebase as provider with the user's uid).
the user's class is:
public class User {
public boolean enabled;
public String group;
public String name;
public User() { }
public User(boolean enabled, String group, String name) {
this.enabled = enabled;
this.group = group;
this.name = name;
}
}
and i try to save it by:
private void saveUserToDB() {
User newUser = new User(true, "operators", currentUser.getDisplayName());
mRootRef.child("users").child(currentUser.getUid()).setValue(newUser, new DatabaseReference.CompletionListener() {
#Override
public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
if (databaseError != null) {
Log.e("ERROR", "Data could not be saved " + databaseError.getMessage());
} else {
Log.e("SUCCESS", "Data saved successfully.");
}
}
});
}
Can anybody kindly help me to understand why this happens?
Thanks
UPDATE
below is a "schema" of how the database should be:
{
"messages" : {
"1763409620982" : { // timestamp
"position" : "position text",
"sender" : "sender name",
"text" : "message text"
},
"1763409734527" : { // timestamp
"position" : "position text",
"sender" : "sender name",
"text" : "message text"
}
...
},
"users" : {
"Yi79w0HgA4ZTNAMHadixzoO5PaR2" : { // firebase user uid
"enabled" : true,
"group" : "group 1",
"name" : "user display name"
},
"kX22c0GgL4ZTNAMHallfgoO4pBN1" : { // firebase user uid
"enabled" : true,
"group" : "group 2",
"name" : "user name"
}
...
}
}
and here's the full set of rules:
{
"rules": {
"messages" : {
".read" : "root.child('users').child(auth.uid).child('group').val() === 'group 1'",
".write" : "root.child('users').child(auth.uid).child('group').val() === 'group 2'",
"$tstamp" :{
".validate": "newData.hasChildren(['position', 'sender', 'text'])",
"position" : { ".validate" : "newData.isString()" },
"sender" : { ".validate" : "newData.isString()" },
"text" : { ".validate" : "newData.isString()" },
}
},
"users" : {
"$user_id" : {
".read" : "$user_id === auth.uid",
".write" : "$user_id === auth.uid",
".validate": "newData.hasChildren(['enabled', 'group', 'name'])",
"enabled" : { ".validate" : "newData.isBoolean()" },
"group" : { ".validate" : "newData.isString()" },
"name" : { ".validate" : "newData.isString()" },
}
}
}
}
as to the class user it's defined as above and only used into the method saveUserToDB
Try to add push() before setValue(): It will add new sub-node containing all the fields from object of User class:
mRootRef.child("users").child(currentUser.getUid()).push().setValue(and so on...
May be you need write permission?:
I'm trying to restrict access to a particular groups if the user is not in the members list.
So this is my rule,
{
"rules": {
".read":"auth != null",
"groups": {
"$groupid":{
".read" : true,
".write" : "root.child('groupmembers').child($groupid).child('admins').child(root.child('users').child(auth.uid).child('username').val()).val()==true"
}
},
"groupchat":{
"$chatid":{
".read" : "root.child('groupmembers').child($chatid).child('members').child(root.child('users').child(auth.uid).child('username').val()).val()==true",
".write" : "root.child('groupmembers').child($chatid).child('members').child(root.child('users').child(auth.uid).child('username').val()).val()==true"
}
}
}
}
The first rule for "groups">"$groupid" works perfectly. But I'm getting problem with second rule for "groupchat"
DataStructure ex:
Group Chat structure
"groupchat" : {
"testinggroup" : {
"chat" : {
"-KiYN6RKZOyWI-qufWn3" : {
"message" : "Hey",
"timestamp" : 1493094660399,
"type" : "text",
"username" : "user2"
}
},
"lasttimestamp" : 1493094660674
}
}
Users Structure
"users":{
"ujciTL9oFKNylcgiGcdYqUxjcgA3" : {
"email" : "example#gmail.com",
"username" : "user1"
}
}
groupmembers structure
"groupmembers":{
"groupid":{
{
"admins" : {
"user1" : true,
"user3" : true
},
"members" : {
"user1" : true,
"user2" : true,
"user3" : true,
"user4" : true
}
}
}
}
groups structure
"groupid"{
{
"category" : "group category",
"creator" : "creator uid",
"desc" : "group desc",
"id" : "groupid",
"image" : "url",
"name" : "groupname"
}
}
Operation ex:
FirebaseDatabase.getInstance()
.getReference()
.child("groupchat")
.child(j)
.child("lasttimestamp")
.setValue(ServerValue.TIMESTAMP);
So to debug the errors
I added "myusername" in place of root.child('users').child(auth.uid).child('username').val()
and it's working.
But in the previous rule("For groups child") I added the same thing and it was working.
I don't know why I'm getting error.
This question already has answers here:
How do you prevent duplicate user properties in Firebase?
(2 answers)
Closed 6 years ago.
I am adding username for my Chat App to Firebase Database successfully.
I implemented in activity:
FirebaseDatabase firedb = FirebaseDatabase.getInstance();
in createUser Method.
private void createUser(String uname){
Map<String,Object> newUser = new HashMap<String, Object>();
newUser.put("uname",uname);
DatabaseReference dbref = firedb.getReference();
dbref.child("Users").push().setValue(newUser);
}
When user click a button this method works. This method work correctly but I want to make uname primary key. I do not want to add a username if it is alerady exists.
Database Structure:
Rules:
{
"rules": {
".read": true,
".write": true
}
}
Finally, how to prevent duplicated uname ?
You have to add a top level /uname/ Node. Your data will be like this for example :
{
"Users" : {
"user1" : {
"uname" : "value1",
},
"user2" : {
"uname" : "value2",
},
"user3" : {
"uname" : "value3",
},
},
"uname" : {
"value1": "user1",
"value2": "user2",
"value3": "user3",
}
}
Now you have to Enforce New Data Structure:
{
"rules": {
"users": {
"uname": {
".validate": "!root.child('uname').child(newData.val()).exists()"
},
}
}
}
My app loads links shared by a user on a listview. I'm trying to load 10-links/posts at a time, and once the user reaches the end query for 10 more links/posts, and so on.
I've denormalized the data, so that once a post is made, its created under "/links" and "user-links/$userId/" by the following code:
String key = mDatabase.child("links").push().getKey();
Map<String, Object> childUpdates = new HashMap<>();
childUpdates.put("/links/" + key, postValues);
childUpdates.put("/user-links/" + FirebaseAuth.getInstance().getCurrentUser().getUid() + "/" + key, postValues);
Firebase Data
links
{
"pushId1": {
"userId" : YuPfsnJD, //userId1
"createdAt" : 1483309151261,
"link" : https://www.youtube.com/watch?v=bwbwTKXlvQA
},
"pushId2": {
"userId" : zZio89, //userId2
"createdAt" : 1483309145896,
"link" : https://firebase.google.com/docs/database/android/lists-of-data
},
"pushId3": {
"userId" : YuPfsnJD, //userId1
"createdAt" : 1483309155525,
"link" : https://firebase.google.com/docs/database/security/indexing-data
}
}
user-links
{
"userId1":
{
"pushId1":
{
"userId" : YuPfsnJD, //userId1
"createdAt" : 1483309151261,
"link" : https://www.youtube.com/watch?v=bwbwTKXlvQA
},
"pushId3":
{
"userId" : YuPfsnJD, //userId1
"createdAt" : 1483309155525,
"link" : https://firebase.google.com/docs/database/security/indexing-data
},
},
"userId2":
{
"pushId2":
{
"userId" : zZio89, //userId2
"createdAt" : 1483309145896,
"link" : https://firebase.google.com/docs/database/android/lists-of-data
},
},
}
This is how I currently retrieve the data based on the time its createdAt:
Query myTopPostsQuery = mDatabase
.child("user-link")
.child(FirebaseAuth.getInstance().getCurrentUser().getUid())
.orderByChild("createdAt")
.limitToFirst(QUERY_LIMIT);
Where; int QUERY_LIMIT = 10;
Firebase Rules
{
"rules":
{
".read": "auth != null",
".write": "auth != null"
}
}
Questions:
1) Once the user reaches the end, how do I skip the first 10 links (which have already been loaded) and load the next 10-links?
2) Is there a way to assign Ascending or Descending order? I would like to show the latest post first.
3) Is firebase-rules ".indexOf" the solution? If so can someone share the exact rule that I must write?
Note: createdAt is generated by the following code while creating the data initially: postValues.put("createdAt", ServerValue.TIMESTAMP );
I can't figure out why firebase won't let me do this specific write. I feel like I do it the same in the simulator and it works there. Below are my security rules for this section.
"joinRequests" : {
"$clanid": {
"$requesterid" : {
".read" : "$requesterid === auth.uid || root.child('clans/' + $clanid + '/members/' + auth.uid + '/admin').val() === true",
".validate": "newData.hasChildren(['request'])",
"request" : {
".write" : "true",
".validate": "newData.hasChildren(['name', 'message'])",
"name" : {
".validate": "newData.isString()"
},
"message" : {
".validate": "newData.isString()"
},
"$other": {
".validate": false
}
},
"approved" : {
".write" : "root.child('clans/' + $clanid + '/members/' + auth.uid + '/admin').val() === true || ($requesterid === auth.uid && !newData.exists())",
".validate": "newData.isBoolean()"
},
"$other": {
".validate": false
}
}
}
},
In the code for my android app I run these two lines:
dataSnapshot.child("request").getRef().removeValue();
dataSnapshot.child("approved").getRef().removeValue();
What I find weird is that it allows me to remove the "approved" value but not the "request" value. The dataSnapshot is a $requesterid. If I run this line in the simulator and it allows the write:
/joinRequests/QV28VJYG/c1cef959-2dd3-4cab-8649-2b81892cffa6/request
The error I get in android studio is this:
W/RepoOperation: setValue at /joinRequests/QV28VJYG/qRlJt4UIAcRVIe9VXoYVBa68tO43/request failed: DatabaseError: Permission denied
It must be something dumb that I'm doing but I can't imagine what it would be. Any help would be great, Thanks.
newData is a snapshot that represent how data will look like after the operation took place.
newData.hasChildren(['request']) tells firebase to make sure that the request node exists after the operation was executed. Therefore, deletion of this node is prohibited.