Docs 菜单

Docs 主页Atlas App Services

Device Sync Permissions Guide

在此页面上

  • 使用模板应用
  • 关于这些示例
  • 读取和写入自己的数据
  • 写入自己的数据,读取所有数据
  • 管理员特权
  • 启用和配置自定义用户数据
  • 设置管理员权限
  • 受限新闻源
  • 启用和配置自定义用户数据
  • 创建身份验证触发器函数
  • 设置受限权限
  • 创建 subscribeToUser 函数
  • 动态协作
  • 设置权限
  • 添加协作者
  • 安全性
  • 用户搜索
  • 分层权限
  • 启用和配置自定义用户数据
  • 创建身份验证触发器函数
  • 创建 joinTeam 函数
  • 定义权限

本页介绍如何针对以下常见用例设置 Device Sync 应用的权限:

使用 Device Sync 权限指南模板应用,您可以快速启动并运行其中一些权限模型。每个模板应用都带有后端和 Node.js 演示客户端。后端已设置好此处所述的所有功能、权限和触发器。演示客户端连接到后端,并运行一个简单的脚本来演示后端如何工作。

使用这些模板需要经过身份验证的 App Services CLI。

App Services CLI 在npm 上可用。要在系统上安装 CLI,请确保您有 Node.js 已安装,然后在 shell 中运行以下命令:

npm install -g atlas-app-services-cli

有关登录说明,请参阅CLI 参考页

完成登录后,可以使用带有 --template 标记的 apps create 命令来实例化模板。

appservices apps create --template=TEMPLATE_NAME

TEMPLATE_NAME 可以是以下之一:

部署模板应用程序

此处的示例使用默认角色。这意味着相同的权限规则适用于您的应用程序中的所有集合。随着您的应用程序变得越来越复杂,您可能需要特定于集合的角色,这些角色仅适用于某些集合,而不适用于其他集合。特别是,如果默认角色中的规则表达式使用特定集合中的对象上不存在的“可查询字段”,您可以通过提供特定于集合的角色来覆盖该集合的规则。有关更多信息,请参阅基于角色的权限

在这种情况下,用户可以读取或写入自己的数据,但不能读取或写入其他用户的数据。考虑一个笔记应用,用户希望永久保存并跨自己的设备共享笔记,但这些笔记是用户帐户私有的,不与他人共享。

当且仅当文档的 owner_id 字段等于用户的 ID 时,此策略才允许用户创建和编辑该文档。

要设置“读取并写入自己的数据”策略,请遵循以下一般步骤:

  1. 登录到 Realm 用户界面,然后单击左侧面板中的 Sync(同步)。

  2. Sync Type(同步类型)下,选择 Flexible(灵活)。

  3. 将开关设置为启用 Development Mode

  4. 选择要同步的集群。

  5. Define a Database Name:选择 +Add a new database(+ 添加新数据库)并输入 Realm 将用于存储同步对象的数据库的名称。可以将其命名为任何想要的名称。一种常用策略是以正在编写的应用来命名该数据库。

  6. Select Queryable Fields:在 owner_id 中输入。这样,权限表达式(接下来将进行设置)将可以使用名称为 owner_id 的任何字段。

  7. 跳过 Advanced Configuration,然后单击 Save Changes 以启用 Sync。

现在,您需要配置权限。前往 Rules 页面。单击 Default Roles and Filters 按钮编辑默认角色。使用模板下拉列表选择名为“用户只能读取和写入自己的数据”的模板。

这将使用以下内容填充规则表达式框:

{
"name": "owner-read-write",
"apply_when": {},
"document_filters": {
"read": { "owner_id": "%%user.id" },
"write": { "owner_id": "%%user.id" }
},
"read": true,
"write": true
}

请注意,document_filters.readdocument_filters.write 表达式使用我们在上面标记为“可查询”的 owner_id 字段。此外,它还使用%%user扩展项来读取请求用户的 ID。App Services 在评估时用对应值(本例中为用户对象)替换(或“展开”)扩展项。

评估对给定文档的权限时,App Services 会先检查 document_filters.readdocument_filters.write,再检查对应的顶级 readwrite 表达式。例如,如果 document_filters.write 的计算结果为 false,则写入访问被拒绝,且不检查后续规则。但是,如果 document_filters.write 的计算结果为 true,App Services 将检查顶级 write 表达式。在本例中,writetrue,因此,授予对整个文档的写入访问权限。有关更多信息,请参阅 Device Sync 兼容权限

