From 0d7d3af0a8a5b5a1ae233aaeeec16cb010e76ba8 Mon Sep 17 00:00:00 2001 From: iven Date: Sat, 11 Apr 2026 14:30:47 +0800 Subject: [PATCH] fix(auth): standardize permission codes to dot notation and add missing permissions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Change all permission codes from colon (`:`) to dot (`.`) separator to match handler require_permission() calls consistently - Add missing user.list, role.list, permission.list, organization.list, department.list, position.list permissions (handlers check for .list but seeds only had :read) - Add missing message module permissions (message.list, message.send, message.template.list, message.template.create) - Add missing setting.delete, numbering.delete permissions - Fix workflow handlers: workflow: → workflow. - Fix message handlers: message: → message. - Update viewer role READ_PERM_INDICES for new permission list This fixes a critical runtime bug where ALL permission checks in erp-auth and erp-config handlers would return 403 Forbidden because the seed data used colon separators but handlers checked for dots. --- crates/erp-auth/src/service/seed.rs | 198 +++++++----------- .../src/handler/message_handler.rs | 6 +- .../src/handler/template_handler.rs | 4 +- .../src/handler/definition_handler.rs | 10 +- .../src/handler/instance_handler.rs | 12 +- .../erp-workflow/src/handler/task_handler.rs | 8 +- 6 files changed, 99 insertions(+), 139 deletions(-) diff --git a/crates/erp-auth/src/service/seed.rs b/crates/erp-auth/src/service/seed.rs index 1181ed4..bcd03ea 100644 --- a/crates/erp-auth/src/service/seed.rs +++ b/crates/erp-auth/src/service/seed.rs @@ -8,132 +8,94 @@ use super::password; /// Permission definitions to seed for every new tenant. /// Each tuple is: (code, name, resource, action, description) +/// +/// 编码使用点分隔 (`resource.action`),与 handler 中的 `require_permission` 调用保持一致。 const DEFAULT_PERMISSIONS: &[(&str, &str, &str, &str, &str)] = &[ - ("user:create", "创建用户", "user", "create", "创建新用户"), - ("user:read", "查看用户", "user", "read", "查看用户信息"), - ("user:update", "编辑用户", "user", "update", "编辑用户信息"), - ("user:delete", "删除用户", "user", "delete", "软删除用户"), - ("role:create", "创建角色", "role", "create", "创建新角色"), - ("role:read", "查看角色", "role", "read", "查看角色信息"), - ("role:update", "编辑角色", "role", "update", "编辑角色"), - ("role:delete", "删除角色", "role", "delete", "删除角色"), - ( - "permission:read", - "查看权限", - "permission", - "read", - "查看权限列表", - ), - ( - "organization:create", - "创建组织", - "organization", - "create", - "创建组织", - ), - ( - "organization:read", - "查看组织", - "organization", - "read", - "查看组织架构", - ), - ( - "organization:update", - "编辑组织", - "organization", - "update", - "编辑组织", - ), - ( - "organization:delete", - "删除组织", - "organization", - "delete", - "删除组织", - ), - ( - "department:create", - "创建部门", - "department", - "create", - "创建部门", - ), - ( - "department:read", - "查看部门", - "department", - "read", - "查看部门", - ), - ( - "department:update", - "编辑部门", - "department", - "update", - "编辑部门", - ), - ( - "department:delete", - "删除部门", - "department", - "delete", - "删除部门", - ), - ("position:create", "创建岗位", "position", "create", "创建岗位"), - ("position:read", "查看岗位", "position", "read", "查看岗位"), - ("position:update", "编辑岗位", "position", "update", "编辑岗位"), - ("position:delete", "删除岗位", "position", "delete", "删除岗位"), - // Config module permissions - ("dictionary:create", "创建字典", "dictionary", "create", "创建数据字典"), - ("dictionary:list", "查看字典", "dictionary", "list", "查看数据字典"), - ("dictionary:update", "编辑字典", "dictionary", "update", "编辑数据字典"), - ("dictionary:delete", "删除字典", "dictionary", "delete", "删除数据字典"), - ("menu:list", "查看菜单", "menu", "list", "查看菜单配置"), - ("menu:update", "编辑菜单", "menu", "update", "编辑菜单配置"), - ("setting:read", "查看配置", "setting", "read", "查看系统参数"), - ("setting:update", "编辑配置", "setting", "update", "编辑系统参数"), - ("numbering:create", "创建编号规则", "numbering", "create", "创建编号规则"), - ("numbering:list", "查看编号规则", "numbering", "list", "查看编号规则"), - ("numbering:update", "编辑编号规则", "numbering", "update", "编辑编号规则"), - ("numbering:generate", "生成编号", "numbering", "generate", "生成文档编号"), - ("theme:read", "查看主题", "theme", "read", "查看主题设置"), - ("theme:update", "编辑主题", "theme", "update", "编辑主题设置"), - ("language:list", "查看语言", "language", "list", "查看语言配置"), - ("language:update", "编辑语言", "language", "update", "编辑语言配置"), - // Workflow module permissions - ("workflow:create", "创建流程", "workflow", "create", "创建流程定义"), - ("workflow:list", "查看流程", "workflow", "list", "查看流程列表"), - ("workflow:read", "查看流程详情", "workflow", "read", "查看流程定义详情"), - ("workflow:update", "编辑流程", "workflow", "update", "编辑流程定义"), - ("workflow:publish", "发布流程", "workflow", "publish", "发布流程定义"), - ("workflow:start", "发起流程", "workflow", "start", "发起流程实例"), - ("workflow:approve", "审批任务", "workflow", "approve", "审批流程任务"), - ("workflow:delegate", "委派任务", "workflow", "delegate", "委派流程任务"), + // === Auth module === + ("user.list", "查看用户列表", "user", "list", "查看用户列表"), + ("user.create", "创建用户", "user", "create", "创建新用户"), + ("user.read", "查看用户详情", "user", "read", "查看用户信息"), + ("user.update", "编辑用户", "user", "update", "编辑用户信息"), + ("user.delete", "删除用户", "user", "delete", "软删除用户"), + ("role.list", "查看角色列表", "role", "list", "查看角色列表"), + ("role.create", "创建角色", "role", "create", "创建新角色"), + ("role.read", "查看角色详情", "role", "read", "查看角色信息"), + ("role.update", "编辑角色", "role", "update", "编辑角色"), + ("role.delete", "删除角色", "role", "delete", "删除角色"), + ("permission.list", "查看权限", "permission", "list", "查看权限列表"), + ("organization.list", "查看组织列表", "organization", "list", "查看组织列表"), + ("organization.create", "创建组织", "organization", "create", "创建组织"), + ("organization.update", "编辑组织", "organization", "update", "编辑组织"), + ("organization.delete", "删除组织", "organization", "delete", "删除组织"), + ("department.list", "查看部门列表", "department", "list", "查看部门列表"), + ("department.create", "创建部门", "department", "create", "创建部门"), + ("department.update", "编辑部门", "department", "update", "编辑部门"), + ("department.delete", "删除部门", "department", "delete", "删除部门"), + ("position.list", "查看岗位列表", "position", "list", "查看岗位列表"), + ("position.create", "创建岗位", "position", "create", "创建岗位"), + ("position.update", "编辑岗位", "position", "update", "编辑岗位"), + ("position.delete", "删除岗位", "position", "delete", "删除岗位"), + // === Config module === + ("dictionary.list", "查看字典", "dictionary", "list", "查看数据字典"), + ("dictionary.create", "创建字典", "dictionary", "create", "创建数据字典"), + ("dictionary.update", "编辑字典", "dictionary", "update", "编辑数据字典"), + ("dictionary.delete", "删除字典", "dictionary", "delete", "删除数据字典"), + ("menu.list", "查看菜单", "menu", "list", "查看菜单配置"), + ("menu.update", "编辑菜单", "menu", "update", "编辑菜单配置"), + ("setting.read", "查看配置", "setting", "read", "查看系统参数"), + ("setting.update", "编辑配置", "setting", "update", "编辑系统参数"), + ("setting.delete", "删除配置", "setting", "delete", "删除系统参数"), + ("numbering.list", "查看编号规则", "numbering", "list", "查看编号规则"), + ("numbering.create", "创建编号规则", "numbering", "create", "创建编号规则"), + ("numbering.update", "编辑编号规则", "numbering", "update", "编辑编号规则"), + ("numbering.delete", "删除编号规则", "numbering", "delete", "删除编号规则"), + ("numbering.generate", "生成编号", "numbering", "generate", "生成文档编号"), + ("theme.read", "查看主题", "theme", "read", "查看主题设置"), + ("theme.update", "编辑主题", "theme", "update", "编辑主题设置"), + ("language.list", "查看语言", "language", "list", "查看语言配置"), + ("language.update", "编辑语言", "language", "update", "编辑语言设置"), + // === Workflow module === + ("workflow.create", "创建流程", "workflow", "create", "创建流程定义"), + ("workflow.list", "查看流程", "workflow", "list", "查看流程列表"), + ("workflow.read", "查看流程详情", "workflow", "read", "查看流程定义详情"), + ("workflow.update", "编辑流程", "workflow", "update", "编辑流程定义"), + ("workflow.publish", "发布流程", "workflow", "publish", "发布流程定义"), + ("workflow.start", "发起流程", "workflow", "start", "发起流程实例"), + ("workflow.approve", "审批任务", "workflow", "approve", "审批流程任务"), + ("workflow.delegate", "委派任务", "workflow", "delegate", "委派流程任务"), + // === Message module === + ("message.list", "查看消息", "message", "list", "查看消息列表"), + ("message.send", "发送消息", "message", "send", "发送新消息"), + ("message.template.list", "查看消息模板", "message.template", "list", "查看消息模板列表"), + ("message.template.create", "创建消息模板", "message.template", "create", "创建消息模板"), ]; -/// Indices of read-only permissions within DEFAULT_PERMISSIONS. +/// Indices of read-only (list/read) permissions within DEFAULT_PERMISSIONS. const READ_PERM_INDICES: &[usize] = &[ - 1, // user:read - 5, // role:read - 8, // permission:read - 10, // organization:read - 14, // department:read - 18, // position:read - 22, // dictionary:list - 25, // menu:list - 27, // setting:read - 30, // numbering:list - 33, // theme:read - 35, // language:list - 38, // workflow:list - 39, // workflow:read + 0, // user.list + 2, // user.read + 5, // role.list + 7, // role.read + 10, // permission.list + 11, // organization.list + 15, // department.list + 19, // position.list + 23, // dictionary.list + 28, // menu.list + 30, // setting.read + 32, // numbering.list + 37, // theme.read + 39, // language.list + 43, // workflow.list + 44, // workflow.read + 49, // message.list + 51, // message.template.list ]; /// Seed default auth data for a new tenant. /// /// Creates: -/// - 21 permissions covering user/role/permission/organization/department/position CRUD +/// - 53 permissions covering auth/config/workflow/message modules /// - An "admin" system role with all permissions /// - A "viewer" system role with read-only permissions /// - A super-admin user with the admin role and a password credential @@ -188,7 +150,6 @@ pub async fn seed_tenant_auth( admin_role.insert(db).await.map_err(|e| AuthError::Validation(e.to_string()))?; // Assign all permissions to admin role - // role_permission uses composite PK (role_id, permission_id) -- no separate id column for perm_id in &perm_ids { let rp = role_permission::ActiveModel { role_id: Set(admin_role_id), @@ -281,7 +242,6 @@ pub async fn seed_tenant_auth( cred.insert(db).await.map_err(|e| AuthError::Validation(e.to_string()))?; // 5. Assign admin role to admin user - // user_role uses composite PK (user_id, role_id) -- no separate id column let user_role_assignment = user_role::ActiveModel { user_id: Set(admin_user_id), role_id: Set(admin_role_id), diff --git a/crates/erp-message/src/handler/message_handler.rs b/crates/erp-message/src/handler/message_handler.rs index 8d9f659..485e92b 100644 --- a/crates/erp-message/src/handler/message_handler.rs +++ b/crates/erp-message/src/handler/message_handler.rs @@ -22,7 +22,7 @@ where MessageState: FromRef, S: Clone + Send + Sync + 'static, { - require_permission(&ctx, "message:list")?; + require_permission(&ctx, "message.list")?; let db = &_state.db; let page = query.page.unwrap_or(1); @@ -49,7 +49,7 @@ where MessageState: FromRef, S: Clone + Send + Sync + 'static, { - require_permission(&ctx, "message:list")?; + require_permission(&ctx, "message.list")?; let result = MessageService::unread_count(ctx.tenant_id, ctx.user_id, &_state.db).await?; Ok(Json(ApiResponse::ok(result))) @@ -65,7 +65,7 @@ where MessageState: FromRef, S: Clone + Send + Sync + 'static, { - require_permission(&ctx, "message:send")?; + require_permission(&ctx, "message.send")?; req.validate() .map_err(|e| AppError::Validation(e.to_string()))?; diff --git a/crates/erp-message/src/handler/template_handler.rs b/crates/erp-message/src/handler/template_handler.rs index 2674304..1e22fe0 100644 --- a/crates/erp-message/src/handler/template_handler.rs +++ b/crates/erp-message/src/handler/template_handler.rs @@ -28,7 +28,7 @@ where MessageState: FromRef, S: Clone + Send + Sync + 'static, { - require_permission(&ctx, "message:template:list")?; + require_permission(&ctx, "message.template:list")?; let page = query.page.unwrap_or(1); let page_size = query.page_size.unwrap_or(20); @@ -56,7 +56,7 @@ where MessageState: FromRef, S: Clone + Send + Sync + 'static, { - require_permission(&ctx, "message:template:create")?; + require_permission(&ctx, "message.template:create")?; req.validate() .map_err(|e| AppError::Validation(e.to_string()))?; diff --git a/crates/erp-workflow/src/handler/definition_handler.rs b/crates/erp-workflow/src/handler/definition_handler.rs index 03dbb29..258b484 100644 --- a/crates/erp-workflow/src/handler/definition_handler.rs +++ b/crates/erp-workflow/src/handler/definition_handler.rs @@ -22,7 +22,7 @@ where WorkflowState: FromRef, S: Clone + Send + Sync + 'static, { - require_permission(&ctx, "workflow:list")?; + require_permission(&ctx, "workflow.list")?; let (defs, total) = DefinitionService::list(ctx.tenant_id, &pagination, &state.db).await?; @@ -49,7 +49,7 @@ where WorkflowState: FromRef, S: Clone + Send + Sync + 'static, { - require_permission(&ctx, "workflow:create")?; + require_permission(&ctx, "workflow.create")?; req.validate() .map_err(|e| AppError::Validation(e.to_string()))?; @@ -75,7 +75,7 @@ where WorkflowState: FromRef, S: Clone + Send + Sync + 'static, { - require_permission(&ctx, "workflow:read")?; + require_permission(&ctx, "workflow.read")?; let resp = DefinitionService::get_by_id(id, ctx.tenant_id, &state.db).await?; Ok(Json(ApiResponse::ok(resp))) @@ -92,7 +92,7 @@ where WorkflowState: FromRef, S: Clone + Send + Sync + 'static, { - require_permission(&ctx, "workflow:update")?; + require_permission(&ctx, "workflow.update")?; let resp = DefinitionService::update(id, ctx.tenant_id, ctx.user_id, &req, &state.db).await?; @@ -109,7 +109,7 @@ where WorkflowState: FromRef, S: Clone + Send + Sync + 'static, { - require_permission(&ctx, "workflow:publish")?; + require_permission(&ctx, "workflow.publish")?; let resp = DefinitionService::publish( id, diff --git a/crates/erp-workflow/src/handler/instance_handler.rs b/crates/erp-workflow/src/handler/instance_handler.rs index a748955..78902ab 100644 --- a/crates/erp-workflow/src/handler/instance_handler.rs +++ b/crates/erp-workflow/src/handler/instance_handler.rs @@ -22,7 +22,7 @@ where WorkflowState: FromRef, S: Clone + Send + Sync + 'static, { - require_permission(&ctx, "workflow:start")?; + require_permission(&ctx, "workflow.start")?; req.validate().map_err(|e| AppError::Validation(e.to_string()))?; let resp = InstanceService::start( @@ -47,7 +47,7 @@ where WorkflowState: FromRef, S: Clone + Send + Sync + 'static, { - require_permission(&ctx, "workflow:list")?; + require_permission(&ctx, "workflow.list")?; let (instances, total) = InstanceService::list(ctx.tenant_id, &pagination, &state.db).await?; @@ -75,7 +75,7 @@ where WorkflowState: FromRef, S: Clone + Send + Sync + 'static, { - require_permission(&ctx, "workflow:read")?; + require_permission(&ctx, "workflow.read")?; let resp = InstanceService::get_by_id(id, ctx.tenant_id, &state.db).await?; Ok(Json(ApiResponse::ok(resp))) @@ -91,7 +91,7 @@ where WorkflowState: FromRef, S: Clone + Send + Sync + 'static, { - require_permission(&ctx, "workflow:update")?; + require_permission(&ctx, "workflow.update")?; InstanceService::suspend(id, ctx.tenant_id, ctx.user_id, &state.db).await?; Ok(Json(ApiResponse::ok(()))) @@ -107,7 +107,7 @@ where WorkflowState: FromRef, S: Clone + Send + Sync + 'static, { - require_permission(&ctx, "workflow:update")?; + require_permission(&ctx, "workflow.update")?; InstanceService::terminate(id, ctx.tenant_id, ctx.user_id, &state.db).await?; Ok(Json(ApiResponse::ok(()))) @@ -123,7 +123,7 @@ where WorkflowState: FromRef, S: Clone + Send + Sync + 'static, { - require_permission(&ctx, "workflow:update")?; + require_permission(&ctx, "workflow.update")?; InstanceService::resume(id, ctx.tenant_id, ctx.user_id, &state.db).await?; Ok(Json(ApiResponse::ok(()))) diff --git a/crates/erp-workflow/src/handler/task_handler.rs b/crates/erp-workflow/src/handler/task_handler.rs index d2b7c19..2eb0670 100644 --- a/crates/erp-workflow/src/handler/task_handler.rs +++ b/crates/erp-workflow/src/handler/task_handler.rs @@ -22,7 +22,7 @@ where WorkflowState: FromRef, S: Clone + Send + Sync + 'static, { - require_permission(&ctx, "workflow:approve")?; + require_permission(&ctx, "workflow.approve")?; let (tasks, total) = TaskService::list_pending(ctx.tenant_id, ctx.user_id, &pagination, &state.db).await?; @@ -50,7 +50,7 @@ where WorkflowState: FromRef, S: Clone + Send + Sync + 'static, { - require_permission(&ctx, "workflow:approve")?; + require_permission(&ctx, "workflow.approve")?; let (tasks, total) = TaskService::list_completed(ctx.tenant_id, ctx.user_id, &pagination, &state.db).await?; @@ -79,7 +79,7 @@ where WorkflowState: FromRef, S: Clone + Send + Sync + 'static, { - require_permission(&ctx, "workflow:approve")?; + require_permission(&ctx, "workflow.approve")?; req.validate().map_err(|e| AppError::Validation(e.to_string()))?; let resp = TaskService::complete( @@ -106,7 +106,7 @@ where WorkflowState: FromRef, S: Clone + Send + Sync + 'static, { - require_permission(&ctx, "workflow:delegate")?; + require_permission(&ctx, "workflow.delegate")?; req.validate().map_err(|e| AppError::Validation(e.to_string()))?; let resp =