Tengo salas de chat almacenadas en Cloud Firestore que deben protegerse y estoy teniendo dificultades para hacerlo. Mi estado en React se ve así:

export class Messages extends React.Component {
    constructor(props) {
    super(props);
    this.boardID = this.props.settings.id;
    this.mainChatRef = database
      .collection("boards")
      .doc(boardID)
      .collection("chats")
      .doc("MAIN")
      .collection("messages");
    this.chatRoomsRef = database
      .collection("boards")
      .doc(boardID)
      .collection("chats");
    }

Mi consulta en reaccionar se ve así:

latestMessages = (messageSnapshot) => {
    const message = [];
    messageSnapshot.forEach((doc) => {
      const { text, createdAt, uid } = doc.data();
      message.push({
        key: doc.id,
        text,
        createdAt,
        uid,
      });
    });
    this.setState({
      dataSource: message,
    });
}
queryRooms = async () => {
    const recentQuery = await this.chatRoomsRef
            .where("uidConcat", "in", [
              this.props.user.uid + this.state.value.objectID,
              this.state.value.objectID + this.props.user.uid,
            ])
            .get();
          for (const qSnap of recentQuery.docs) {
            const messagesRef = this.chatRoomsRef
              .doc(qSnap.id)
              .collection("messages")
              .orderBy("createdAt")
              .limitToLast(30);
            messagesRef.onSnapshot(this.latestMessages);
          }
}

Mi estructura de base de datos:

boards(collection)
  {boardId}
    chats (collection)
      MAIN 
      {chatId_1}
      {chatId_2}
        uidArray (has only two UIDs since it's for private chat)
        uidConcat: user1_uid+user2_uid
        messages(collection)
          {message1}
            createdAt
            text
            uid_creator
            uidArray (has UID of user1 and user2)

Intenté asegurar mis tablas como tal:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /boards/{boardId} {
      allow read, write: if request.time < timestamp.data(2050, 5, 1);
      match /chats/MAIN/messages/{messagesId=**} {
        allow read, create: if request.auth.uid !=null;
      }
      match /chats/{chatId} {
        allow read, write: if request.auth.uid !=null;
        match /messages/{messagesId=**} {
          allow read: if (request.auth.uid in get(/databases/{database}/documents/boards/
                      {boardId}/chats/{chatId}/messages/{messageId}).data.uidArray) 
                      && request.query.limit <= 30 && request.query.orderBy.createdAt == 'ASC';
          allow write: if request.auth.uid != null;
        }
      }
    }
  }
}

Deseo que cualquiera que esté autenticado tenga siempre acceso al chat PRINCIPAL del tablero y la forma en que lo tengo escrito funciona. Para las habitaciones privadas, sigo recibiendo un error. He leído los documentos y sé que Security rules are not filters, por eso agregué un montón de declaraciones AND en un intento de asemejar mucho mis valores a mi consulta de código escrito en React, pero sigo recibiendo Uncaught Errors in snapshot listener: FirebaseError: Missing or insufficient permissions . Actualmente estoy escribiendo mis reglas para asegurar a nivel de documento dentro de la colección de mensajes, pero idealmente me gustaría asegurar a nivel de documento en la colección de chat y verificar si mi request.auth.uid está en el campo de documento uidArray así:

match /chats/{chatId=**} {
  allow read, write if request.auth.uid in resource.data.uidArray
}

Me imagino que proteger los documentos en la colección de chat sería más seguro, pero cualquier método que proteja los mensajes es más que apreciado.

0
jfar41 11 feb. 2021 a las 22:03

1 respuesta

La mejor respuesta

El problema es que está intentando realizar una búsqueda dinámica en su regla en función de cada fila de los datos de esta línea: get(/databases/{database}/documents/boards/{boardId}/chats/{chatId}/messages/{messageId}).data.uidArray)

El punto crítico que hay que entender aquí es que las reglas no miran los datos reales cuando realizan consultas. Las operaciones de obtención son un poco diferentes porque hay exactamente un registro para obtener, pero para las consultas, solo examina la consulta para asegurarse de que no pueda obtener datos que no coincidan con su regla; en realidad, nunca carga datos para examinar. Esto se trata en los documentos aquí y hay una buena descripción general en video. este aquí. Una inmersión más profunda está aquí.

El acceso basado en roles es un tema complejo y probablemente no sea fácil de responder en un Stack Overflow dado lo amplias que serían las soluciones y lo específicas que son para su caso de uso. Pero una alternativa ingenua sería listar los miembros en /chats/{chatId} y usar una regla como esta:

   match /chats/{chatId} {
     // scoped to chats/{chatId} doc
     function isChatMember(uid) {
        // assumes this document contains an array of
        // uids allowed to read the messages subcollection
        return uid in resource.data.members;
     }

     match /messages/{messagesId=**} {
        allow read: if isChatMember(request.auth.uid)
     }
   }
3
Kato 9 mar. 2021 a las 20:02