在这种情况下,用户可以读取所有数据,但只能写入自己的数据。考虑一个菜谱应用,用户可以读取所有菜谱并添加新菜谱。任何使用该应用的人都可以查看他们添加的菜谱。用户只能更新或删除他们贡献的菜谱。

要设置“写入自己的数据,读取所有数据”策略,请遵循以下一般步骤:

  1. 登录到 Realm 用户界面,然后单击左侧面板中的 Sync

  2. Sync Type(同步类型)下,选择 Flexible(灵活)。

  3. 将开关设置为启用 Development Mode

  4. 选择要同步的集群。

  5. Define a Database Name:选择 +Add a new database(+ 添加新数据库)并输入 Realm 将用于存储同步对象的数据库的名称。可以将其命名为任何想要的名称。一种常用策略是以正在编写的应用来命名该数据库。

  6. Select Queryable Fields:在 owner_id 中输入。这样,权限表达式(接下来将进行设置)将可以使用名称为 owner_id 的任何字段。

  7. 跳过 Advanced Configuration,然后单击 Save Changes 以启用 Sync。

现在,您需要配置权限。前往 Rules 页面。单击 Default Roles and Filters 按钮编辑默认角色。使用模板下拉列表选择名为“用户可以读取所有数据,但只能写入自己的数据”的模板。

这将使用以下内容填充规则表达式框:

{
"name": "owner-write",
"apply_when": {},
"document_filters": {
"read": true,
"write": { "owner_id": "%%user.id" }
},
"read": true,
"write": true
}

请注意,document_filters.read 表达式设置为 true,表示无论哪个用户通过身份验证,他们都可以读取所有数据。document_filters.write 表达式使用我们在上面标记为 "可查询" 的 owner_id 字段,并使用 %%user 扩展项来匹配请求用户的 ID。App Services 在评估时用对应值(本例中为用户对象)替换(“展开”)扩展项。

当且仅当 document_filters.write 的计算结果为 true 时,App Services 才会检查顶级 write 表达式。当 document_filters.write 的计算结果为 false 时,无论顶级 write 表达式的内容如何,写入访问都会被拒绝。有关更多信息,请参阅 Device Sync 兼容权限

在此权限策略中,具有特定“管理员”角色的用户可以读取和写入任何文档。没有该指定角色的用户只能读取和写入自己的数据。要使此策略发挥作用,首先需要定义哪些用户具有管理员权限。

Atlas Apps 允许您将集群中的自定义用户数据与应用程序用户相关联。利用此功能,您可以创建一个文档,文档的一个字段指示用户是否具有管理权限。具体设置方法有很多种,一种方法是添加一个名为 isGlobalAdmin 的布尔属性,对于具有高级权限的用户,该属性设置为 true。另一种方法是创建一个名为 role 的字符串字段,其预期值之一可以是“admin”。

在以下示例中,我们将创建的自定义用户对象具有一个 _id 字段(对应于用户 ID)和 3 个附加字段:firstNamelastNameisGlobalAdmin

{
"_id" : "1234",
"firstName": "Lily",
"lastName": "Realmster",
"isGlobalAdmin": true
}

注意

使用自定义用户数据获取权限时,切勿允许客户端写入自定义用户数据对象。这样做将允许任何用户授予自己任何权限。相反,应使用服务器端的系统函数来更新自定义用户数据对象。

Atlas App Services 在关联的 MongoDB Atlas 集群中存储与自定义用户数据相对应的 MongoDB 文档。当为应用程序配置自定义用户数据时,请指定集群、数据库、集合,最后指定用户 ID 字段,该字段将自定义用户数据文档映射到经过身份验证的用户 ID。

