我在使用Firestore处理删除时遇到问题。简而言之,我为这样的帖子创建了安全规则:
首先,规则中包含一些功能:
服务cloud.firestore {
function userRoles() {
return ['admin', 'customer', 'reader'];
}
function userGenders() {
return ['mal', 'female', 'other'];
}
function postVisibilities() {
return ['public', 'private', 'protected'];
}
function postType() {
return ['music', 'motion_design', 'graphic_art'];
}
function isPayment(paymentDoc) {
return paymentDoc != null
&& paymentDoc.date is timestamp
&& paymentDoc.price is number
&& paymentDoc.price is number
&& paymentDoc.price > 0;
}
function isBill(billDoc) {
return billDoc.sellerId is string
&& billDoc.buyerId is string
&& billDoc.postIds != null
&& billDoc.date is timestamp
&& billDoc.paymentDoc != null
&& isPayment(billDoc.paymentDoc);
}
function isAccount(accountDoc) {
return accountDoc.isRegistered is bool
&& accountDoc.addressId is string
&& accountDoc.contactId is string
&& accountDoc.email is string
&& accountDoc.username is string
&& accountDoc.gender is string
&& accountDoc.gender in userGenders()
&& accountDoc.role is string
&& accountDoc.role in userRoles();
}
function isPost(postDoc) {
return postDoc.createdAt is timestamp
&& postDoc.updatedAt is timestamp
&& postDoc.title is string
&& postDoc.text is string
&& postDoc.image is string
&& postDoc.authorId is string
&& postDoc.visibility is string
&& postDoc.visibility in postVisibilities();
}
function isVote(voteDoc) {
return voteDoc.authorId is string
&& voteDoc.reaction is string
&& voteDoc.reaction in ['up', 'down'];
}
function isComment(commentDoc) {
return commentDoc.authorId is string
&& commentDoc.message is string;
}
function isSingle(doc) {
return doc.size() == 1;
}
match /databases/{database}/documents {
function userExists(userId) {
return userId != null && exists(/databases/$(database)/documents/accounts/$(userId));
}
function getUserRole(userId) {
return get(/databases/$(database)/documents/accounts/$(userId)).data.roles;
}
function hatUserRole(userId, role) {
return getRoleForUser(userId) in role;
}
match /{document=**} {
allow read: if true;
allow write: if false;
}
match /accounts/{accountId} {
allow create: if isAccount(request.resource.data)
&& (request.auth.uid == accountId || hatUserRole(request.auth.uid, ['admin']));
allow update: if request.auth.uid == accountId || hatUserRole(request.auth.uid, ['admin']);
allow delete: if hatUserRole(request.auth.uid, ['admin']);
match /contacts/{contactId} {
allow write: if isSingle(request.resource.data)
&& request.auth.uid == accountId;
allow read: if userExists(request.auth.uid);
}
match /favorites/{favoriteId} {
allow write: if isSingle(request.resource.data)
&& request.auth.uid == accountId;
allow read: if userExists(request.auth.uid);
}
match /votes/{voteId} {
allow create: if isVote(request.resource.data)
&& userExists(request.auth.uid);
allow update: if userExists(request.auth.uid)
&& isVote(request.resource.data)
&& request.resource.data.authorId == request.auth.uid
allow delete: if userExists(request.auth.uid)
&& (request.auth.uid == accountId
|| hatUserRole(request.auth.uid, ['admin']))
}
}
match /bills/{billId} {
allow create: if isBill(request.resource.data)
&& userExists(request.resource.data.sellerId)
&& userExists(request.resource.data.buyerId)
&& (request.resource.data.buyerId == request.auth.uid
|| request.resource.data.sellerId == request.auth.uid);
allow update, delete: if false;
allow read: if request.resource.data.buyerId == request.aut.uid
|| request.resource.data.sellerId == request.aut.uid;
}
match /posts/{postId} {
function publicPost() {
return get(/databases/$(database)/documents/posts/$(postId)).data.visibility == 'public';
}
function postVisibility() {
return get(/databases/$(database)/documents/posts/$(postId)).data.visibility;
}
function protectedPost() {
return userExists(request.auth.uid)
&& get(/databases/$(database)/documents/posts/$(postId)).data.visibility == 'public';
}
function findPostAuthor(pathToFind) {
return get(/databases/$(database)/documents/posts/$(pathToFind)).data.authorId
}
allow create, update: if isPost(request.resource.data)
&& userExists(request.auth.uid)
&& request.resource.data.authorId == request.auth.uid;
allow read: if request.resource.data.visibility == 'public';
allow delete: if userExists(request.auth.uid)
&& findPostAuthor(request.resource.id) == request.auth.uid;
match /votes/{voteId} {
allow read: if protectedPost(postId)
|| publicPost(postId);
allow create: if isVote(request.resource.data)
&& postVisibility(postId) in ['public', 'protected']
&& userExists(request.auth.uid);
allow update: if isVote(request.resource.data)
&& request.resource.data.authorId == request.auth.uid;
allow delete: if userExists(request.auth.uid)
&& (request.auth.uid == request.resource.data.authorId
|| hatUserRole(request.auth.uid, ['admin']));
}
match /comments/{commentId} {
allow read: if protectedPost(postId) || publicPost(postId);
allow create: if isComment(request.resource.data)
&& postVisibility(postId) in ['public', 'protected']
&& userExists(request.auth.uid);
allow update: if isComment(request.resource.data)
&& request.resource.data.authorId == request.auth.uid;
allow delete: if userExists(request.auth.uid)
&& (request.auth.uid == request.resource.data.authorId
|| hatUserRole(request.auth.uid, ['admin']));
}
}
}
}
对于创建和更新,一切正常。
此后,我创建了两个方法实现来删除文档,其中两个使用id
来实现,
public deletePost(postId: string): Observable<void> {
const postRef = this.db.collection('posts').doc(postId).ref;
return fromPromise(this.db.firestore.runTransaction((transaction => {
return transaction.get(postRef).then(snapshot => {
if (!snapshot.exists) {
this.snackBar.open('Post doesn\'t exist', 'close');
} else {
const auth = snapshot.data().authorId === this._userId;
if (auth) {
transaction.delete(postRef);
} else {
this.snackBar.open('You\' not allowed to do that!');
}
}
});
})));
}
使用交易,并且:
protected removeElement(elementId: string): Observable<any> {
return fromPromise(this.db.collection(this.dbCollection).doc(elementId).delete());
}
不使用任何交易,只需删除即可。
这些方法都不起作用。
使用第一种方法时,我得到:
ERROR Error: Server responded with status
at new FirestoreError (index.cjs.js:346)
at T.<anonymous> (index.cjs.js:6901)
at Ab (index.js:23)
at T.push../node_modules/@firebase/webchannel-wrapper/dist/index.js.g.dispatchEvent (index.js:21)
at te (index.js:66)
at ve (index.js:69)
at T.push../node_modules/@firebase/webchannel-wrapper/dist/index.js.g.jb (index.js:67)
at T.push../node_modules/@firebase/webchannel-wrapper/dist/index.js.g.Na (index.js:67)
at XMLHttpRequest.wrapFn (zone.js:1188)
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:421)
at Object.onInvokeTask (core.js:3815)
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:420)
at Zone.push../node_modules/zone.js/dist/zone.js.Zone.runTask (zone.js:188)
at ZoneTask.push../node_modules/zone.js/dist/zone.js.ZoneTask.invokeTask [as invoke] (zone.js:496)
at invokeTask (zone.js:1540)
at XMLHttpRequest.globalZoneAwareCallback (zone.js:1566)
我得到第二个:
ERROR Error: Missing or insufficient permissions.
at new FirestoreError (index.cjs.js:346)
at index.cjs.js:7088
at W.<anonymous> (index.cjs.js:7033)
at Ab (index.js:23)
at W.push../node_modules/@firebase/webchannel-wrapper/dist/index.js.g.dispatchEvent (index.js:21)
at Re.push../node_modules/@firebase/webchannel-wrapper/dist/index.js.Re.Ca (index.js:98)
at ye.push../node_modules/@firebase/webchannel-wrapper/dist/index.js.g.Oa (index.js:86)
at dd (index.js:42)
at ed (index.js:39)
at ad (index.js:37)
at L.push../node_modules/@firebase/webchannel-wrapper/dist/index.js.g.Sa (index.js:36)
at L.push../node_modules/@firebase/webchannel-wrapper/dist/index.js.g.nb (index.js:35)
at Ab (index.js:23)
at T.push../node_modules/@firebase/webchannel-wrapper/dist/index.js.g.dispatchEvent (index.js:21)
at ve (index.js:68)
at T.push../node_modules/@firebase/webchannel-wrapper/dist/index.js.g.jb (index.js:67)
at T.push../node_modules/@firebase/webchannel-wrapper/dist/index.js.g.Na (index.js:67)
at XMLHttpRequest.wrapFn (zone.js:1188)
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:421)
at Object.onInvokeTask (core.js:3815)
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:420)
at Zone.push../node_modules/zone.js/dist/zone.js.Zone.runTask (zone.js:188)
at ZoneTask.push../node_modules/zone.js/dist/zone.js.ZoneTask.invokeTask [as invoke] (zone.js:496)
at invokeTask (zone.js:1540)
at XMLHttpRequest.globalZoneAwareCallback (zone.js:1566)
由于我不确定问题出在哪里,所以我有一些理论:
也许在rules
中,我写
allow delete: if userExists(request.auth.uid) && findPostAuthor(request.resource.data.id) == request.auth.uid;
我认为,由于我仅使用Id
直接查看文档,因此request.resource.data.id
不能包含任何内容。
我还认为,如果transactions
不起作用,可能是因为它的实际工作方式与我们在其他交易功能上看到的确实不同。
在我使用angularFire2
时,this.db => AngularFirestore,this.dbCollection =>'posts',在任何帖子的结构中,都有一个autorId
字段,是一个字符串。
答案 0 :(得分:0)
对于delete
,您需要将request.auth.uid
与resource.data.uid
进行比较,而不是与request.resource.data.uid
进行比较。例如:
match /bookmarks/{id} {
allow create: if request.resource.data.uid == request.auth.uid
allow read, delete: if resource.data.uid == request.auth.uid
}