Firestore.rules自定义函数未按预期评估

我具有以下文档层次结构: / 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;
}

这似乎不起作用。虽然它正确地允许管理员读取/创建/编辑,但不允许具有相同的request.auth.uid的已认证用户读取,该请求与存储在父类文档中的TeacherUid值相同。

我已经使用在线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.
jf5437 回答:Firestore.rules自定义函数未按预期评估

U。 :)发现了问题。在这里发布我的答案,以防其他人绊倒。

每个Firestore custom function documentation都需要在其使用的变量范围内声明该函数,或者可以将这些变量作为参数传递。

类似于SO的相同问题: Moving Firestore security rule to custom function breaks rule

以下两个选项之一有效:

选项1-在变量范围内声明:

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} {
        function belongsToCurrentClass() {
          // retuns 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;
        }

        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() || belongsToCurrentClass();
        }
      }
    }
  }
}

function isAdmin() {
  // Removed for security.
}

选项2-将变量作为参数传递:

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() || belongsToCurrentClass(database,orgId,classId);
        }
      }
    }
  }
}

function isAdmin() {
  // Removed for security.
}

function belongsToCurrentClass(database,classId) {
  // 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;
}

我个人选择了选项1。虽然我不喜欢在代码中声明通过所有参数传递的函数也很丑陋,并且甚至不在该范围之外调用该函数,所以它更有意义在那里宣布。

本文链接:https://www.f2er.com/3126221.html

大家都在问