要在 App Services 用户界面中启用自定义用户数据,请执行以下步骤:

  1. 单击左侧面板中的 App Users(应用用户)。

  2. 选择 User Settings 标签页并找到 Custom User Data 部分。

  3. Enable Custom User Data 切换设置为 On

  4. 指定以下值:

    • Cluster Name:将包含自定义用户数据数据库的关联 MongoDB 集群的名称。

    • Database Name:将包含自定义用户数据集合的 MongoDB 数据库的名称。

    • Collection Name:将包含自定义用户数据的 MongoDB 集合的名称。

  5. 指定用户 ID 字段。自定义用户数据集合中的每个文档都必须有一个映射到特定用户的字段。该字段必须存在于映射到用户的每个文档中,并且必须包含字符串形式的用户 ID。建议您使用标准 _id 字段来存储用户 ID。MongoDB 自动对 _id 字段施加约束,以确保唯一性。

    注意

    如果此集合中的两个文档包含相同的用户 ID 值,则 App Services 将使用第一个匹配的文档,这会导致意外结果。

  6. 保存并部署更改。

    注意

    更新自定义用户数据后重启同步会话

    同步服务器会在会话期间缓存自定义用户数据。因此,更新自定义用户数据后必须重启同步会话,以避免补偿写入错误。要重启同步会话,请暂停并恢复客户端应用程序中所有打开的同步会话。有关从客户端 SDK 暂停并恢复同步的更多信息,请参阅您首选的 SDK:

提示

另请参阅:

启用自定义用户数据后,您可以实施管理员权限策略。为此,请遵循以下一般步骤:

  1. 登录到 Realm 用户界面,然后单击左侧面板中的 Sync

  2. Sync Type(同步类型)下,选择 Flexible(灵活)。

  3. 将开关设置为启用 Development Mode

  4. 选择要同步的集群。

  5. Define a Database Name:选择 +Add a new database(+ 添加新数据库)并输入 Realm 将用于存储同步对象的数据库的名称。可以将其命名为任何想要的名称。一种常用策略是以正在编写的应用来命名该数据库。

  6. Select Queryable Fields:在 owner_id 中输入。这样,权限表达式(接下来将进行设置)将可以使用名称为 owner_id 的任何字段。

  7. 跳过 Advanced Configuration,然后单击 Save Changes 以启用 Sync。

现在,需要配置权限。导航至 Rules(规则)页面。单击 Default Roles and Filters(默认角色和筛选器)按钮以编辑默认角色。使用模板下拉列表选择名为 "Users can read and write their own data, admins can read and write all data"(用户可以读取和写入自己的数据,管理员可以读取和写入所有数据)的模板。

这将使用以下内容填充规则表达式框:

[
{
"name": "admin",
"apply_when": {
"%%user.custom_data.isGlobalAdmin": true
},
"document_filters": {
"read": true,
"write": true
},
"read": true,
"write": true
},
{
"name": "user",
"apply_when": {},
"document_filters": {
"read": { "owner_id": "%%user.id" },
"write": { "owner_id": "%%user.id" }
},
"read": true,
"write": true
}
]

注意

更改默认设置

此配置有两个默认角色。第一个定义管理员的权限。请注意,自动生成的表达式假定自定义用户数据文档中有一个名为 isGlobalAdmin 的布尔字段。根据您定义自定义用户数据文档的方式,您可能需要来更改此设置。

第二个默认角色指定所有其他用户的规则。默认设置是限制用户只能读取和写入自己的数据。可以将这两个字段或其中之一更改为 true,使用户能够读取和/或写入所有数据。请参阅前面的部分,了解有关这策略的更多信息。

注意

模板可用!

要使用后端模板并获取演示客户端,请运行以下命令:

appservices apps create --name=restricted-feed --template=flex-sync-guides.restricted-feed

