【发布时间】:2019-11-11 06:15:58
【问题描述】:
我有以下文档层次结构: /organizations/{orgId}/classes/{classId}/students/{studentId}
想法是班级文档有一个teacherUid字段,该字段当前存储分配给班级的老师的Uid。只有教师或管理员才能在课堂上阅读/创建/编辑学生。 *请注意,我只是在测试老师的阅读,遇到了这个障碍,之后我将对创建/更新权限应用相同的规则。
我有以下 firestore.rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /organizations/{orgId} {
allow read: if isAdmin();
allow create, update: if isAdmin();
match /classes/{classId} {
allow read: if request.auth.uid != null;
allow create, update: if isAdmin();
match /students/{studentId} {
allow read: if isAdmin() || belongsToCurrentClass();
allow create, update: if isAdmin();
}
}
}
}
}
function isAdmin() {
// Removed for security. isAdmin routine currently works correctly
}
function belongsToCurrentClass() {
// returns true if the authenticated user is the teacher of the requested class
return get(/databases/$(database)/documents/organizations/$(orgId)/classes/$(classId)).data.teacherUid == request.auth.uid;
}
这似乎不起作用。虽然它正确地允许管理员读取/创建/编辑,但它不允许经过身份验证的用户使用与存储在父类文档中的teacherUid 值相同的 request.auth.uid 进行读取。
我已经使用在线 firebase 控制台 firestore 模拟器以及运行本地 firestore 模拟器的 mocha 单元测试对此进行了测试。
我似乎无法弄清楚问题所在。
以下是我的 test.js 文档的相关部分:
const fs = require('fs');
const path = require('path');
const TEST_FIREBASE_PROJECT_ID = 'test-firestore-rules-project';
const firebase = require('@firebase/testing');
const authTeacher = {
uid: 'testTeacher1',
};
const authAdmin = {
// Removed for security
};
before(async () => {
// The above was from the codelab. Commenting out the below since we aren't testing rules at this moment.
const rulesContent = fs.readFileSync(path.resolve(__dirname, '../../firestore.rules'));
await firebase.loadFirestoreRules({
projectId: TEST_FIREBASE_PROJECT_ID,
rules: rulesContent,
});
});
after(() => {
firebase.apps().forEach(app => app.delete());
});
...
describe('Classes/Students/* rules', () => {
const testClassPath = 'organizations/testOrg/classes/testClass';
const testStudentPath = testClassPath + '/students/testStudent';
const newStudentPath = testClassPath + '/students/newStudent';
const testOtherClassPath = 'organizations/testOrg/classes/testClass';
const testOtherStudentPath = testOtherClassPath + '/students/testOtherStudent';
const newOtherStudentPath = testOtherClassPath + '/students/newOtherStudent';
const dbUnauth = firebase
.initializeTestApp({
projectId: TEST_FIREBASE_PROJECT_ID,
})
.firestore();
const dbTeacher = firebase
.initializeTestApp({
projectId: TEST_FIREBASE_PROJECT_ID,
auth: authTeacher,
})
.firestore();
const dbAdmin = firebase
.initializeTestApp({
projectId: TEST_FIREBASE_PROJECT_ID,
auth: authAdmin,
})
.firestore();
before(async () => {
const admin = firebase
.initializeAdminApp({
projectId: TEST_FIREBASE_PROJECT_ID,
})
.firestore();
// Create Class - for testing classes that belong to the authenticated user
await admin.doc(testClassPath).set({
teacherUid: authTeacher.uid,
});
// Create Student
await admin.doc(testStudentPath).set({
name: 'John Smith',
});
// Create Other Class - for testing classes that belong to other users
await admin.doc(testOtherClassPath).set({
teacherUid: 'someOtherTeacherUid',
});
// Create Other Student
await admin.doc(testOtherStudentPath).set({
name: 'Cave Johnson',
});
});
after(() => {
// Clear data from the emulator
firebase.clearFirestoreData({ projectId: TEST_FIREBASE_PROJECT_ID });
});
it('Unauthenticated users cannot access students', async () => {
await firebase.assertFails(dbUnauth.doc(testStudentPath).get());
});
it('Unauthenticated users cannot create students', async () => {
await firebase.assertFails(
dbUnauth.doc(newStudentPath).set({
name: 'Jane Doe',
})
);
});
it('Non-admin users can read students', async () => {
await firebase.assertSucceeds(dbTeacher.doc(testStudentPath).get());
});
it('Non-admin users cannot read students from another user', async () => {
await firebase.assertFails(dbTeacher.doc(testOtherStudentPath).get());
});
it('Non-admin users can edit students', async () => {
await firebase.assertSucceeds(
dbTeacher.doc(testStudentPath).set({
anotherProperty: 'Some Value',
})
);
});
it('Non-admin users cannot edit students from another user', async () => {
await firebase.assertFails(
dbTeacher.doc(testOtherStudentPath).set({
anotherProperty: 'Some Value',
})
);
});
it('Non-admin users can create students', async () => {
await firebase.assertSucceeds(
dbTeacher.doc(newStudentPath).set({
name: 'Jane Doe',
})
);
});
it('Non-admin users cannot create students in a class they do not belong to', async () => {
await firebase.assertFails(
dbTeacher.doc(testOtherStudentPath).set({
name: 'Jane Doe',
})
);
});
it('Non-admin users cannot delete students', async () => {
await firebase.assertFails(dbTeacher.doc(testStudentPath).delete());
});
it('Admin users can read students', async () => {
await firebase.assertSucceeds(dbAdmin.doc(testStudentPath).get());
});
it('Admin users can create students', async () => {
await firebase.assertSucceeds(
dbAdmin.doc(newStudentPath).set({
name: 'Jane Doe',
})
);
});
it('Admin users can edit students', async () => {
await firebase.assertSucceeds(
dbAdmin.doc(testStudentPath).set({
anotherProperty: 'Some Value',
})
);
});
it('Admin users cannot delete students', async () => {
await firebase.assertFails(dbAdmin.doc(testStudentPath).delete());
});
});
这是运行单元测试时的错误输出:
PS C:\Local\Personal\Angular Projects\TSI\functions> npm test
> functions@ test C:\Local\Personal\Angular Projects\TSI\functions
> mocha
Organization rules
√ Unauthenticated users cannot read organizations (48ms)
√ Unauthenticated users cannot create orgs organizations
√ Unauthenticated users cannot delete organizations
√ Non-admin users cannot read organizations (45ms)
√ Non-admin users cannot edit organizations
√ Non-admin users cannot create organizations
√ Non-admin users cannot delete organizations
√ Admin users can read organizations (47ms)
√ Admin users can create organizations
√ Admin users can edit organizations
√ Admin users cannot delete organizations
Classes rules
√ Unauthenticated users cannot access classes
√ Unauthenticated users cannot create classes
√ Unauthenticated users cannot delete classes
√ Non-admin users can read classes (38ms)
√ Non-admin users cannot edit classes
√ Non-admin users cannot create classes
√ Non-admin users cannot delete classes
√ Admin users can read classes
√ Admin users can create classes
√ Admin users can edit classes
√ Admin users cannot delete classes
Classes/Students/* rules
√ Unauthenticated users cannot access students
√ Unauthenticated users cannot create students
1) Non-admin users can read students
√ Non-admin users cannot read students from another user
2) Non-admin users can edit students
√ Non-admin users cannot edit students from another user
3) Non-admin users can create students
√ Non-admin users cannot create students in a class they do not belong to
√ Non-admin users cannot delete students
√ Admin users can read students
√ Admin users can create students
√ Admin users can edit students
√ Admin users cannot delete students
32 passing (3s)
3 failing
1) Classes/Students/* rules
Non-admin users can read students:
FirebaseError:
Null value error. for 'get' @ L15
at new FirestoreError ...
2) Classes/Students/* rules
Non-admin users can edit students:
FirebaseError: 7 PERMISSION_DENIED:
false for 'update' @ L16
at new FirestoreError ...
3) Classes/Students/* rules
FirebaseError: 7 PERMISSION_DENIED:
false for 'create' @ L16
at new FirestoreError ...
npm ERR! Test failed. See above for more details.
【问题讨论】:
-
仅供参考,如果我可以在调试会话中运行规则评估,我可能会在 10 秒内回答这个问题。如果您知道如何在规则引擎评估请求时检查值,我很乐意在这里。为了不在同一个 SO 帖子中问两个问题,我在这里询问了调试建议:stackoverflow.com/questions/58796514/…
标签: firebase google-cloud-firestore firebase-security