跳到主要内容

权限模型

概述​

Open ACE 将基于角色的访问控制(RBAC)与多租户边界强制结合在一起:

  • 6 个角色构成角色宇宙(app/models/user.py:14-22 的 UserRole):user、readonly、manager、tenant_admin、platform_admin,以及遗留的 admin
  • 19 个权限通过 4 个内置 RBAC 角色定义分配(app/services/permission_service.py:72-113 的 DEFAULT_ROLES)
  • 10 个认证装饰器(见下方清单),覆盖登录、各管理层级、机器访问和租户边界
  • 一个严格模式开关 OPENACE_PLATFORM_ADMIN_STRICT_MODE,用于收紧"谁算平台管理员"

按端点逐一查看权限要求,请参阅同目录的 API_PERMISSION_MATRIX.md(由 scripts/generate_permission_matrix.py 生成)。

角色宇宙​

权威来源是 app/models/user.py:14-30 的 UserRole / ADMIN_ROLES / PERSISTABLE_ROLES,以及 app/constants/role_groups.py 的 ROLE_GROUPS。

角色租户范围可持久化?说明
platform_admin所有租户是平台管理员;可管理所有租户(遗留 admin 是否也算平台管理员取决于严格模式,见下文)
tenant_admin仅本租户是租户管理员;必须有非空 tenant_id(数据库约束 chk_2332_tenant_admin_requires_tenant)
admin(遗留)兼容用途的系统级否 —— 不在 PERSISTABLE_ROLES 中;存量账户继续可用,但 create/update/restore 操作拒绝该值为向后兼容保留的超级管理员(Issue #2286、#2332);应迁移为 platform_admin
manager—是团队管理者:11 个权限(消息、分析、用户、配额、审计、内容过滤的查看与导出)
user—是普通用户:4 个权限(view_dashboard、view_messages、view_analysis、view_quota)
readonly—是仅仪表盘查看(view_dashboard)

ADMIN_ROLES = frozenset({"admin", "platform_admin", "tenant_admin"}) 定义了整个代码库中"是管理员"的含义(User.is_admin、所有权检查绕过均基于它)。在统计展示时,角色折叠为三组(app/constants/role_groups.py):admin(admin/platform_admin/tenant_admin)、manager、user(user/readonly)。

内置 RBAC 权限矩阵(19 个权限,来自 app/services/permission_service.py:72-113 的 DEFAULT_ROLES):

权限adminmanageruserreadonly
view_dashboard✓✓✓✓
view_messages✓✓✓
export_messages✓✓
view_analysis✓✓✓
run_analysis✓✓
export_analysis✓✓
view_users✓✓
create_user / edit_user / delete_user✓
manage_permissions✓
view_quota✓✓✓
manage_quota✓
view_audit_logs / export_audit_logs✓✓
view_content_filter✓✓
manage_content_filter✓
admin_access / system_config✓

admin_access 是超级用户绕过——持有该权限的角色自动通过所有权限检查(Role.has_permission)。

注意:就装饰器判定而言,tenant_admin / platform_admin 属于管理员层级(满足 ADMIN_ROLES);上面的 DEFAULT_ROLES 矩阵覆盖非管理员层级,以及拥有全部 19 个权限的遗留 admin 条目。

认证装饰器(10 个)​

除 machine_access_required / machine_admin_required(app/routes/remote.py:1059-1088)和 api_key_admin_required(app/auth/decorators.py:1984)外,其余定义都在 app/auth/decorators.py。使用计数为撰写本文时 app/ 下的调用点数量:

装饰器使用数一句话职责
@admin_required91接受任意管理员角色(admin/platform_admin/tenant_admin),凭 session token 或 WebUI URL token 认证;已弃用(Issue #2332)——应迁移到 any_admin_required 或 platform_admin_required
@auth_required51要求有效登录;可选 ownership='session' 或 ownership='machine' 所有权检查,管理员角色绕过
@platform_admin_required25按严格模式要求平台管理员;遗留 admin 被接受时会记录弃用告警
@same_tenant_or_platform_admin8租户范围资源访问:资源所属租户的 tenant_admin,或任意 platform_admin(租户敏感词治理端点)
@machine_access_required9远程机器访问:系统管理员或被分配到该机器的用户
@same_tenant_user_required8叠放在管理装饰器之下;将面向用户的管理端点(重置密码、更新、删除等)限制在操作者租户内,并防止租户管理员操作平台级账户
@machine_admin_required4远程机器管理:系统管理员或该机器指定的管理员
@api_key_admin_required4API Key 管理专用授权(Issue #2327):解析经授权的目标租户并设置 g.actor_scope;见下文专节
@any_admin_required4任意管理员角色,用于非跨租户操作(本租户内用户管理、租户中立配置、审计访问)
@tenant_member_required1管理员角色外加 manager,用于租户范围的告警端点(Issue #3082)

另有两个不计入认证装饰器的标记:@public_endpoint 为安全扫描器标记有意公开的路由;@security_annotated(reason=...) 标记所有权检查以行内方式(而非装饰器)实现的端点。

对于接收 tenant_id 过滤或操作非用户资源的管理端点,装饰器会与同模块的辅助函数配合:resolve_tenant_scope() / require_tenant_scope()(非管理员被限制在租户范围内,无法解析出租户时直接拒绝——按 Issue #1775 fail closed)、enforce_requested_tenant_scope()、enforce_resource_tenant_scope() 和 enforce_target_user_tenant()。它们在租户 id 缺失或不一致时一律 fail closed,而不是悄悄放大范围。

平台管理员严格模式​

OPENACE_PLATFORM_ADMIN_STRICT_MODE(app/auth/permissions.py:19-46)控制谁算平台管理员:

  • 关闭(默认):platform_admin 和遗留 admin 都能通过 is_platform_admin_role()
  • 开启:只有 platform_admin 通过;遗留 admin 账户不再是平台管理员,访问平台管理端点会得到 403

运维规则:

  1. 该值在启动时读取一次并缓存整个进程生命周期——修改后必须重启。create_app() 会调用 init_platform_admin_strict_mode(),确保模式在首个请求前冻结并记入日志
  2. 开启严格模式之前,先把所有 role='admin' 账户迁移为 platform_admin;warn_if_strict_mode_locks_out_legacy_admins() 会在启动时大声警告严格模式将锁死遗留账户
  3. 该开关只影响经由 is_platform_admin_role() / User.is_platform_admin() 的检查(platform_admin_required、same_tenant_or_platform_admin、resolve_admin_tenant_scope)。直接比较角色字符串的代码(如 app/routes/mapping_rules.py)不受影响——开启开关收窄授权,但不会让遗留角色消失

迁移手册见 docs/dev-notes/2332-admin-role-migration-runbook.md。

认证​

登录流程​

  1. 客户端发送 POST /api/auth/login,包含 {username, password}
  2. 服务端验证凭证 —— bcrypt,12 轮(app/routes/auth.py:72 的 hash_password(),admin.py / tenant.py 相同)
  3. 创建带过期时间的会话记录
  4. 设置 session_token Cookie(HttpOnly,HTTPS 时为 Secure)
  5. 返回含角色和 tenant_id 的用户资料

速率限制​

失败的登录尝试记录在 login_attempts 表中(锁定阈值来自安全设置,默认最多 5 次)。安全设置缓存 60 秒。

Token 提取​

Session token 只从安全来源提取 —— cookie → Authorization: Bearer 头。按 Issue #1896,查询参数中的 session token 会被拒绝。少量路径前缀白名单(/api/admin/、/api/remote/terminal/、/api/remote/vscode/、/api/workspace/、/api/projects)接受特殊的 URL token(WebUI v1/v2、proxy、browser token),按格式校验并始终写审计日志。

强制改密​

用户被标记 must_change_password=true 时,所有装饰器都会拦截请求,仅放行最小必要端点:认证检查、个人资料、修改密码、登出和密码策略。

路由保护​

  • 管理模式(/manage/*)—— 所有管理页面要求管理员角色;普通用户和机器管理员无法访问
  • API 路由 —— 大多数 /api/* blueprint 通过装饰器或 before_request 认证;敏感操作叠加上述管理层级装饰器
  • 公开路由:
    • / —— SPA 全局捕获(提供 index.html)
    • /api/auth/login —— 登录端点
    • /api/auth/check —— 认证状态检查
    • /livez、/readyz、/metrics、/security-status —— 探针与指标(/health 已废弃,委托给 /readyz)

自定义权限​

在内置角色之外,可以按用户授予自定义权限:

PermissionService.grant_permission(user_id, 'export_analysis', granted_by=admin_id)
has_perm = PermissionService.has_permission(user_id, 'export_analysis')

自定义权限存储在 user_permissions 表中,与用户的角色权限合并计算。

多租户隔离​

启用多租户模式时:

  • 用户通过 tenant_id 关联到租户
  • 租户配额强制执行每个租户的 token 和请求限制;QuotaEnforcementScheduler 每 60 秒运行一次,终止超额会话并生成告警
  • 租户感知边界覆盖用户、项目、工作区会话、会话消息、每日用量聚合、审计日志、远程机器、机器权限和配额
  • 非管理员用户可见 API 会把认证租户范围应用到会话、项目、用量和审计查询
  • 平台管理员有意保留全局运维可见性,用于支持和故障处理;每次跨租户访问都写审计日志(ADMIN_CROSS_TENANT_ACCESS)

API Key 管理租户授权(Issue #2327)​

API Key 管理接口的 tenant_id 参数是目标选择器,不是授权凭据。路由层守卫是 @api_key_admin_required,它调用 resolve_authorized_target_tenant(actor, requested_tenant_id):

  • tenant_admin:目标租户始终来自认证上下文(g.tenant_id);不提供 tenant_id 时使用 actor 租户(向后兼容),指定其他租户返回 403
  • platform_admin / 遗留 admin:必须显式提供 tenant_id;缺失时返回 400(fail closed,不返回全局列表),跨租户操作被允许但写审计日志
  • PUT/DELETE /api/api-keys/<key_id> 的所有权校验对目标租户之外的 key 返回 403(绝不返回 404),避免探测 key id

Service/Repository 层接收不可变的 ActorScope(由 @require_actor_scope() 校验),因此直接调用更深层也无法绕过授权。一切 fail closed:不回退默认租户、不静默忽略无效 tenant_id、不返回全局列表。

密钥存储、轮换与代理语义见 KEY_MANAGEMENT.md;完整的按端点权限表见 API_PERMISSION_MATRIX.md。