自定义用户数据配置错误的解决办法:有时,模板生成器无法将自定义用户数据配置正确复制到新应用。可以按如下方式修复此问题:appservices apps create 命令应该输出一些有关刚刚创建的应用的 JSON。从该 JSON 中复制 "url" 值(类似于 https://services.cloud.mongodb.com/groups/...),并在浏览器中访问该 URL。如果出现相应提示,请登录。在应用仪表板的左侧面板中,单击 App Users(应用用户)。单击 Custom User Data(自定义用户数据)。确保 Enable Custom User Data(启用自定义用户数据)为 ON(开)。如果未开启,请将其打开并分别为 Cluster Name(集群名称)、Database Name(数据库名称)和 Collection Name(集合名称)输入 "mongodb-atlas"、"Item" 和 "User"。对于 User ID Field(用户 ID 字段),请输入 _id。单击 Save(保存)(或 Save Draft(保存草稿),然后进行部署)。

接下来,在终端中进入客户端目录,安装依赖项,并运行演示:

cd restricted-feed/frontend/flex-sync-guides.restricted-feed/
npm install
npm run demo

阅读控制台的输出结果,了解演示正在执行的操作。

在此权限策略中,用户可以创建自己的内容并订阅其他创建者的内容。与管理员权限场景一样,我们将使用自定义用户数据集合来定义用户订阅读取哪些作者的内容。

灵活 Device Sync 支持查询数组,因此我们将在用户数据对象中创建数组。该数组包含此用户有权“关注”的作者 ID。然后,我们设置一个订阅,其核心要求是:“提供所有符合以下条件的文档:我本人是作者,或者作者的 ID 在我的自定义用户数据的作者数组中。”

重要

当用户订阅或取消订阅某个作者时,我们会更新自定义用户数据中的数组,但这些更改要等到当前会话关闭且新会话启动后才会生效。

注意

大小限制

在此示例中,我们将在自定义用户数据中创建一个数组。该数组的大小不受 App Services 限制,但由于每个请求中都包含此数据,因此我们建议将大小保持在 16KB 以下,这足以容纳 1000 个 128 位 GUID 样式的用户 ID。

注意

使用自定义用户数据获取权限时,切勿允许客户端写入自定义用户数据对象。这样做将允许任何用户授予自己任何权限。相反,应使用服务器端的系统函数来更新自定义用户数据对象。

要在 App Services 用户界面中启用自定义用户数据,请执行以下步骤:

  1. 单击左侧面板中的 App Users(应用用户)。

  2. 选择 User Settings 标签页并找到 Custom User Data 部分。

  3. Enable Custom User Data 切换设置为 On

  4. 指定以下值:

    • Cluster Name:将包含自定义用户数据数据库的关联 MongoDB 集群的名称。

    • Database Name:将包含自定义用户数据集合的 MongoDB 数据库的名称。

    • Collection Name:将包含自定义用户数据的 MongoDB 集合的名称。

  5. 指定用户 ID 字段。自定义用户数据集合中的每个文档都必须有一个映射到特定用户的字段。该字段必须存在于映射到用户的每个文档中,并且必须包含字符串形式的用户 ID。建议您使用标准 _id 字段来存储用户 ID。MongoDB 自动对 _id 字段施加约束,以确保唯一性。

    注意

    如果此集合中的两个文档包含相同的用户 ID 值,则 App Services 将使用第一个匹配的文档,这会导致意外结果。

  6. 保存并部署更改。

    注意

    更新自定义用户数据后重启同步会话

    同步服务器会在会话期间缓存自定义用户数据。因此,更新自定义用户数据后必须重启同步会话,以避免补偿写入错误。要重启同步会话,请暂停并恢复客户端应用程序中所有打开的同步会话。有关从客户端 SDK 暂停并恢复同步的更多信息,请参阅您首选的 SDK:

提示

另请参阅:

我们需要创建一个身份验证触发器函数,该函数在用户首次进行身份验证时创建自定义用户对象。为此,请按照以下步骤操作:

  1. 登录到 Realm 用户界面,然后单击左侧面板中的 Triggers

  2. 单击 Add a Trigger 按钮。

  3. Trigger Type 切换设置为 Authentication

  4. 在“触发器详细信息”下,指定以下值:

    • Name:"onUserCreated"

    • Enbaled:将开关切换到 "On"(开)

    • Action Type:选择“Create”(创建)

    • Provider(s):选择“Email/Password”(电子邮件/密码),或您使用的任何身份验证提供程序

    • Select an Event Type:选择“Function”(函数)

    • Function:选择 "+New Function"(+ 新增函数),然后:

      • Function Name:"onUserCreated"

      • Function:用以下函数替换占位符文本:

      exports = function(authEvent) {
      const user = authEvent.user;
      const collection = context.services.get("mongodb-atlas").db("Item").collection("User");
      const newDoc = {
      _id: user.id,
      email: user.data.email, // Useful for looking up user IDs by email later - assuming email/password auth is used
      team: "", // Used for tiered privileges
      isTeamAdmin: false, // Used for tiered privileges
      isGlobalAdmin: false, // Used for admin privileges
      subscribedTo: [], // Used for restricted feed
      };
      return collection.insertOne(newDoc);
      };

在自定义用户数据对象中,创建一个数组以保存用户关注的每个作者的 _id 值。在此示例中,我们将其称为“订阅”。我们的用户数据对象与以下所示类似,其中,Lily Realmster ("_id": "1234") 订阅了用户 "456" 和 "789" 写入的所有文档:

{
"_id" : "1234",
"firstName": "Lily",
"lastName": "Realmster",
"user.custom_data.subscribedTo": [
"456",
"789"
]
}

现在您可以实施限制特权策略。为此,请遵循以下一般步骤:

  1. 登录到 Realm 用户界面,然后单击左侧面板中的 Sync

  2. Sync Type(同步类型)下,选择 Flexible(灵活)。

  3. 将开关设置为启用 Development Mode

  4. 选择要同步的集群。

  5. Define a Database Name:选择 +Add a new database(+ 添加新数据库)并输入 Realm 将用于存储同步对象的数据库的名称。可以将其命名为任何想要的名称。一种常用策略是以正在编写的应用来命名该数据库。

  6. Select Queryable Fields:在 owner_id 中输入。这样,权限表达式(接下来将进行设置)将可以使用名称为 owner_id 的任何字段。

  7. 跳过 Advanced Configuration,然后单击 Save Changes 以启用 Sync。

现在,需要配置权限。导航至 Rules(规则)页面。单击 Default Roles and Filters(默认角色和筛选器)按钮以编辑默认角色。使用模板下拉列表选择名为 "Users can only read and write their own data"(用户只能读取和写入自己的数据)的模板。这将用以下内容填充规则表达式框,这并不完全是我们想要的,但为我们提供了大部分逻辑:

{
"name": "owner-read-write",
"apply_when": {},
"document_filters": {
"read": { "owner_id": "%%user.id" },
"write": { "owner_id": "%%user.id" }
},
"read": true,
"write": true
}

请注意,用户当前只能读取自己的文档 ("read": {"owner_id": "%%user.id"})。我们可以更改此设置,以包含作者的 ID 位于用户的 "subscribedTo" 数组中的文档。为此,我们使用 $in 操作符。表达式如下所示:

{
"name": "owner-read-write",
"apply_when": {},
"document_filters": {
"read": {
"owner_id": {
"$in": "%%user.custom_data.subscribedTo"
}
},
"write": { "owner_id": "%%user.id" }
},
"read": true,
"write": true
}

使用此新逻辑更新规则表达式框并保存更改。

注意:我们更改了 document_filters.read 表达式,仅提及 subscribedTo 数组中的作者,但由于 document_filters.read 表达式,用户仍然对他们自己的文档拥有“读取”访问权限。只要授予写入访问权限,就隐式授予了读取访问权限。

我们需要一个让一个用户订阅另一个用户的函数。我们的权限设置为如下:如果订阅者的自定义用户数据“subscribedTo”数组中有作者的用户 ID,则认为订阅者“订阅”了作者。“subscribeToUser”函数接受用户的电子邮件地址,在自定义用户数据集合中查找相应的用户 ID,并将该 ID 添加到请求用户的“subscribedTo”数组中。

请注意,此函数将在系统身份验证下运行并写入自定义用户数据。使用自定义用户数据进行权限设置时,绝不能允许客户端直接编辑自定义用户数据。否则,任何用户都可以授予自己任何权限。相反,请使用系统函数在后端修改用户数据。该函数应该执行所有必要的检查,以确保用户的权限请求有效。

要创建该函数,请按照以下步骤操作:

  1. 登录到 Realm 用户界面,然后单击左侧面板中的 Functions

  2. 单击 Create New Function 按钮。

  3. 指定以下值:

    • Name:"subscribeToUser"

    • Authentication:"System"

  4. 切换到 Function Editor(函数编辑器)标签页,并将占位符文本替换为以下代码:

    exports = async function(email) {
    const collection = context.services.get("mongodb-atlas").db("Item").collection("User");
    // Look up the author object to get the user id from an email
    const author = await collection.findOne({email});
    if (author == null) {
    return {error: `Author ${email} not found`};
    }
    // Whoever called this function is the would-be subscriber
    const subscriber = context.user;
    try {
    return await collection.updateOne(
    {_id: subscriber.id},
    {$addToSet: {
    subscribedTo: author._id,
    }
    });
    } catch (error) {
    return {error: error.toString()};
    }
    };

注意

模板可用!

要使用后端模板并获取演示客户端,请运行以下命令:

appservices apps create --name=add-collaborators --template=flex-sync-guides.add-collaborators

接下来,进入客户端目录,安装依赖项,并运行演示:

cd add-collaborators/frontend/flex-sync-guides.add-collaborators/
npm install
npm run demo

阅读控制台的输出结果,了解演示正在执行的操作。

在动态协作策略中,用户可以创建文档并添加其他用户作为该文档的编辑者。

与“读取和写入自己的数据”策略类似,此策略允许用户创建并编辑文档,只要该文档的 owner_id 字段等于用户的 ID。此外,如果文档的 collaborators 数组字段包含一个用户的 ID,则此用户可以编辑该文档。

若要实施此策略,请执行以下常规步骤:

  1. 登录到 Realm 用户界面,然后单击左侧面板中的 Sync

  2. Sync Type(同步类型)下,选择 Flexible(灵活)。

  3. 将开关设置为启用 Development Mode

  4. 选择要同步的集群。

  5. Define a Database Name:选择 +Add a new database(+ 添加新数据库)并输入 Realm 将用于存储同步对象的数据库的名称。可以将其命名为任何想要的名称。一种常用策略是以正在编写的应用来命名该数据库。

  6. Select Queryable Fields:在 owner_id 中输入。这样,权限表达式(接下来将进行设置)将可以使用名称为 owner_id 的任何字段。

  7. 跳过 Advanced Configuration,然后单击 Save Changes 以启用 Sync。

Select Queryable Fields(选择可查询字段)字段中,同时输入 collaborators。这将是存储同样可以读取和写入文档的用户 ID 的字段。

现在,您需要配置权限。在 Define Permissions(定义权限)下,使用模板下拉列表选择“自定义(从零开始)”。将以下内容粘贴到规则表达式框中:

{
"name": "collaborator",
"apply_when": {},
"document_filters": {
"read": {
"$or": [
{
"owner_id": "%%user.id"
},
{
"collaborators": "%%user.id"
}
]
},
"write": {
"$or": [
{
"owner_id": "%%user.id"
},
{
"collaborators": "%%user.id"
}
]
}
},
"read": true,
"write": true
}

"write" 和 "read" 表达式相同。以其中之一为例。$or 接受一个选项数组。用户可以写入文档的可能情况有两种:

  • 文档的 owner_id 字段等于用户的 ID

  • 文档的 collaborators 数组字段包含用户的 ID。

一般来说,当一个用户被授予写入权限时,该用户会自动获得读取权限。但是,我们不能省略 document_filters.read 表达式,因为 App Services 需要同时定义 document_filters.readdocument_filters.write 才能使角色与 Sync 兼容

用户可以将对其文档的写入权限授予给另一个用户,方法是将该用户的 ID collaborators 添加到文档的数组字段中。这可以在客户端完成。

我们不建议将此模型用于高度敏感的数据。

此模型使用权限系统来保持文档私有,只有文档创建者和添加到该文档的协作者可以访问。但是,如果用户对文档具有写入权限,则他们可以写入文档的任何字段。因此,该策略允许协作者添加其他协作者。它还允许协作者编辑 owner_id 字段。为了实现 Sync 兼容性,字段级权限必须是布尔值字面量,因此我们不能将对这些字段的写入权限限制为特定用户。

具体如何获取其他用户的 ID 取决于应用的详细信息。例如,当一个用户想要将另一个用户添加到文档中时,可能有一个接受电子邮件地址的搜索框。如果给定的电子邮件地址对应于另一个用户,则客户端可以将该用户的 ID 添加到文档的 collaborators 数组中。

Realm 没有内置搜索用户的方法。一般来说,搜索用户的流程如下:

  • 设置身份验证触发器,以便在用户注册时创建用户文件。用户文档包含您稍后用于查找的信息,例如用户的电子邮件地址。

  • 创建一个查询用户的用户数据集合的函数。

  • 当用户想要查找其他用户时,从客户端调用该函数。

要创建身份验证触发器,请按照以下步骤操作:

  1. 登录到 Realm 用户界面,然后单击左侧面板中的 Triggers

  2. 单击 Add a Trigger 按钮。

  3. Trigger Type 切换设置为 Authentication

  4. 在“触发器详细信息”下,指定以下值:

    • Name:"onUserCreated"

    • Enbaled:将开关切换到 "On"(开)

    • Action Type:选择“Create”(创建)

    • Provider(s):选择“Email/Password”(电子邮件/密码),或您使用的任何身份验证提供程序

    • Select an Event Type:选择“Function”(函数)

    • Function:选择 "+New Function"(+ 新增函数),然后:

      • Function Name:"onUserCreated"

      • Function:用以下函数替换占位符文本:

      exports = function(authEvent) {
      const user = authEvent.user;
      const collection = context.services.get("mongodb-atlas").db("Item").collection("User");
      const newDoc = {
      _id: user.id,
      email: user.data.email, // Useful for looking up user IDs by email later - assuming email/password auth is used
      team: "", // Used for tiered privileges
      isTeamAdmin: false, // Used for tiered privileges
      isGlobalAdmin: false, // Used for admin privileges
      subscribedTo: [], // Used for restricted feed
      };
      return collection.insertOne(newDoc);
      };

接下来,创建一个名为 findUser 的系统函数:

  • 登录到 Realm 用户界面,转到 Realm 应用,然后单击左侧面板中的 Functions(函数)。

  • 单击 Create New Function 按钮。

  • 为函数提供一个描述性的 Name(名称)。

  • Authentication:选择 System(系统)。这将允许函数绕过集合上的权限。

  • Log Function Arguments:将其保留为 off

  • Authorization

    • Can Evaluate:将其留空。

    • Private:将其保留为 off

  • 单击 Save(保存)。随即进入 Function Editor(函数编辑器),现在,可以在其中输入一些函数来运行。

警告

此配置允许任何人调用此函数。作为一个系统函数,此函数会绕过访问规则。假设调用此函数的任何客户端都有恶意意图。

Function Editor 中,粘贴以下代码并保存:

exports = async function(email) {
const collection = context.services.get("mongodb-atlas")
.db("Item").collection("User");
const filter = {
email,
};
// Search for the user by email
const result = await collection.findOne(filter);
// Return corresponding user id or null
return result != null ? result._id : null;
};

现在,您可以从客户端调用此函数。函数的唯一参数是电子邮件地址字符串。如果该电子邮件对应于一个用户,则函数返回用户的 ID。否则,函数返回空。

注意

模板可用!

要使用后端模板并获取演示客户端,请运行以下命令:

appservices apps create --name=tiered --template=flex-sync-guides.tiered

自定义用户数据配置错误的解决办法:有时,模板生成器无法将自定义用户数据配置正确复制到新应用。可以按如下方式修复此问题:appservices apps create 命令应该输出一些有关刚刚创建的应用的 JSON。从该 JSON 中复制 "url" 值(类似于 https://services.cloud.mongodb.com/groups/...),并在浏览器中访问该 URL。如果出现相应提示,请登录。在应用仪表板的左侧面板中,单击 App Users(应用用户)。单击 Custom User Data(自定义用户数据)。确保 Enable Custom User Data(启用自定义用户数据)为 ON(开)。如果未开启,请将其打开并分别为 Cluster Name(集群名称)、Database Name(数据库名称)和 Collection Name(集合名称)输入 "mongodb-atlas"、"Item" 和 "User"。对于 User ID Field(用户 ID 字段),请输入 _id。单击 Save(保存)(或 Save Draft(保存草稿),然后进行部署)。

接下来,在终端中进入客户端目录,安装依赖项,并运行演示:

cd tiered/frontend/flex-sync-guides.tiered/
npm install
npm run demo

阅读控制台的输出结果,了解演示正在执行的操作。

在此权限策略中,我们将添加特殊角色和规则。有两种角色:团队成员团队管理员。规则如下:

  • 每个用户都是团队成员。

  • 用户可以读取和写入自己的文档。

  • 团队的所有成员都可以阅读由团队成员创建的所有文档。

  • 每个团队有一个团队管理员,其对团队文档拥有读取和写入权限。

为此,我们需要执行以下操作:

  • 启用自定义用户数据

  • 创建一个触发器函数,以为每个新用户创建一个新的自定义用户数据对象

  • 创建用于将用户添加到团队的函数

  • 定义权限

注意

使用自定义用户数据获取权限时,切勿允许客户端写入自定义用户数据对象。这样做将允许任何用户授予自己任何权限。相反,应使用服务器端的系统函数来更新自定义用户数据对象。

要在 App Services 用户界面中启用自定义用户数据,请执行以下步骤:

  1. 单击左侧面板中的 App Users(应用用户)。

  2. 选择 User Settings 标签页并找到 Custom User Data 部分。

  3. Enable Custom User Data 切换设置为 On

  4. 指定以下值:

    • Cluster Name:将包含自定义用户数据数据库的关联 MongoDB 集群的名称。

    • Database Name:将包含自定义用户数据集合的 MongoDB 数据库的名称。

    • Collection Name:将包含自定义用户数据的 MongoDB 集合的名称。

  5. 指定用户 ID 字段。自定义用户数据集合中的每个文档都必须有一个映射到特定用户的字段。该字段必须存在于映射到用户的每个文档中,并且必须包含字符串形式的用户 ID。建议您使用标准 _id 字段来存储用户 ID。MongoDB 自动对 _id 字段施加约束,以确保唯一性。

    注意

    如果此集合中的两个文档包含相同的用户 ID 值,则 App Services 将使用第一个匹配的文档,这会导致意外结果。

  6. 保存并部署更改。

    注意

    更新自定义用户数据后重启同步会话

    同步服务器会在会话期间缓存自定义用户数据。因此,更新自定义用户数据后必须重启同步会话,以避免补偿写入错误。要重启同步会话,请暂停并恢复客户端应用程序中所有打开的同步会话。有关从客户端 SDK 暂停并恢复同步的更多信息,请参阅您首选的 SDK:

我们需要创建一个身份验证触发器函数,该函数在用户首次进行身份验证时创建自定义用户对象。为此,请按照以下步骤操作:

  1. 登录到 Realm 用户界面,然后单击左侧面板中的 Triggers

  2. 单击 Add a Trigger 按钮。

  3. Trigger Type 切换设置为 Authentication

  4. 在“触发器详细信息”下,指定以下值:

    • Name:"onUserCreated"

    • Enbaled:将开关切换到 "On"(开)

    • Action Type:选择“Create”(创建)

    • Provider(s):选择“Email/Password”(电子邮件/密码),或您使用的任何身份验证提供程序

    • Select an Event Type:选择“Function”(函数)

    • Function:选择 "+New Function"(+ 新增函数),然后:

      • Function Name:"onUserCreated"

      • Function:用以下函数替换占位符文本:

      exports = function(authEvent) {
      const user = authEvent.user;
      const collection = context.services.get("mongodb-atlas").db("Item").collection("User");
      const newDoc = {
      _id: user.id,
      email: user.data.email, // Useful for looking up user IDs by email later - assuming email/password auth is used
      team: "", // Used for tiered privileges
      isTeamAdmin: false, // Used for tiered privileges
      isGlobalAdmin: false, // Used for admin privileges
      subscribedTo: [], // Used for restricted feed
      };
      return collection.insertOne(newDoc);
      };

我们现在需要一个将用户添加到团队的函数。请注意,此函数将在系统身份验证下运行并写入自定义用户数据。它不执行更新或插入,因为用户自定义数据是在用户首次成功通过身份验证时创建的。

要创建该函数,请按照以下步骤操作:

  1. 登录到 Realm 用户界面,然后单击左侧面板中的 Functions

  2. 单击 Create New Function 按钮。

  3. 指定以下值:

    • Name:"joinTeam"

    • Authentication:"System"

  4. 切换到 Function Editor(函数编辑器)标签页,并将占位符文本替换为以下代码:

    exports = async function(userId, teamName) {
    const collection = context.services.get("mongodb-atlas")
    .db("Item").collection("User");
    const filter = { _id: userId };
    const update = { $set: { team: teamName }};
    const options = { upsert: false };
    return collection.updateOne(filter, update, options);
    };

以下权限指定了两种角色:

  • teamAdmin 仅在用户的自定义数据设置有 isTeamAdmin: true 时适用。如果是这样,则用户可以读取和写入文档的 team 值与用户的 team 值匹配的所有文档。

  • teamMember 适用于每个用户。用户可以写入自己的文档,并读取特定文档,这些文档的 team 值与用户的 team 值相匹配。

[
{
"name": "admin",
"apply_when": {
"%%user.custom_data.isTeamAdmin": true
},
"document_filter": {
"read": {
"team": "%%user.custom_data.team"
},
"write": {
"team": "%%user.custom_data.team"
}
},
"read": true,
"write": true
},
{
"name": "user",
"apply_when": {},
"document_filters": {
"read": {
"team": "%%user.custom_data.team"
},
"write": {
"owner_id": "%%user.id"
}
},
"read": true,
"write": true
}
]

注意

更进一步

此策略可以扩展为支持“globalAdmin”(全局管理员)角色。全局管理员对任何团队中创建的任何文档都具有读取和写入权限。

提示

另请参阅:身份验证触发器

← 应用构建器指南