InfraGuard

角色、团队与负责人

角色和团队只是信息性标签 —— 唯一开放访问权限的是级别 —— 另含用户与团队合一的负责人选择器。

概览 —— 本功能的作用

角色 与 团队 只是用于给人员分类的 信息性标签。真正开放画面与操作访问权限的,只有 级别(user_level)。用户能看到什么、能保存或删除什么,100% 由 级别 决定;角色(“DBA”“DevOps”“安全”)和团队(“基础设施团队”“安全团队”)只是 显示与组织用的标签,不授予任何权限。

这样可以保持权限模型的简单。要给某人管理员权限,就 提升其级别 —— 而不是修改其角色。给用户打上“安全”标签并不会打开安全类画面;其级别必须达到员工(5)或以上。

本文涵盖三部分内容:

  1. 角色(infr_role) —— 六个职务标签(DBA、DevOps、安全、管理员、员工、开发者)。
  2. 团队(infr_team) —— 组织单位,用于用户归属与资产负责人。
  3. 负责人选择器 —— 一个合一的下拉框,可指定用户或团队,其唯一事实来源(SOT)是 App\Support\Owners。实例、硬件与存储上的“负责人”字段都使用它。

访问控制 = 仅级别(user_level)。角色和团队在程序上不开放任何权限。级别体系参见 访问级别(RBAC)。

何时使用

页面结构

/admin/users(管理 ▸ 用户与角色)分为两个标签页。

标签页 内容
用户 用户列表(登录 ID · 姓名 · 邮箱 · 角色 · 团队 · 级别 · 状态 · 最近登录)+ 弹窗 CRUD
团队 团队列表(名称 · 颜色 · 负责人 · 说明 · 成员数)+ 弹窗 CRUD

用户编辑弹窗中有级别/角色/团队选择框,其下方是这样一段提示:

级别决定访问权限 — 演示(只读)、员工(操作)、管理员(全部)。角色只是显示标签。

使用方法

角色 —— 职务标签

角色主表共六条记录,由 InfrRoleSeeder 按 key 幂等 upsert。

key 标签 颜色令牌 seq
dba DBA info 10
devops DevOps accent 20
security 安全 crit 30
admin 管理员 warn 40
staff 员工 muted 50
developer 开发者 good 60

角色 admin(标签“管理员”)与 管理员级别(10)毫无关系。角色只是标签,因此角色为“管理员”但级别为演示(1)的用户什么都保存不了。反之,级别为 10 的管理员即使角色为空,也拥有全部管理权限。

团队 —— 组织单位

团队可在 团队 标签页自由 CRUD。四个种子团队如下:

名称 颜色 说明
基础设施团队 accent 服务器/网络/存储运维
安全团队 crit 检测 · 响应 · 封禁
开发团队 good 服务开发 · 部署
DBA 团队 info 数据库运维 · 调优

团队编辑字段:

字段 含义
名称(name) 必填。团队名称
颜色(color) 徽标色调(accent/crit/good/info/muted …)
负责人(lead_user_id) 一名用户(可选)
说明(description) 自由文本
启用(is_active) 停用的团队不会出现在负责人选择器中

负责人选择器 —— 用户与团队合一

资产(服务器实例、硬件、存储)都有一个“负责人”字段,可以 是个人,也可以是团队。这个合一选择器的规则位于 App\Support\Owners(SOT)。

存储形态 = 两个列

资产表用两个列保存负责人。

列 含义
owner_id 用户或团队的整数主键
owner_type 'user' 或 'team'

前端交换形态 = 单个 ref 字符串

浏览器(表单、筛选)把 owner_type 与 owner_id 作为一个字符串来交换。

user:5     → user id 5
team:3     → team id 3

引导选项(Owners::options())返回两组数据以供 optgroup 渲染(20 秒缓存)。已关闭的用户(is_close=1)与停用的团队(is_active=0)会被排除。

{
  "users": [ { "ref": "user:5", "id": 5, "name": "Jane Doe" } ],
  "teams": [ { "ref": "team:3", "id": 3, "name": "Infra Team" } ]
}

服务端的拆分/还原

辅助方法 行为
Owners::split($ref) "user:5" → ['user', 5]。格式错误 → ['user', 0](= 未分配)
Owners::ref($ownerId, $ownerType) (5, 'user') → "user:5"。ownerId 为空 → null
Owners::resolveName($ownerId, $ownerType) 用于卡片/列表的显示名称(没有则为 null,20 秒缓存)
Owners::flush() 用户/团队变更后使缓存失效

例如实例的保存控制器是这样处理的:

// 负责人 ref("user:5"/"team:3")→ owner_id + owner_type
[$ownerType, $ownerId] = Owners::split($request->input('owner_ref'));
$payload['owner_id']   = $ownerId ?: null;
$payload['owner_type'] = $ownerId ? $ownerType : 'user';   // 未设置时默认为 'user'

按负责人 筛选 时使用同一个 ref —— 列表查询用 split() 拆分 owner 参数(user:5/team:3),再收敛为 WHERE owner_id = ? AND owner_type = ?。

负责人选择器在 实例、硬件 与 存储与文件 各资产画面上被原样复用。把团队指定为负责人,可以让整个团队站在责任线上(任何一人离开都不受影响)。

头像 · 首字母 · 颜色

标签旁显示的头像由 RolePolicy 依据姓名生成。

数据来源

表 用途
infr_role 角色主表(6 个信息性标签)—— key·name·color·seq·is_active
infr_team 团队主表 —— name·color·lead_user_id·description·is_active·seq
users 用户 —— user_level(决定访问权限)、role_id(可为空)、team_id(可为空)
资产表 负责人以 owner_id + owner_type(user/team)两个列保存(实例/硬件/存储等)

SOT 辅助类:

访问权限

保护机制(最后一名管理员保护)

情形 结果
降级/停用/删除最后一名启用的管理员 拒绝 —— “最后一名启用的管理员不能降级、停用或删除。”
降低或停用自己的管理员级别 拒绝 —— “不能降级或停用自己的管理员权限。”
删除自己 拒绝 —— “不能删除自己。”
登录 ID 已被使用 拒绝 —— “该登录 ID 已被使用。”

排查与常见问题

Q. 我给某人分配了“安全”角色,但安全类画面打不开。 角色只是标签,不开放任何权限。安全类画面(以只读为主)需要的是 级别 —— 请把该用户提升到员工(5)或以上。参见 RBAC 级别。

Q. 负责人下拉框里少了某个用户/团队。 已关闭的用户(is_close=1)与停用的团队(is_active=0)会被排除在选择器之外。请重新启用该用户/团队;若列表内容陈旧,请等待 20 秒缓存过期(Owners::flush() 可立即失效)。

Q. 删除团队后,团队里的人会消失吗? 不会。用户仍在,只是其 team_id 变为 null(未分配)。资产的 owner_type='team' 引用是另一回事,因此资产也不会被删除(只是负责人显示变为空)。

Q. 我保存了负责人,却不显示名称。 resolveName() 有 20 秒缓存。如果刚刚重命名过,稍等片刻就会刷新。另请确认 ref 格式是 user:5/team:3 —— 格式损坏时 split() 会视为未分配(user:0)。

Q. 一个用户可以有两个角色吗? 不可以。role_id 是单一值(可为空)。如果还需要组织层面的分组,请使用团队(同样是单一值)。

相关内容

사용자 · 팀 관리
사용자 · 팀 관리