bknd-assign-permissions

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Assign Permissions

分配权限

Configure detailed permissions for roles using simple strings, extended format with effects, and conditional policies.
使用简单字符串、带效果的扩展格式和条件策略为角色配置详细权限。

Prerequisites

前提条件

  • Bknd project with code-first configuration
  • Auth enabled (
    auth: { enabled: true }
    )
  • Guard enabled (
    guard: { enabled: true }
    )
  • At least one role defined (see bknd-create-role)
  • 采用代码优先配置的Bknd项目
  • 已启用认证(
    auth: { enabled: true }
  • 已启用Guard(
    guard: { enabled: true }
  • 至少已定义一个角色(参见bknd-create-role

When to Use UI Mode

何时使用UI模式

  • Viewing current role permissions
  • Quick permission checks
UI steps: Admin Panel > Auth > Roles > Select role
Note: Permission assignment requires code mode. UI is read-only.
  • 查看当前角色权限
  • 快速检查权限
UI操作步骤: 管理面板 > 认证 > 角色 > 选择角色
注意: 权限分配需要使用代码模式,UI仅支持只读查看。

When to Use Code Mode

何时使用代码模式

  • Assigning permissions to roles
  • Adding permission effects (allow/deny)
  • Creating conditional policies
  • Entity-specific permission rules
  • 为角色分配权限
  • 添加权限效果(允许/拒绝)
  • 创建条件策略
  • 配置实体特定权限规则

Code Approach

代码实现方式

Step 1: Simple Permission Strings

步骤1:简单权限字符串

Assign basic permissions as string array:
typescript
import { serve } from "bknd/adapter/bun";
import { em, entity, text } from "bknd";

const schema = em({
  posts: entity("posts", { title: text().required() }),
});

serve({
  connection: { url: "file:data.db" },
  config: {
    data: schema.toJSON(),
    auth: {
      enabled: true,
      guard: { enabled: true },
      roles: {
        editor: {
          implicit_allow: false,
          permissions: [
            "data.entity.read",    // Read any entity
            "data.entity.create",  // Create in any entity
            "data.entity.update",  // Update any entity
            // No delete permission
          ],
        },
      },
    },
  },
});
以字符串数组形式分配基础权限:
typescript
import { serve } from "bknd/adapter/bun";
import { em, entity, text } from "bknd";

const schema = em({
  posts: entity("posts", { title: text().required() }),
});

serve({
  connection: { url: "file:data.db" },
  config: {
    data: schema.toJSON(),
    auth: {
      enabled: true,
      guard: { enabled: true },
      roles: {
        editor: {
          implicit_allow: false,
          permissions: [
            "data.entity.read",    // 读取任意实体
            "data.entity.create",  // 在任意实体中创建记录
            "data.entity.update",  // 更新任意实体记录
            // 无删除权限
          ],
        },
      },
    },
  },
});

Available Permissions

可用权限列表

PermissionFilterableDescription
data.entity.read
YesRead entity records
data.entity.create
YesCreate new records
data.entity.update
YesUpdate existing records
data.entity.delete
YesDelete records
data.database.sync
NoSync database schema
data.raw.query
NoExecute raw SELECT
data.raw.mutate
NoExecute raw INSERT/UPDATE/DELETE
Filterable means you can add conditions/filters via policies.
权限可过滤描述
data.entity.read
读取实体记录
data.entity.create
创建新记录
data.entity.update
更新现有记录
data.entity.delete
删除记录
data.database.sync
同步数据库 schema
data.raw.query
执行原生SELECT语句
data.raw.mutate
执行原生INSERT/UPDATE/DELETE语句
可过滤表示你可以通过策略添加条件/过滤规则。

Step 2: Extended Permission Format

步骤2:扩展权限格式

Use objects for explicit allow/deny effects:
typescript
{
  roles: {
    moderator: {
      implicit_allow: false,
      permissions: [
        { permission: "data.entity.read", effect: "allow" },
        { permission: "data.entity.update", effect: "allow" },
        { permission: "data.entity.delete", effect: "deny" },  // Explicit deny
      ],
    },
  },
}
使用对象形式定义明确的允许/拒绝效果:
typescript
{
  roles: {
    moderator: {
      implicit_allow: false,
      permissions: [
        { permission: "data.entity.read", effect: "allow" },
        { permission: "data.entity.update", effect: "allow" },
        { permission: "data.entity.delete", effect: "deny" },  // 明确拒绝
      ],
    },
  },
}

Permission Effects

权限效果说明

EffectDescription
allow
Grant the permission (default)
deny
Explicitly block the permission
Deny overrides allow - useful when
implicit_allow: true
but you want to block specific actions.
效果描述
allow
授予权限(默认值)
deny
明确阻止权限
拒绝会覆盖允许 - 当
implicit_allow: true
但你想阻止特定操作时非常有用。

Step 3: Conditional Policies

步骤3:条件策略

Add policies for fine-grained control:
typescript
{
  roles: {
    content_editor: {
      implicit_allow: false,
      permissions: [
        {
          permission: "data.entity.read",
          effect: "allow",
          policies: [
            {
              description: "Only read posts and comments",
              condition: { entity: { $in: ["posts", "comments"] } },
              effect: "allow",
            },
          ],
        },
        {
          permission: "data.entity.create",
          effect: "allow",
          policies: [
            {
              condition: { entity: { $in: ["posts", "comments"] } },
              effect: "allow",
            },
          ],
        },
      ],
    },
  },
}
添加策略实现细粒度控制:
typescript
{
  roles: {
    content_editor: {
      implicit_allow: false,
      permissions: [
        {
          permission: "data.entity.read",
          effect: "allow",
          policies: [
            {
              description: "仅读取文章和评论",
              condition: { entity: { $in: ["posts", "comments"] } },
              effect: "allow",
            },
          ],
        },
        {
          permission: "data.entity.create",
          effect: "allow",
          policies: [
            {
              condition: { entity: { $in: ["posts", "comments"] } },
              effect: "allow",
            },
          ],
        },
      ],
    },
  },
}

Policy Structure

策略结构

typescript
{
  description?: string,      // Human-readable (optional)
  condition?: ObjectQuery,   // When policy applies
  effect: "allow" | "deny" | "filter",
  filter?: ObjectQuery,      // Row filter (for effect: "filter")
}
typescript
{
  description?: string,      // 人类可读描述(可选)
  condition?: ObjectQuery,   // 策略适用条件
  effect: "allow" | "deny" | "filter",
  filter?: ObjectQuery,      // 行级过滤规则(当effect为"filter"时使用)
}

Policy Effects

策略效果说明

EffectPurpose
allow
Grant when condition met
deny
Block when condition met
filter
Apply row-level filter to results
效果用途
allow
当条件满足时授予权限
deny
当条件满足时阻止权限
filter
对结果应用行级过滤

Condition Operators

条件运算符

OperatorDescriptionExample
$eq
Equal
{ entity: { $eq: "posts" } }
$ne
Not equal
{ entity: { $ne: "users" } }
$in
In array
{ entity: { $in: ["posts", "comments"] } }
$nin
Not in array
{ entity: { $nin: ["users", "secrets"] } }
$gt
Greater than
{ age: { $gt: 18 } }
$gte
Greater or equal
{ level: { $gte: 5 } }
$lt
Less than
{ count: { $lt: 100 } }
$lte
Less or equal
{ priority: { $lte: 3 } }
运算符描述示例
$eq
等于
{ entity: { $eq: "posts" } }
$ne
不等于
{ entity: { $ne: "users" } }
$in
在数组中
{ entity: { $in: ["posts", "comments"] } }
$nin
不在数组中
{ entity: { $nin: ["users", "secrets"] } }
$gt
大于
{ age: { $gt: 18 } }
$gte
大于等于
{ level: { $gte: 5 } }
$lt
小于
{ count: { $lt: 100 } }
$lte
小于等于
{ priority: { $lte: 3 } }

Step 4: Variable Placeholders

步骤4:变量占位符

Reference runtime context with
@variable
:
PlaceholderDescription
@user.id
Current user's ID
@user.email
Current user's email
@user.role
Current user's role
@entity
Current entity name
@id
Current record ID
Example - user can only update their own profile:
typescript
{
  permissions: [
    {
      permission: "data.entity.update",
      effect: "allow",
      policies: [
        {
          condition: { entity: "users", "@id": "@user.id" },
          effect: "allow",
        },
      ],
    },
  ],
}
使用
@variable
引用运行时上下文:
占位符描述
@user.id
当前用户ID
@user.email
当前用户邮箱
@user.role
当前用户角色
@entity
当前实体名称
@id
当前记录ID
示例 - 用户仅能更新自己的资料:
typescript
{
  permissions: [
    {
      permission: "data.entity.update",
      effect: "allow",
      policies: [
        {
          condition: { entity: "users", "@id": "@user.id" },
          effect: "allow",
        },
      ],
    },
  ],
}

Step 5: Entity-Specific Permissions

步骤5:实体特定权限

Grant different permissions per entity:
typescript
{
  roles: {
    blog_author: {
      implicit_allow: false,
      permissions: [
        // Full CRUD on posts
        {
          permission: "data.entity.read",
          effect: "allow",
          policies: [{ condition: { entity: "posts" }, effect: "allow" }],
        },
        {
          permission: "data.entity.create",
          effect: "allow",
          policies: [{ condition: { entity: "posts" }, effect: "allow" }],
        },
        {
          permission: "data.entity.update",
          effect: "allow",
          policies: [{ condition: { entity: "posts" }, effect: "allow" }],
        },
        {
          permission: "data.entity.delete",
          effect: "allow",
          policies: [{ condition: { entity: "posts" }, effect: "allow" }],
        },

        // Read-only on categories
        {
          permission: "data.entity.read",
          effect: "allow",
          policies: [{ condition: { entity: "categories" }, effect: "allow" }],
        },
      ],
    },
  },
}
为不同实体授予不同权限:
typescript
{
  roles: {
    blog_author: {
      implicit_allow: false,
      permissions: [
        // 对文章拥有完整CRUD权限
        {
          permission: "data.entity.read",
          effect: "allow",
          policies: [{ condition: { entity: "posts" }, effect: "allow" }],
        },
        {
          permission: "data.entity.create",
          effect: "allow",
          policies: [{ condition: { entity: "posts" }, effect: "allow" }],
        },
        {
          permission: "data.entity.update",
          effect: "allow",
          policies: [{ condition: { entity: "posts" }, effect: "allow" }],
        },
        {
          permission: "data.entity.delete",
          effect: "allow",
          policies: [{ condition: { entity: "posts" }, effect: "allow" }],
        },

        // 对分类仅拥有只读权限
        {
          permission: "data.entity.read",
          effect: "allow",
          policies: [{ condition: { entity: "categories" }, effect: "allow" }],
        },
      ],
    },
  },
}

Common Patterns

常见模式

Read-Only Role

只读角色

typescript
{
  roles: {
    viewer: {
      implicit_allow: false,
      permissions: ["data.entity.read"],
    },
  },
}
typescript
{
  roles: {
    viewer: {
      implicit_allow: false,
      permissions: ["data.entity.read"],
    },
  },
}

CRUD Without Delete

拥有CRUD权限但无删除权限

typescript
{
  roles: {
    contributor: {
      implicit_allow: false,
      permissions: [
        "data.entity.read",
        "data.entity.create",
        "data.entity.update",
        { permission: "data.entity.delete", effect: "deny" },
      ],
    },
  },
}
typescript
{
  roles: {
    contributor: {
      implicit_allow: false,
      permissions: [
        "data.entity.read",
        "data.entity.create",
        "data.entity.update",
        { permission: "data.entity.delete", effect: "deny" },
      ],
    },
  },
}

Admin with Restricted Raw Access

受限的管理员角色(无原生数据库访问权限)

typescript
{
  roles: {
    admin: {
      implicit_allow: true,  // Allow all by default
      permissions: [
        // But deny raw database access
        { permission: "data.raw.query", effect: "deny" },
        { permission: "data.raw.mutate", effect: "deny" },
      ],
    },
  },
}
typescript
{
  roles: {
    admin: {
      implicit_allow: true,  // 默认允许所有操作
      permissions: [
        // 但拒绝原生数据库访问权限
        { permission: "data.raw.query", effect: "deny" },
        { permission: "data.raw.mutate", effect: "deny" },
      ],
    },
  },
}

Multi-Entity Role

多实体角色

typescript
{
  roles: {
    content_manager: {
      implicit_allow: false,
      permissions: [
        // Content entities: full CRUD
        {
          permission: "data.entity.read",
          effect: "allow",
          policies: [{
            condition: { entity: { $in: ["posts", "pages", "comments", "media"] } },
            effect: "allow",
          }],
        },
        {
          permission: "data.entity.create",
          effect: "allow",
          policies: [{
            condition: { entity: { $in: ["posts", "pages", "comments", "media"] } },
            effect: "allow",
          }],
        },
        {
          permission: "data.entity.update",
          effect: "allow",
          policies: [{
            condition: { entity: { $in: ["posts", "pages", "comments", "media"] } },
            effect: "allow",
          }],
        },
        {
          permission: "data.entity.delete",
          effect: "allow",
          policies: [{
            condition: { entity: { $in: ["posts", "pages", "comments"] } },  // No media delete
            effect: "allow",
          }],
        },
      ],
    },
  },
}
typescript
{
  roles: {
    content_manager: {
      implicit_allow: false,
      permissions: [
        // 内容实体:拥有完整CRUD权限
        {
          permission: "data.entity.read",
          effect: "allow",
          policies: [{
            condition: { entity: { $in: ["posts", "pages", "comments", "media"] } },
            effect: "allow",
          }],
        },
        {
          permission: "data.entity.create",
          effect: "allow",
          policies: [{
            condition: { entity: { $in: ["posts", "pages", "comments", "media"] } },
            effect: "allow",
          }],
        },
        {
          permission: "data.entity.update",
          effect: "allow",
          policies: [{
            condition: { entity: { $in: ["posts", "pages", "comments", "media"] } },
            effect: "allow",
          }],
        },
        {
          permission: "data.entity.delete",
          effect: "allow",
          policies: [{
            condition: { entity: { $in: ["posts", "pages", "comments"] } },  // 无媒体删除权限
            effect: "allow",
          }],
        },
      ],
    },
  },
}

Deny Specific Entity

拒绝访问特定实体

typescript
{
  roles: {
    user: {
      implicit_allow: false,
      permissions: [
        // Can read most entities
        "data.entity.read",
        // But never access secrets entity
        {
          permission: "data.entity.read",
          effect: "deny",
          policies: [{
            condition: { entity: "secrets" },
            effect: "deny",
          }],
        },
      ],
    },
  },
}
typescript
{
  roles: {
    user: {
      implicit_allow: false,
      permissions: [
        // 可读取大多数实体
        "data.entity.read",
        // 但永远无法访问secrets实体
        {
          permission: "data.entity.read",
          effect: "deny",
          policies: [{
            condition: { entity: "secrets" },
            effect: "deny",
          }],
        },
      ],
    },
  },
}

Create Helper Function

创建辅助函数

For complex role definitions:
typescript
// helpers/permissions.ts
type EntityPermission = "read" | "create" | "update" | "delete";

function entityPermissions(
  entities: string[],
  actions: EntityPermission[]
) {
  const permMap: Record<EntityPermission, string> = {
    read: "data.entity.read",
    create: "data.entity.create",
    update: "data.entity.update",
    delete: "data.entity.delete",
  };

  return actions.map((action) => ({
    permission: permMap[action],
    effect: "allow" as const,
    policies: [{
      condition: { entity: { $in: entities } },
      effect: "allow" as const,
    }],
  }));
}

// Usage
{
  roles: {
    blog_author: {
      implicit_allow: false,
      permissions: [
        ...entityPermissions(["posts", "comments"], ["read", "create", "update"]),
        ...entityPermissions(["categories", "tags"], ["read"]),
      ],
    },
  },
}
针对复杂的角色定义:
typescript
// helpers/permissions.ts
type EntityPermission = "read" | "create" | "update" | "delete";

function entityPermissions(
  entities: string[],
  actions: EntityPermission[]
) {
  const permMap: Record<EntityPermission, string> = {
    read: "data.entity.read",
    create: "data.entity.create",
    update: "data.entity.update",
    delete: "data.entity.delete",
  };

  return actions.map((action) => ({
    permission: permMap[action],
    effect: "allow" as const,
    policies: [{
      condition: { entity: { $in: entities } },
      effect: "allow" as const,
    }],
  }));
}

// 使用示例
{
  roles: {
    blog_author: {
      implicit_allow: false,
      permissions: [
        ...entityPermissions(["posts", "comments"], ["read", "create", "update"]),
        ...entityPermissions(["categories", "tags"], ["read"]),
      ],
    },
  },
}

Verification

验证方法

Test permission assignments:
1. Login as user with role:
bash
curl -X POST http://localhost:7654/api/auth/password/login \
  -H "Content-Type: application/json" \
  -d '{"email": "editor@example.com", "password": "password123"}'
2. Test allowed permission:
bash
curl http://localhost:7654/api/data/posts \
  -H "Authorization: Bearer <token>"
测试权限分配效果:
1. 使用对应角色的用户登录:
bash
curl -X POST http://localhost:7654/api/auth/password/login \
  -H "Content-Type: application/json" \
  -d '{"email": "editor@example.com", "password": "password123"}'
2. 测试允许的权限:
bash
curl http://localhost:7654/api/data/posts \
  -H "Authorization: Bearer <token>"

Should return 200 with data

应返回200状态码及数据


**3. Test denied permission:**

```bash
curl -X DELETE http://localhost:7654/api/data/posts/1 \
  -H "Authorization: Bearer <token>"

**3. 测试拒绝的权限:**

```bash
curl -X DELETE http://localhost:7654/api/data/posts/1 \
  -H "Authorization: Bearer <token>"

Should return 403 Forbidden

应返回403禁止访问


**4. Test entity-specific permission:**

```bash

**4. 测试实体特定权限:**

```bash

If only posts/comments allowed:

如果仅允许访问文章/评论:

curl http://localhost:7654/api/data/users
-H "Authorization: Bearer <token>"
curl http://localhost:7654/api/data/users
-H "Authorization: Bearer <token>"

Should return 403 if users entity not in allowed list

若用户实体不在允许列表中,应返回403

undefined
undefined

Common Pitfalls

常见问题

Permission Not Taking Effect

权限未生效

Problem: Changed permissions but old behavior persists
Fix: Restart server - role config is loaded at startup:
bash
undefined
问题: 修改权限后,旧的行为仍然存在
解决方法: 重启服务器 - 角色配置在启动时加载:
bash
undefined

Stop and restart

停止并重启

bknd run
undefined
bknd run
undefined

Deny Not Overriding

拒绝效果未生效

Problem: Deny effect not blocking access
Fix: Check policy condition - deny only applies when condition matches:
typescript
// WRONG - no condition, may not match
{ permission: "data.entity.delete", effect: "deny" }

// CORRECT - simple deny at permission level
{
  permissions: [
    "data.entity.read",
    "data.entity.create",
    // Don't include delete at all
  ],
}
问题: 拒绝效果未阻止访问
解决方法: 检查策略条件 - 仅当条件匹配时拒绝才会生效:
typescript
// 错误示例 - 无条件,可能不匹配
{ permission: "data.entity.delete", effect: "deny" }

// 正确示例 - 在权限级别直接拒绝
{
  permissions: [
    "data.entity.read",
    "data.entity.create",
    // 完全不包含delete权限
  ],
}

Entity Condition Not Matching

实体条件不匹配

Problem: Entity-specific permission not working
Fix: Verify entity name matches exactly:
typescript
// WRONG - entity name case matters
{ condition: { entity: "Posts" } }

// CORRECT - use exact entity name
{ condition: { entity: "posts" } }
问题: 实体特定权限未生效
解决方法: 验证实体名称完全匹配:
typescript
// 错误示例 - 实体名称大小写敏感
{ condition: { entity: "Posts" } }

// 正确示例 - 使用精确的实体名称
{ condition: { entity: "posts" } }

Multiple Policies Conflict

多个策略冲突

Problem: Confusing behavior with multiple policies
Fix: Understand evaluation order - first matching policy wins:
typescript
{
  policies: [
    // More specific first
    { condition: { entity: "secrets" }, effect: "deny" },
    // General fallback last
    { effect: "allow" },
  ],
}
问题: 多个策略导致行为混乱
解决方法: 理解策略评估顺序 - 第一个匹配的策略生效:
typescript
{
  policies: [
    // 更具体的策略放在前面
    { condition: { entity: "secrets" }, effect: "deny" },
    // 通用回退策略放在最后
    { effect: "allow" },
  ],
}

Variable Placeholder Not Resolving

变量占位符未解析

Problem:
@user.id
appearing literally in filter
Fix: Variables only work in
filter
and
condition
fields:
typescript
// CORRECT usage
{
  condition: { "@id": "@user.id" },  // Works
  filter: { user_id: "@user.id" },   // Works
}
问题:
@user.id
在过滤规则中以字面量形式出现
解决方法: 变量仅在
filter
condition
字段中生效:
typescript
// 正确用法
{
  condition: { "@id": "@user.id" },  // 生效
  filter: { user_id: "@user.id" },   // 生效
}

DOs and DON'Ts

注意事项

DO:
  • Start with minimal permissions, add as needed
  • Use
    $in
    operator for multiple entities
  • Test each permission after adding
  • Use descriptive policy descriptions
  • Prefer explicit permissions over
    implicit_allow
DON'T:
  • Grant
    data.raw.*
    to non-admin roles (SQL injection risk)
  • Use
    implicit_allow: true
    with deny policies (confusing)
  • Forget to restart server after config changes
  • Mix simple strings and extended format unnecessarily
  • Over-complicate with too many nested policies
建议:
  • 从最小权限开始,根据需要逐步添加
  • 对多个实体使用
    $in
    运算符
  • 添加权限后逐一测试
  • 使用描述性的策略说明
  • 优先使用明确的权限而非
    implicit_allow
禁止:
  • 为非管理员角色授予
    data.raw.*
    权限(存在SQL注入风险)
  • 在使用拒绝策略时启用
    implicit_allow: true
    (易混淆)
  • 修改配置后忘记重启服务器
  • 不必要地混合使用简单字符串和扩展格式
  • 使用过多嵌套策略导致过于复杂

Related Skills

相关技能

  • bknd-create-role - Define new roles
  • bknd-row-level-security - Filter data by user ownership
  • bknd-protect-endpoint - Secure specific endpoints
  • bknd-public-vs-auth - Configure public vs authenticated access
  • bknd-setup-auth - Initialize authentication system
  • bknd-create-role - 定义新角色
  • bknd-row-level-security - 按用户所有权过滤数据
  • bknd-protect-endpoint - 保护特定端点
  • bknd-public-vs-auth - 配置公开与认证访问
  • bknd-setup-auth - 初始化认证系统