Faizan Naeem
Faizan Naeem

Reputation: 515

Allow Create And Read in all the cases But don't allow update if the data does not exist in firebase real time database

I'm working on a chat module using fiebase , I have a structure as below .

enter image description here

My main node is mychatexperiment

inside it I have another node ChatListing and some other chat keys.

Today I set up setValues in my structure and when I passed my url without any node it deletes all of my data inside the parent node.

What i want is to set up the rules

  1. One can create the node in any case
  2. One can update the node in any case
  3. One can not delete the node in any case
  4. One Can only update or set the data inside ChatListing

I was trying using this but it does not work . any idea how to implement these things .

{
  "rules": {
         ".write": "!data.exists() || !newData.exists()",
         "read" : true
   }
}

Note : I'm not using any authentication so need to implement rules without any authentication

Revise Requirements :

I have a structure where I have a single parent node and inside it I have multiple chat keys that is generated through firebase , Inside chat keys I have a node of individual message inside that chat . The second thing which is most important is that I have a node called chatlisting in which I am storing my userids in a unique format like If My userid is 5 so inside chatlisting I am storing 5 and inside that 5 I have all the chat keys nodes which are related to me .

{
  "ChatListing": {
    "1126_2": { //userUnique key 

      "MjmhpHb6qR7VSkYzbjI": { // 1126 userid has this chat key and inside this chat last msg was welcome and its unread count is 0 
        "UnReadCount": 0,
        "lastmessage": "Welcome",
        "lastmessagetime": 1631870264251
      }
    },
    "4184_1": {
      "MjmhpHb6qR7VSkYzbjI": { // 4184 userid has this chat key as well and inside this chat last msg was welcome and its unread count is 1 
        "UnReadCount": 1,
        "lastmessage": "Welcome",
        "lastmessagetime": 1.6318646965369204E12
      }
    }

  },
  "MjmhpHb6qR7VSkYzbjI": { // chat key
    "-MjmhpQbBaL7EbHPHayA": { // mesg key
      "data": "Thankyou",
      "time": 1.6318646965369204E12,
      "type": 0,
      "user": 4184   // the msg is sent by this user
    },
    "-Mjn21A4067dT4emYe05": {  // another msg in the same chat
      "data": "Welcome",
      "time": 1631870264251,
      "type": 0,
      "user": 1126 // the msg is sent by this user
    }
  }
}

What I want is to setup the rules in which no one can run update , set or delete inside parent node (except ChatList node) . Any one can create chat keys and read chat keys inside parent node , nothing else they can do .

but inside chatlist they can perform create read , set and update(not delete) as I need to update the last message in this node against user chat .

Upvotes: 1

Views: 698

Answers (1)

samthecodingman
samthecodingman

Reputation: 26171

So reusing the points as covered by my other answer, you would apply those rules using:

{
  "rules": {
    "ChatListing": {
      "$userid": { // the user's unique ID
        // anyone who knows this user ID can read their messages
        ".read": true,

        "$chatid": { // a chatroom the user is in

          // data stored here MUST have this shape (with nothing else)
          // {
          //   UnReadCount: number,
          //   lastmessage: string,
          //   lastmessagetime: number
          // }

          // Data may be created or updated, but not deleted

          ".validate": "newData.child('UnReadCount').isNumber() && newData.child('lastmessage').isString() && newData.child('lastmessagetime').isNumber()",

          "UnReadCount":     { ".write": "newData.exists()" },
          "lastmessage":     { ".write": "newData.exists()" },
          "lastmessagetime": { ".write": "newData.exists()" }
        }
      }
    },
    // when using $ keys at the same level as defined keys,
    // this rule will catch everything that doesn't match
    // the above rules
    "$chatId": { // a chatroom where messages can be sent
      // anyone who knows this chat ID can read its messages
      ".read": true, 

      "$msgId": { // a message in this chatroom

        // Data stored here MUST have this shape (with nothing else)
        // {
        //   data: string,
        //   time: number
        //   type: number,
        //   user: string, // see note
        // }

        // Data may be created, but not edited or deleted
        // change like above if needed

        ".validate": "newData.child('data').isString() && newData.child('time').isNumber() && newData.child('type').isNumber() && newData.child('user').isString()",
        
        "data": { ".write": "!data.exists()" },
        "time": { ".write": "!data.exists()" },
        "type": { ".write": "!data.exists()" },
        "user": { ".write": "!data.exists()" }
      }
    }
  }
}

Notes:

  • Don't use numeric user IDs as they are easily guessable, generate something random. You could even use const userId = push(getReference(getDatabase())).key. Consider securing the data with anonymous authentication.
  • Unlike your requirements, I have made the messages in the chat immutable. Once sent, no one can edit them. This prevents someone other than the sender from coming in and changing the message. With authentication, edits could be allowed because it's more secure.
  • Take note how unlike my /cars example, I haven't put ".read": true at the root of the database or at /ChatListing. This prevents someone coming along and running either of the below pieces of code to pull all of your stored data or pull all stored user IDs at once which will then allow them to find messages not meant for them. It does not prevent brute-forcing the data though.
const rootRef = ref(getDatabase());

rootRef
  .then((snapshot) => {
    console.log(snapshot.val()) // all data in database!
  });

const chatListingRef = ref(getDatabase(), "ChatListing");

chatListingRef
  .then((snapshot) => {
    const usersArray = [];
    const chatIdSet = new Set();
    snapshot.forEach(userData => {
      usersArray.push(userData.key)
      userData.forEach(lastChatData => chatIdSet.add(lastChatData.key));
    });
    
    // logs all user IDs in the database!
    console.log("User IDs:", usersArray)

    // logs all chatroom IDs in the database!
    console.log("Chatroom IDs:", [...chatIdSet]) 
  });

Upvotes: 2

Related Questions