Op.Kemal
Op.Kemal

Reputation: 127

Firebase security rule for data in same child

My database structure is.

Homeland
 +Ui4PSHU0kCPGpFJ6jWIJPOdLvEds
  +service
  +quta
  +period
+55dwaSHU0kCPGpFJ6jWIJPOdLvEds
  +service
  +quta
  +period

I want to make public "service" and "quta" childs, but only registered user can shown the "period" child.But there is an issue about able to my user directory.

First I want to test for able to my $uid.But I can't.It's work when I remove to $uid only use "Homeland".So it's didn't work

{
  "rules": {
    "Homeland": {
         "$uid" : {
           ".read": true,
    ".write": "auth !== null"
         },
        },
    ".read": "auth !== null",
    ".write": "auth !== null"
  }
}

This is work

{
  "rules": {
    "Homeland": {
           ".read": true,
    ".write": "auth !== null"
        },
    ".read": "auth !== null",
    ".write": "auth !== null"
  }
}

UPDATE

Change my security rules as samthecodingman mentioned.

{
  "rules": {
    "Homeland": {
      "$uid" : {
        "service": {
          ".read": true, // <-- publically readable
          ".write": "auth.uid === $uid" // <-- writable only by owner
        },
        "quta": {
          ".read": true, // <-- publically readable
          ".write": "auth.uid === $uid" // <-- writable only by owner
        },
        "period": {
          ".read": "auth !== null", // <-- readable by any logged in user
          ".write": "auth.uid === $uid" // <-- writable only by owner
        }
      },
    },
    "$other": { // <-- everywhere else not named above
      ".read": "auth !== null", // <-- allows read if logged in
      ".write": "auth !== null" // <-- allows write if logged in
    }
  }
}

Code

var kullanici = firebase.database().ref().child("Homeland").child(uservaluefrominput).child("service");
kullanici.on('value' ,function(datasnapshot) {


     sifre = datasnapshot.val();

     console.log(sifre);



});

But code and simulation both are denied the read value.

Catch the error.Note: This time I declared the user id clear.

try {

kullanici = firebase.database().ref().child("Homeland").child("Ui4PSHU0kCPGpFJ6jWIJPOdLvEO2").child("service");
kullanici.on('value' ,function(datasnapshot) {


     sifre = datasnapshot.val();

     console.log(sifre);



});

} catch (someError) {
  console.log('got some error: ', someError);
}

Upvotes: 0

Views: 55

Answers (1)

samthecodingman
samthecodingman

Reputation: 26171

It is important to note that database rules cascade. Even if you denied access to /users/$uid to anyone other than $uid but allowed access to /users/ to anyone, anyone would be able to modify the deeper location /users/$uid.

In your rules above, even though you have configured rules for /Homeland/$uid, you have allowed read/write access to the root database for any logged in user.

{
  "rules": {
    "Homeland": {
      "$uid" : {
        ".read": true,
        ".write": "auth !== null"
      },
    },
    ".read": "auth !== null", // <-- allows read everywhere if logged in
    ".write": "auth !== null" // <-- allows write everywhere if logged in
  }
}

To fix this, you can introduce the $others key so that a logged in user can read/write anywhere in your database except underneath /Homeland unless otherwise allowed.

{
  "rules": {
    "Homeland": {
      "$uid" : {
        ".read": true,
        ".write": "auth !== null"
      },
    },
    "$other": { // <-- everywhere else not named above
      ".read": "auth !== null", // <-- allows read if logged in
      ".write": "auth !== null" // <-- allows write if logged in
    }
  }
}

The next step is to restrict access to /service, /quta and /period under the /Homeland/$uid key.

{
  "rules": {
    "Homeland": {
      "$uid" : {
        "service": {
          ".read": true, // <-- publically readable
          ".write": "auth.uid === $uid" // <-- writable only by owner
        },
        "quta": {
          ".read": true, // <-- publically readable
          ".write": "auth.uid === $uid" // <-- writable only by owner
        },
        "period": {
          ".read": "auth !== null", // <-- readable by any logged in user
          ".write": "auth.uid === $uid" // <-- writable only by owner
        }
      },
    },
    "$other": { // <-- everywhere else not named above
      ".read": "auth !== null", // <-- allows read if logged in
      ".write": "auth !== null" // <-- allows write if logged in
    }
  }
}

However, because of the mixed permissions levels under /Homeland, you are limited by what queries you can perform on this data. As an example, you can't just list off all the users under /Homeland using a query because the read operation will be denied.

A query that wouldn't work would be:

firebase.database().ref('/Homeland').on('child_added', (snap) => { ... })`

To fix this in as simple a way as possible, you can split /Homeland into /HomelandPublic and /HomelandRegistered based on who can access the data. The rules for this would be similar to:

{
  "rules": {
    "HomelandPublic": {
      "$uid" : {
        ".write": "auth.uid === $uid" // <-- writable only by owner
      },
      ".read": true // <-- publically readable
    },
    "HomelandRegistered": {
      "$uid" : {
        ".write": "auth.uid === $uid" // <-- writable only by owner
      },
      ".read": "auth !== null" // <-- readable by any logged in user
    },
    "$other": { // <-- everywhere else not named above
      ".read": "auth !== null", // <-- allows read if logged in
      ".write": "auth !== null" // <-- allows write if logged in
    }
  }
}

The query from before would now be changed to:

// get publicly accessible data
firebase.database().ref('/HomelandPublic').on('child_added', (snap) => { ... }) // alternatively: once('value', (snap) => {})

// get data accessible by registered users
if (firebase.auth().currentUser) {
  firebase.database().ref('/HomelandRegistered').on('child_added', (snap) => { ... }) // alternatively: once('value', (snap) => {})
}

This should give you enough knowledge to adapt it to your situation and needs.

Upvotes: 1

Related Questions