如何检测 Firestore 规则中的数组条目删除请求

How To Detect Array Entry Deletion Request In Firestore Rules

我需要删除我在 firestore 中的一个文档的数组中的一个条目(有很多“输入”)

唯一的方法(根据 )是通过更新请求,使用 FieldValue.arrayRemove(val)FieldValue.delete() 将条目的值设置为一些奇怪的非数字值。

如何在 Firestore 规则中检测到这个“hippity hoppity delete your 属性”值?因为目前我已经设置了你只能将你自己的 UID 写入那个数组,我希望它也允许你删除你自己的条目。

我的 Dart 代码:

FirebaseFirestore.instance.collection("requests").doc(id).update({"likes." + FirebaseAuth.instance.currentUser.uid: FieldValue.delete()});

我的 Firestore 规则(likes 是数组):

rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
  match /requests/{requestID} {
    allow update: if (request.resource.data.likes.hasOnly([request.auth.uid])) && (resource.data.likes.size() + 1 == request.resource.data.likeCount);
        }
    }
}

我的 Firestore:

你可以看看进来的数组是不是没有uid,size是不是只少了一个所以只删了一个应该是uid的

应该是这样的:


match /databases/{database}/documents {
    match /requests/{requestID} {
        allow update: if
            resource.data.likes.hasAll(request.resource.data.likes) &&
            resource.data.likes.hasAll([request.auth.uid]) &&
            !request.resource.data.likes.hasAll([request.auth.uid]) &&
            request.resource.data.likes.size() == resource.data.followers.size() - 1          
    }
}

Cloud Firestore Security Rules, an array is called a List and an array of unique values is a Set.

保留这个术语,您想要检查 likes 的值集是否从中添加了 request.auth.uid to/removed,或者保持不变。您还想断言唯一的更改与该值有关。

我们可以借助规则中的 Custom Functions 来做到这一点。

首先,让我们编写一个函数来断言列表是唯一的:

// Checks whether the given list is unique
function isListUnique(list) {
  return list.toSet().size() == list.size();
}

// Examples:
isListUnique(['a', 'b']) == true
isListUnique(['a', 'a']) == false

接下来,我们要确保唯一值的初始列表和最终的唯一值列表仅在给定的值列表上有所不同。

// Checks whether the two sets differ by only the given changes set
function doesSetDifferBy(setA, setB, setChanges) {
  return setA.difference(setB).union(setB.difference(setA)) == setChanges;
}

// Examples:
doesSetDifferBy(['a', 'b'].toSet(), ['a', 'c'].toSet(), ['b'].toSet()) == false // differs by ['b','c'].toSet() not ['b'].toSet()
doesSetDifferBy(['a', 'b'].toSet(), ['a'].toSet(), ['b'].toSet()) == true
doesSetDifferBy(['a', 'b'].toSet(), ['a', 'b'].toSet(), ['b'].toSet()) == false // sets are equal, not different
doesSetDifferBy(['a'].toSet(), ['a', 'b'].toSet(), ['b'].toSet()) == true

虽然我们可以使用下一行调用上述函数,但我们首先应确保输入列表是唯一的:

doesSetDifferBy(resource.data.likes.toSet(), request.resource.data.likes.toSet(), [request.auth.uid].toSet())

要断言两个列表是唯一的并且仅在给定的更改列表中有所不同,您可以使用此函数:

// Checks whether the two lists are both unique and differ by only the given changes list
function doesUniqueListDifferBy(listA, listB, listChanges) {
  return isListUnique(listA) && isListUnique(listB)
    && doesSetDifferBy(listA.toSet(), listB.toSet(), listChanges.toSet())
}

// Examples:
doesUniqueListDifferBy(['a', 'b'], ['a', 'c'], ['b']) == false // differs by ['b','c'] not ['b']
doesUniqueListDifferBy(['a', 'b'], ['a'], ['b']) == true
doesUniqueListDifferBy(['a', 'b'], ['a', 'b'], ['b']) == false // lists are equal, not different
doesUniqueListDifferBy(['a'], ['a', 'b'], ['b']) == true
doesUniqueListDifferBy(['a', 'a'], ['a', 'b'], ['b']) == false // first list isn't unique

