Описание
У меня проблема с удалением с помощью firestore.Короче говоря, я создал правило безопасности для таких сообщений:
Сначала в правилах есть несколько функций:
service 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
s:
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
не работает, это может быть из-за того, как яt на самом деле работает действительно отличается от того, что мы видим в других функциях транзакций.
Поскольку я использую angularFire2
, this.db => AngularFirestore, this.dbCollection => 'posts' и в структуре любого сообщения есть поле autorId
, представляющее собой строку.