如何为基于角色的访问控制(RBAC)创建自定义条件

页面摘要: 在 Strapi 中使用 conditionProvider API 创建自定义的基于角色的访问控制(RBAC)条件,以基于用户属性、实体查询或外部条件限制管理面板的访问。

基于角色的访问控制(RBAC)是一种将访问限制为部分用户的方法。在 Strapi 应用中,管理面板的用户是管理员。他们的角色和权限在管理面板中配置。

声明新条件

将单个条件声明为一个对象,将多个条件声明为一个对象数组。每个条件对象可以有 5 个可能的属性:

  • displayName(字符串):在管理面板中显示的条件名称,
  • name(字符串):条件名称,kebab-case 形式 ,
  • category(字符串,可选):条件可以分组到管理面板中可用的类别;如果未定义,该条件将出现在 "Default" 类别下,
  • plugin(字符串,可选):如果条件由插件创建,应为插件的名称,kebab-case 形式(例如 content-manager),
  • handler:用于验证条件的函数(参见使用条件处理器)

在位于 /src/index.js 的全局 bootstrap 函数 中声明并注册条件(参见注册条件)。

NOTE

条件 name 属性在其命名空间内充当唯一 id,命名空间要么是插件(如果定义了 plugin 属性),要么是根命名空间。

使用条件处理器

条件可以应用于任何权限,条件 handler 用于验证该条件。handler 是一个返回查询对象或布尔值的函数。

查询对象对于验证你读取、创建、更新、删除或发布的实体上的条件很有用。它们使用 sift.js 库,但仅使用以下受支持的运算符:

  • $or
  • $and
  • $eq
  • $ne
  • $in
  • $nin
  • $lt
  • $lte
  • $gt
  • $gte
  • $exists
  • $elemMatch
WARNING

此列表是穷尽的。条件查询对象在管理员创建、更新、删除或发布实体时通过 sift.js 在内存中进行评估,并且仅注册了上面列出的运算符。其他运算符,包括 REST API 和 Query Engine API 中可用的过滤运算符,例如 $eqi、$contains、$startsWith 或 $null,在此不受支持,并且在评估条件时会抛出 Unsupported operation 错误。

条件 handler 可以是同步或异步函数,它:

  • 接收发出请求已通过身份验证的用户,
  • 并返回 true、false 或查询对象。

返回 true 或 false 对于验证外部条件或已身份验证用户上的条件很有用。 例如,一个仅在服务器时间为下午 5 点时允许访问管理面板页面的条件可以使用此处理器:

handler: () => new Date().getHours() === 17;

handler 函数接收已身份验证的用户,因此它可以验证用户上的条件:

const condition = {
  displayName: 'Email address from strapi.io',
  name: 'email-strapi-dot-io',
  async handler(user) {
    return user.email.includes('@strapi.io');
  },
};

为了更精细的控制,handler 函数也可以返回查询对象:

const condition = {
  displayName: 'price greater than 50',
  name: 'price-gt-50',
  async handler(user) {
    return { price: { $gt: 50 } };
  },
};

注册条件

要在管理面板中可用,条件应在位于 /src/index 的全局 bootstrap 函数 中声明并注册。使用 conditionProvider.register() 方法注册单个条件:

JavaScript


module.exports = async () => {
  await strapi.admin.services.permission.conditionProvider.register({
    displayName: 'Billing amount under 10K',
    name: 'billing-amount-under-10k',
    plugin: 'admin',
    handler: { amount: { $lt: 10000 } },
  });
};

TypeScript


export default async () => {
  await strapi.admin.services.permission.conditionProvider.register({
    displayName: 'Billing amount under 10K',
    name: 'billing-amount-under-10k',
    plugin: 'admin',
    handler: { amount: { $lt: 10000 } },
  });
};

要注册定义为 条件对象 数组的多个条件,请使用 conditionProvider.registerMany():

JavaScript


const conditions = [
  {
    displayName: "Entity has same name as user",
    name: "same-name-as-user",
    plugin: "name of a plugin if created in a plugin",
    handler: (user) => {
      return { name: user.name };
    },
  },
  {
    displayName: "Email address from strapi.io",
    name: "email-strapi-dot-io",
    async handler(user) {
      return user.email.includes('@strapi.io');
    },
  }
];

module.exports = {
  async bootstrap(/*{ strapi }*/) {
  // do your boostrap

    await strapi.admin.services.permission.conditionProvider.registerMany(conditions);
  },
};

TypeScript


const conditions = [
  {
    displayName: "Entity has same name as user",
    name: "same-name-as-user",
    plugin: "name of a plugin if created in a plugin"
    handler: (user) => {
      return { name: user.name };
    },
  },
  {
    displayName: "Email address from strapi.io",
    name: "email-strapi-dot-io",
    async handler(user) {
      return user.email.includes('@strapi.io');
    },
  }
];

export default async () => {
  // do your boostrap

  await strapi.admin.services.permission.conditionProvider.registerMany(conditions);
};