javascript - Cloud Firestore 安全规则不起作用
问题描述
我正在尝试提高我的 Firestore 数据库安全性,但我对此感到绝望。我已经尝试编辑它几个小时以尝试它是否可以工作,但它不断出错或拒绝访问。规则游乐场表明它应该工作..但它没有。
我正在尝试使用用户/environment/STABLE/users/RMwv3mmP4RIOVqPOsa77访问/environment/STABLE/route_sets/ w3TBJrovQbgFPr2GFfug 。用户有一个名为 db_role 的字段,其中包含字符串ADMIN。route_sets 中的文档包含一个名为 _permissions_read 的数组,其值为TEST_NOTHING和ADMIN。
使用规则游乐场测试它是可行的,但是当我通过我的 Angular 应用程序尝试它时,我得到“ERROR FirebaseError:缺少或不足的权限。”。对于身份验证,我将 Firebase 身份验证与自定义提供程序一起使用,uid 是用户表中的文档 ID。
Cloud Firestore -> 规则上的“监控规则”选项卡上的信息似乎表明当前规则(如下所示)在尝试访问 route_set 时生成错误。
rules_version = "2";
service cloud.firestore {
match /databases/{database}/documents {
match /environment/{environment}/{collectionName}/{documentId} {
allow read, write: if request.auth != null && collectionName == "route_sets" && checkPermissionRead(environment, collectionName, documentId);
allow read: if request.auth != null && collectionName != "route_sets";
allow write, delete: if request.auth != null;
function checkPermissionRead(environment, collectionName, documentId) {
return get(/databases/$(database)/documents/environment/STABLE/users/$(request.auth.uid)).data.db_role in get(/databases/$(database)/documents/environment/$(environment)/$(collectionName)/$(documentId)).data._permissions_read;
}
}
match /environment/STABLE/licenseholders/{document=**} {
allow read: if true
allow read, write: if request.auth != null;
}
match /environment/STABLE/users/{document=**} {
allow read: if true
allow read, write: if request.auth != null;
}
}
}
角应用程序代码:
import {Component, OnDestroy, OnInit} from '@angular/core';
import {AngularFirestore} from '@angular/fire/firestore';
import {AngularFireAuth} from '@angular/fire/auth';
@Component({
selector: 'app-example-security-rules',
template: `<span *ngFor="let set of routeSets"></span>`,
styles: [``],
providers: [ ]
})
export class ExampleSecurityRulesComponent implements OnInit {
routeSets = [];
constructor(private db: AngularFirestore,
public afAuth: AngularFireAuth) {
}
ngOnInit() {
this.afAuth.onAuthStateChanged(async (user) => {
if (user) {
this.db.collection<any>('environment/STABLE/route_sets').get().subscribe((d) => {
d.forEach(docT => {
this.routeSets.push({__document__key: docT.id, ...docT.data()});
});
});
}
});
}
}
新的代码和安全规则工作:
安全规则
rules_version = "2";
service cloud.firestore {
match /databases/{database}/documents {
match /environment/{environment}/{collectionName}/{documentId} {
allow read: if request.auth != null && collectionName != "route_sets";
allow write, delete: if request.auth != null;
}
match /environment/{environment}/route_sets/{document=**} {
allow read, write: if request.auth != null && checkPermissionRead(environment);
function checkPermissionRead(environment) {
let user = get(/databases/$(database)/documents/environment/$(environment)/users/$(request.auth.uid)).data;
return user.db_role in resource.data._permissions_read && user.licenseholder_id == resource.data.licenseholder_id;
}
}
match /environment/STABLE/licenseholders/{document=**} {
allow read: if true
allow read, write: if request.auth != null;
}
match /environment/STABLE/users/{document=**} {
allow read: if true
allow read, write: if request.auth != null;
}
}
}
角
this.db.collection<any>('environment/STABLE/route_sets').ref.where('licenseholder_id', '==', environment.licenseholder_id)
.where('_permissions_read', 'array-contains', DatabaseRoles.ADMIN).get().then((d) => {
d.forEach(docT => {
this.routeSets.push({__document__key: docT.id, ...docT.data()});
});
});
解决方案
如果我正确理解了用户案例,您将尝试阅读整个集合,然后在您的规则中过滤各个文档。
这是行不通的,因为规则不是过滤器。相反,规则仅在查询开始时检查是否允许读取操作,而不检查单个文档 - 因为这不会在性能和成本上进行扩展。
因此,get()
规则中的调用仅在您阅读单个文档时有效,而不是在您请求一系列文档时(称为list
规则中的操作)。
如果您想安全地阅读一系列文档,则必须在代码中构建正确的查询,然后使用规则保护该查询。不幸的是,没有办法get()
从查询中的规则中获得等效的操作。
安全执行此类操作的唯一方法是复制每个route_sets
文档下的权限数据,以便您的规则可以在那里检查它 - 并且他们可以验证您将正确的条件传递给查询以仅请求文档你被授权。