Firestore实时数据库身份验证规则

时间:2020-10-08 08:43:29

标签: firebase-realtime-database firebase-security

给出以下数据结构:

Firestore data

是否仍然根据条目的复合键中auth.uid的存在来验证读取请求?例如“ 5f6d9d83258b65b5f272aa9f | 5f79b99dc6a7c11b9c0eab78”中的“ 5f79b99dc6a7c11b9c0eab78”?

我已经尝试过-但是我认为包含包含与条目中的字段相关,而不是键本身。

   {
      "rules": {
          "messages": {
            "$message": {
              ".read":  "$message.contains(auth.uid)"          
            }
          }
        }    
    }

编辑:

这是我已验证用户身份,预取现有消息的代码(这里将添加一些其他逻辑以一次仅提取X条消息),发送和检索新消息。

import * as firebase from "firebase/app";
import "firebase/auth";    
import "firebase/database";

export const initClient = async (token) => {
  if (!firebase.apps.length) {
    var firebaseConfig = {
      apiKey: "[API]",
      authDomain: "[DOMAIN]",
      databaseURL: "[URL]",
      projectId: "[ID]",
      storageBucket: "[STORAGE]",
      messagingSenderId: "[SENDERID]",
      appId: "[APP_ID]",
      measurementId: "[MEAS_ID]"
    };

    firebase.initializeApp(firebaseConfig);

    await firebase.auth().signInWithCustomToken(token).catch(function (error) {
      var errorCode = error.code;
      var errorMessage = error.message;
      //todo
    });

    return { authenticated: true }
  }   

}

export const writeMessage = (fromId, chatId, message) => {
  var newMessageKey = firebase.database().ref().child('messages/' + chatId).push().key;
  
  firebase.database().ref('messages/' + chatId + '/' + newMessageKey).set({
    senderId: fromId,
    message: encodeURIComponent(message)
  });
}

//get all messages once when a particular chat window is open - works fine without auth rules in place
export const getMessages = async (chatId) => {  
  //chatId = '5f6d9d83258b65b5f272aa9f|5f79b99dc6a7c11b9c0eab78
  var ref = firebase.database().ref('messages/' + chatId);
  const snapshot = await ref.once('value')
  const messages = snapshot.val();
  const messagesArray = Object.keys(messages).map(key => ({ ...messages[key], message: decodeURIComponent(messages[key].message), messageId: key }))
  
  return { chatId, messages: messagesArray };
}


let bound = false;
//set listeners to get new messages and pass the messages back into the application
export const setMessageListeners = async (chatId, onNewMessage) => {
  if(bound) return;
  bound = true;  
  var ref = firebase.database().ref('messages/' + chatId);
  ref.endAt().limitToLast(1).on('child_added', function (data) {
      onNewMessage(chatId, data.key, decodeURIComponent(data.val().message), data.val().senderId);      
  });
}

也许我需要为每个请求使用自定义令牌做什么?

我应该注意,我也无法获得在规则编辑器操场上打球的规则。毫无疑问,这与用于测试的有效负载有关。

除此之外,我还具有一些服务器端逻辑来跟踪每个会话,这些数据实际上存储在RTDB之外-上面纯粹是消息存储。

编辑:已排序!

{
  "rules": {
      "messages": {
        "$conversation": {
          ".read":  "$conversation.contains(auth.uid)",
            "$message": {
                ".write": "newData.child('senderId').val() === auth.uid && !data.exists() || data.child('senderId').val() === auth.uid && data.exists()"
            }
        }
        }
  }    
}

看来我的问题根本不是问题。只是我错误地使用了操场,并在与规则集中的动态键匹配的路径段中添加了花括号(不知道为什么,但是我确定我在某处看到了它)。上面的结构是我现在确定的。参与对话的任何人都可以阅读消息(直到我添加一个已删除的属性,而不是删除消息后我才会将其隐藏)。您可以编写不存在的新项目,也可以更新属于您的现有邮件。

感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

我只是尝试重现此问题,但它似乎对我来说很好。我将在下面分享我的代码,数据和规则,希望它们能帮助您找到问题/差异。

我的JSON:

{
  "anotheruser-m4yXaC9wmCdJXOiL2LDPEJDPoHw1" : "value2",
  "m4yXaC9wmCdJXOiL2LDPEJDPoHw1-anotheruser" : "value1"
}

我的代码:

firebase.auth().signInAnonymously();

firebase.auth().onAuthStateChanged(function(user) {
  if (user) {
    console.log("uid: "+user.uid);
    var ref = firebase.database().ref("64258893");
    ref.child("m4yXaC9wmCdJXOiL2LDPEJDPoHw1-anotheruser").once("value")
    .then(function(snapshot) {
      console.log(snapshot.key+": "+snapshot.val());
    }).catch(function(error) {
      console.error(error);
    });
  }
});

我对节点64258893的安全规则:

{
  "rules": {
    "64258893": { 
      "$message": {
        ".read": "$message.contains(auth.uid)"
      }
    },
    ...

此代码记录:

“ uid:m4yXaC9wmCdJXOiL2LDPEJDPoHw1”

“ m4yXaC9wmCdJXOiL2LDPEJDPoHw1-另一个用户:value1”

这正是我所期望的,因为您可以看到UID嵌入在密钥中。如果我将读取更改为不包含UID的其他路径(例如ref.child("yes-anotheruser"),那么读取将被拒绝。