Firestore基本安全规则不起作用。

huangapple go评论69阅读模式
英文:

Firestore basic security rules not working

问题

请保持谦逊,因为我对iOS Swift编程和Firestore非常新手。

我在Swift中有这两个结构体:

import FirebaseFirestoreSwift
import Foundation

struct WashingMachine: Codable {
    @DocumentID var id: String?
    var brand = "unknown"
    var price = 0
    var boughtDate: Date
}

struct PrivateData: Codable {
    @DocumentID var id: String?
    var roles: Dictionary<String, String>
}

我尝试遵循Firestore创建服务器安全性的指南,以便只有具有“Owner”角色的已登录用户才能访问洗衣机文档。我的Firestore规则如下:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /washingMachine/{docID} {
      allow read: if isSignedIn() && get(/databases/$(database)/documents/washingMachine/$(docID)/privateData/$(docID)).data.roles[request.auth.uid] == "Owner";

      function isSignedIn() {
        return request.auth != null;
      }

      allow create: if isSignedIn();
      match /privateData/{docID} {
        allow create: if isSignedIn();
      }
    }
  }
}

如果我在安全规则中删除 get(/databases/$(database)/documents/washingMachine/$(docID)/privateData/$(docID)).data.roles[req,那么我可以读取洗衣机文档。但是,当我添加附加检查,确保用户在子集合privateData中的角色时,我无法读取洗衣机文档。

请问是否有人可以为我提供指导?


更新信息(在@trndjc提出的安全更新后)

当使用这些建议的安全规则时,我不能再创建privateData子集合:

rules_version = '2';

service cloud.firestore {

    match /databases/{database}/documents {

        function isSignedIn() {
            return request.auth != null;
        }

        match /washingMachine/{docID} {
            allow read: if isSignedIn() && get(/databases/$(database)/documents/washingMachine/$(docID)/privateData/$(docID)).data.roles[request.auth.uid] == "Owner";
            allow create: if isSignedIn();
        }

        match /privateData/{docID} {
            allow create: if isSignedIn();
        }
    }
}

我的SwiftUI代码中的函数如下(抱歉,我硬编码了一些值):

func addDefaultWashingMachine(userId: String) {
    let privateData = PrivateData(
        roles: [userId: "Owner"])
    let washingMachine = WashingMachine(boughtDate: Date.now)
    let collectionRef = db.collection("washingMachine")

    do {
        let newDocReference = try collectionRef.addDocument(from: washingMachine)
        print(newDocReference.debugDescription)

        let subCollectionRef = db.collection("washingMachine").document(newDocReference.documentID).collection("privateData")

        let subDocReference = try subCollectionRef.addDocument(from: privateData)
        
    } catch {
        print(error.localizedDescription)
    }
}

结果是只创建了washingMachine集合,而没有创建子集合privateData。以下是在打印输出中给出的错误消息:

<FIRDocumentReference: 0x28222c630>
2023-06-18 19:45:47.107265+0200 MyTestApp[7196:3316489] 10.9.0 - [FirebaseFirestore][I-FST000001] WriteStream (107e47088) Stream error: 'Permission denied: Missing or insufficient permissions.'
2023-06-18 19:45:47.108332+0200 MyTestApp[7196:3316489] 10.9.0 - [FirebaseFirestore][I-FST000001] Write at washingMachine/E8uAMthVmZ2ZSvji37iX/privateData/ZULclTBPGovQpijy0PW6 failed: Missing or insufficient permissions.

我修改了建议的安全规则,以便能够添加子集合privateData:

rules_version = '2';

service cloud.firestore {

    match /databases/{database}/documents {

        function isSignedIn() {
            return request.auth != null;
        }

        match /washingMachine/{docID} {
            allow read: if isSignedIn() && get(/databases/$(database)/documents/washingMachine/$(docID)/privateData/$(docID)).data.roles[request.auth.uid] == "Owner";
            allow create: if isSignedIn();
            
            match /privateData/{docID} {
                allow create: if isSignedIn();
            }
        }       
    }
}

尝试读取文档时,我仍然收到以下错误:

2023-06-18 20:26:46.331840+0200 MyTestApp[7211:3330007] 10.9.0 - [FirebaseFirestore][I-FST000001] Listen for query at washingMachine failed: Missing or insufficient permissions.
Error getting documents: Error Domain=FIRFirestoreErrorDomain Code=7 "Missing or insufficient permissions." UserInfo={NSLocalizedDescription=Missing or insufficient permissions.}

如果我从安全规则中删除 get(/databases/$(database)/documents/washingMachine/$(docID)/privateData/$(docID)).data.roles[request.auth.uid] == "Owner",那么它可以正常工作。

英文:

Please be humble as I'm very new to iOS swift programming and Firestore.

I have these two structs in Swift:

import FirebaseFirestoreSwift
import Foundation


struct WashingMachine: Codable {
    @DocumentID var id: String?
    var brand = &quot;unknown&quot;
    var price = 0
    var boughtDate: Date
        
  }
import FirebaseFirestoreSwift
import Foundation

struct PrivateData: Codable {
    @DocumentID var id: String?
    var roles: Dictionary&lt;String, String&gt;
}

I have tried to follow Firestore's guidelines in creating server security so only the logged in user with the role of 'Owner', given in the PrivateData subcollection can access a washingMachine document.

My rules in Firestore look like this:

rules_version = &#39;2&#39;;
service cloud.firestore {
  match /databases/{database}/documents {
    match /washingMachine/{docID} {
    
    	allow read: if isSignedIn() &amp;&amp; get(/databases/$(database)/documents/washingMachine/$(docID)/privateData/$(docID)).data.roles[request.auth.uid] == &quot;Owner&quot;;
    
    	function isSignedIn() {
      	return request.auth != null;
      }
    
      allow create: if isSignedIn();
      	match /privateData/{docID} {
      		allow create: if isSignedIn();
      }      
    }
  }
}

I have no problem in creating the collection washingMachine and a subcollection privateData to that.
If I take away get(/databases/$(database)/documents/washingMachine/$(docID)/privateData/$(docID)).data.roles[req in my security rules, I can read the washingMachine documents.
I have however not been able to read the washingMachine document when I add the additional check that the user is represented in the subcollection privateData --> roles.

Anyone who can guide me?


Updated information after trying the proposed security update by @trndjc

When using these proposed security rules, I can no longer create the privateData subcollection:

rules_version = &#39;2&#39;;

service cloud.firestore {

match /databases/{database}/documents {

    function isSignedIn() {
        return request.auth != null;
    }

    match /washingMachine/{docID} {
        allow read: if isSignedIn() &amp;&amp; get(/databases/$(database)/documents/washingMachine/$(docID)/privateData/$(docID)).data.roles[request.auth.uid] == &quot;Owner&quot;;
        allow create: if isSignedIn();
    }

    match /privateData/{docID} {
        allow create: if isSignedIn();
    }
}

}

My function in SwiftUI code looks like this (sorry for the hardcoding):

func addDefaultWashingMachine(userId: String) {
    let privateData = PrivateData(
        roles: [userId: &quot;Owner&quot;])
    let washingMachine = WashingMachine(boughtDate: Date.now)
    let collectionRef = db.collection(&quot;washingMachine&quot;)

    do {
        let newDocReference = try collectionRef.addDocument(from: washingMachine)
        print(newDocReference.debugDescription)

        let subCollectionRef = db.collection(&quot;washingMachine&quot;).document(newDocReference.documentID).collection(&quot;privateData&quot;)

        let subDocReference = try subCollectionRef.addDocument(from: privateData)
        
    } catch {
        print(error.localizedDescription)
    }
}

The result is that only the washingMachine collection is created, but the subcollection privateData isn't. The following error message is given in the printouts.

<FIRDocumentReference: 0x28222c630>
2023-06-18 19:45:47.107265+0200 MyTestApp[7196:3316489] 10.9.0 - [FirebaseFirestore][I-FST000001] WriteStream (107e47088) Stream error: 'Permission denied: Missing or insufficient permissions.'
2023-06-18 19:45:47.108332+0200 MyTestApp[7196:3316489] 10.9.0 - [FirebaseFirestore][I-FST000001] Write at washingMachine/E8uAMthVmZ2ZSvji37iX/privateData/ZULclTBPGovQpijy0PW6 failed: Missing or insufficient permissions.


Modified the proposed security rules like this to being able to add subcollection privateData:

rules_version = &#39;2&#39;;

service cloud.firestore {

match /databases/{database}/documents {

    function isSignedIn() {
        return request.auth != null;
    }

    match /washingMachine/{docID} {
        allow read: if isSignedIn() &amp;&amp; get(/databases/$(database)/documents/washingMachine/$(docID)/privateData/$(docID)).data.roles[request.auth.uid] == &quot;Owner&quot;;
        allow create: if isSignedIn();
        
        match /privateData/{docID} {
        allow create: if isSignedIn();
    	}
        
    }       
}

}

I still get the following error when trying to read the document:

2023-06-18 20:26:46.331840+0200 MyTestApp[7211:3330007] 10.9.0 - [FirebaseFirestore][I-FST000001] Listen for query at washingMachine failed: Missing or insufficient permissions.
Error getting documents: Error Domain=FIRFirestoreErrorDomain Code=7 "Missing or insufficient permissions." UserInfo={NSLocalizedDescription=Missing or insufficient permissions.}

I call it by using these functions:

func findAllWashingMachinesInCollection() {
    
    db.collection(&quot;washingMachine&quot;).whereField(&quot;brand&quot;, isEqualTo: &quot;unknown&quot;).getDocuments() { (querySnapshot, err) in
        if let err = err {
            print(&quot;Error getting documents: \(err)&quot;)
        } else {
            for document in querySnapshot!.documents {
                print(&quot;\(document.documentID) =&gt; \(document.data())&quot;)
                self.fetchWashingMachine(documentId: document.documentID)
                
            }
        }
    }
}


func fetchWashingMachine(documentId: String) {
    let docRef = db.collection(&quot;washingMachine&quot;).document(documentId)
    
    docRef.getDocument(as: WashingMachine.self) { result in
        switch result {
        case .success(let washingMachine):
            self.washingMachine = washingMachine
            print(&quot;WASHING: \(washingMachine.brand)&quot;)
            self.errorMessage = nil
        case .failure(let error):
            self.errorMessage = &quot;Error decoding document: \(error.localizedDescription)&quot;
        }
    }
}

The work well if I remove get(/databases/$(database)/documents/washingMachine/$(docID)/privateData/$(docID)).data.roles[request.auth.uid] == "Owner" in the security rules.

答案1

得分: 1

很遗憾,你试图使用Firebase安全规则来实现的目标是不可能的。安全规则不会过滤数据,而是“仅仅”确保你的代码只能访问被允许的数据。

所以当我们看你的规则中的检查时:

get(/databases/$(database)/documents/washingMachine/$(docID)/privateData/$(docID)).data.roles[request.auth.uid] == &quot;Owner&quot;;

这需要满足以下条件:

  • 要么规则引擎检查每个文档的私有数据,只返回用户是所有者的文档,这违反了我上面提到的“规则不是过滤器”的限制。
  • 要么你的代码必须包含相同的条件,只请求那些你是所有者的文档。但这是不可能的,因为查询只能基于它返回的文档中的数据进行过滤。

解决方法是在你想要过滤的数据(如roles)中复制数据,然后在安全规则和查询中都引用该数据。

英文:

Unfortunately what you're trying to do isn't possible with Firebase security rules. Security rules don't filter data, but instead "only" ensure that your code only accesses data that it is permitted to.

So when we look at the check in your rule:

get(/databases/$(database)/documents/washingMachine/$(docID)/privateData/$(docID)).data.roles[request.auth.uid] == &quot;Owner&quot;;

This requires that:

  • Either the rules engine check each document's private data and only return the documents for which the user is the owner, which violates the rules are not filters limitation that I mentioned above.
  • Or your code will have to include the same condition, so only requesting the documents for which it is the owner. But that isn't possible, as a query can only filter based on the data in the documents that it returns.

The workaround is to duplicate the data that you want to filter on (so the roles) in the parent document, and refer to that both in your security rules and in the query.

答案2

得分: 0

我不知道你是否正确分配了角色,是否正确设置了集合和文档路径,或者数据库中的数据是否反映了这些规则,但我知道你的安全规则没有正确编写。你必须在匹配规则之外声明这些函数。我在这里将它们分开以更好地说明规则应该如何看待。

rules_version = '2';

service cloud.firestore {

    function isSignedIn() {
        return request.auth != null;
    }

    match /databases/{database}/documents {

        match /washingMachine/{docID} {
            allow read: if isSignedIn() && get(/databases/$(database)/documents/washingMachine/$(docID)/privateData/$(docID)).data.roles[request.auth.uid] == "Owner";
            allow create: if isSignedIn();
        }

        match /privateData/{docID} {
            allow create: if isSignedIn();
        }
    }
}
英文:

I don't know if you have the roles properly assigned, if the collection and document paths are correct, or if the data in the database reflects these rules, but I do know that your security rules aren't properly written. You must declare the functions outside of the match rules. I've spaced them out here to better illustrate how the rules should look.

rules_version = &#39;2&#39;;

service cloud.firestore {

    match /databases/{database}/documents {

        function isSignedIn() {
            return request.auth != null;
        }

        match /washingMachine/{docID} {
            allow read: if isSignedIn() &amp;&amp; get(/databases/$(database)/documents/washingMachine/$(docID)/privateData/$(docID)).data.roles[request.auth.uid] == &quot;Owner&quot;;
            allow create: if isSignedIn();
        }

        match /privateData/{docID} {
            allow create: if isSignedIn();
        }
    }
}

huangapple
  • 本文由 发表于 2023年6月16日 00:52:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/76483892.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定