Firestore 安全规则:仅当新文档 ID 与用户 ID 相同时才允许用户创建文档

Mat*_*out 7 firebase firebase-security google-cloud-firestore

当用户第一次登录时,我还需要调用一个函数,该函数在我的 firestore 用户集合中创建一个文档来存储他们的个人资料数据。使用网络 SDK。

(我以前使用带有 firebase 函数的新用户触发事件,但是等待冷函数启动太慢了)。

安全规则要求

需要确保用户只能在文档 id 与其用户 id 相同的情况下才能创建文档(以防止用户创建其他文档)。需要确保此文档尚不存在。

尝试 - 在模拟器中工作,而不是 IRL

这些测试在模拟器中通过,但在 IRL 中不通过。

// Allow users to create a doc if the doc ID == their user id
allow create: if path("/databases/" + database + "/documents/users/" + request.auth.uid) == request.path;
Run Code Online (Sandbox Code Playgroud)

或者

allow create: if /databases/$(database)/documents/users/$(request.auth.uid) == request.resource['__name__']
Run Code Online (Sandbox Code Playgroud)

也试过这个(同样,在模拟器中工作,但不是 IRL)

match /users/{userId} {
    // Allow users to read their own profile
    allow create: if request.auth.uid == userId;
}
Run Code Online (Sandbox Code Playgroud)

Mat*_*out 10

更新

由于 firestore 规则的工作方式发生了一些变化,以及“getAfter”函数的工作方式发生了变化,我最近不得不更新我的规则集。具体来说,我现在可以使用request.resource进行数据comarisons。无论如何,现在看来我可以用更简单的规则来实现我的目标,所以我想我会更新这个答案并分享。

目标

  • 仅当新文档 ID 与其用户 ID 匹配时,用户才能创建文档。
  • 用户不能声明自己是“管理员”,如果“管理员”是一个字段,则阻止创建/更新/写入请求(除非他们已经是管理员)
service cloud.firestore {
  match /databases/{database}/documents {
  
    // Allow users to create a document for themselves in the users collection
    match /users/{document=**} {
      allow create: if request.resource.id == request.auth.uid &&
        !("admin" in request.resource.data);
    }
    
    // Allow users to read, write, update documents that have the same ID as their user id
    match /users/{userId} {   
        // Allow users to read their own profile (doc id same as user id)
      allow read: if request.auth.uid == userId;
      
      // Allow users to write / update their own profile as long as no "admin"
      // field is trying to be added or created - unless they are already an admin
      allow write, update: if request.auth.uid == userId &&
        (
          !("admin" in request.resource.data) ||
          get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true // allow admin to update their own profile
        )

      // Allow users to read their own feeds
      match /feeds/{document=**} {
        allow read: if request.auth.uid == userId;
      } 
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

旧答案

所以我想出了如何以一种解决方法来做到这一点。我还有一些额外的写入/更新条件来阻止用户更改他们的权限级别。这是出于某种原因,防止发生任何“创建”。所以我必须在创建和写入/更新规则中镜像相同的条件。出于某种原因,这是必要的。

这个新的规则结构实现了以下功能

第一部分,用于创建规则

  • 允许唯一经过身份验证的用户仅在“用户”集合中 创建文档(在用户设置过程中,会自动创建一个文档,其 ID 与他们的用户 ID 相同)。
  • 不允许创建包含“admin”字段的文档,这表明他们正在尝试获得管理员访问权限。
  • 似乎在创建期间验证文档的 id 是不可能的,因此下面的附加写入/更新规则

第二部分 - 读取、更新、写入

  • 允许用户仅读取/写入/更新与他们的用户 ID 具有相同 ID的文档(用户尝试使用其用户 ID 以外的 ID 创建文档将失败,还可以防止用户通过操纵垃圾邮件创建大量文档客户端 JS 请求。)
  • 不允许用户编写/更新他们的个人资料以包含“管理员”字段

规则

service cloud.firestore {

      match /databases/{database}/documents {
      
        // Allow users to create documents in the user's collection
        match /users/{document=**} {
          allow create: if request.auth.uid != null &&
            !("admin" in getAfter(/databases/$(database)/documents/users/$(request.auth.uid)).data);
        }
        
        // Allow users to read, write, update documents that have the same ID as their user id
        match /users/{userId} {   
            // Allow users to read their own profile (doc id same as user id)
          allow read: if request.auth.uid == userId;
          
          // Allow users to write / update their own profile as long as no "admin" field is trying to be added or created
          allow write, update: if request.auth.uid == userId &&
            !("admin" in getAfter(/databases/$(database)/documents/users/$(request.auth.uid)).data);
        }
      }
    }
Run Code Online (Sandbox Code Playgroud)

PS 这根本不直观,所以如果有人有更好的解决方法,请发布。此外,我真的希望一旦 firestore 1.0 发布,它将为规则和规则文档带来一些巨大的改进。