对于您的应用程序,您还需要确保如果 likes 列表未更改,则允许它通过。我们可以通过将上面的 doesUniqueListDifferBy() 函数调整为:

来实现这一点
// Checks whether the two lists are both unique and either are
// equal or differ by only the given changes list
function isUniqueListUnchangedOrDiffersBy(listA, listB, listChanges) {
  return isListUnique(listA) && isListUnique(listB)
    && (listA.toSet() == listB.toSet()
      || doesSetDifferBy(listA.toSet(), listB.toSet(), listChanges.toSet()))
}

// Examples:
isUniqueListUnchangedOrDiffersBy(['a', 'b'], ['a', 'c'], ['b']) == false // differs by ['b','c'] not ['b']
isUniqueListUnchangedOrDiffersBy(['a', 'b'], ['a', 'b'], ['b']) == true
isUniqueListUnchangedOrDiffersBy(['a', 'b'], ['a'], ['b']) == true
isUniqueListUnchangedOrDiffersBy(['a'], ['a', 'b'], ['b']) == true
isUniqueListUnchangedOrDiffersBy(['a', 'a'], ['a', 'b'], ['b']) == false // first list isn't unique

将上述函数组合在一起,您的规则将如下所示:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
  
    // Checks whether the given list is unique
    function isListUnique(list) {
      return list.toSet().size() == list.size();
    }

    // Checks whether the two sets differ by only the given changes set
    function doesSetDifferBy(setA, setB, setChanges) {
      return setA.difference(setB).union(setB.difference(setA)) == setChanges;
    }
  
    // Checks whether the two lists are both unique and either are
    // equal or differ by only the given changes list
    function isUniqueListUnchangedOrDiffersBy(listA, listB, listChanges) {
      return isListUnique(listA) && isListUnique(listB)
        && (listA.toSet() == listB.toSet()
          || doesSetDifferBy(listA.toSet(), listB.toSet(), listChanges.toSet()))
    }
  
    match /requests/{requestID} {
      allow update: if isUniqueListUnchangedOrDiffersBy(resource.data.likes, request.resource.data.likes, [request.auth.uid]);
    }
  }
}

您还需要将客户的 Dart 代码更新为:

FirebaseFirestore.instance.collection("requests").doc(id)
  .update({
    "likes": FieldValue.arrayRemove(FirebaseAuth.instance.currentUser.uid)
  });

如果您喜欢使用语法糖,您也可以调整函数以仅使用 属性 名称,但这不支持嵌套属性。

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
  
    // Checks whether the given list is unique
    function isListUnique(list) {
      return list.toSet().size() == list.size();
    }

    // Checks whether the two sets differ by only the given changes set
    function doesSetDifferBy(setA, setB, setChanges) {
      return setA.difference(setB).union(setB.difference(setA)) == setChanges;
    }
  
    // Checks whether the two lists are both unique and either are
    // equal or differ by only the given changes list
    function isUniqueListUnchangedOrDiffersBy(listA, listB, listChanges) {
      return isListUnique(listA) && isListUnique(listB)
        && (listA.toSet() == listB.toSet()
          || doesSetDifferBy(listA.toSet(), listB.toSet(), listChanges.toSet()))
    }

    // Checks whether the property contains a unique list that was either
    // unchanged or differs by only the given changes list
    function isUniqueListDataPropUnchangedOrDiffersBy(propName, listChanges) {
      return isUniqueListUnchangedOrDiffersBy(resource.data[propName], request.resource.data[propName], listChanges)
    }
  
    match /requests/{requestID} {
      allow update: if isUniqueListDataPropUnchangedOrDiffersBy("likes", [request.auth.uid]);
    }
  }
}