Cron 执行身份边界(INV-CRON-10)
wesgine 定时任务子系统中 Actor 边界的设计,确保定时任务只能由所有者操作和触发。
核心问题
Actor 边界的第四个面,也是唯一一个泄漏的不是数据而是执行身份的面。
executeJob 把 entry.Actor 抄进 JobRequest,job 以所有者身份跑——所以一次不查主的 Trigger 不是"改了别人的配置",是以别人的身份跑代码。
后果:
- 写进他的记忆
- 消耗他的配额
- 在他的账上留下 Run 记录
两个单点
jobVisible(可见性)
func jobVisible(entry CronEntry, actor string) bool {
if actor == "" { return true } // admin 视图
if entry.Actor == "" { return false } // 无主仅 admin 可见
return entry.Actor == actor
}
ownedJob(by-ID 解析)
行身份是 (id, actor):
SELECT * FROM wes_cron_jobs WHERE id = ? AND actor = ?
不存在与不属于你返回同一个 wcron.ErrJobNotFound——不给 ID 探针。
Manager 方法全收 actor
wcron.Manager 的每个方法都收 actor 参数:
type Manager interface {
List(ctx, actor) ([]Entry, error)
Get(ctx, id, actor) (*Entry, error)
Add(ctx, actor, spec) (string, error)
Update(ctx, id, actor, patch) error
Remove(ctx, id, actor) error
Trigger(ctx, id, actor) error
Enable(ctx, id, actor) error
Disable(ctx, id, actor) error
}
少一个就是一条完整越权路径,而它编译通过、行为看起来"正常"。
工具侧 actor 来源
模型侧 cron_manage 工具:
| action | actor 来源 | 说明 |
|---|---|---|
add | tc.Actor | jobInput 无 Actor 字段,不可冒名 |
update | tc.Actor | 缺 actor 时只需一个 ID 即可越权 |
remove | tc.Actor | 同上 |
trigger | tc.Actor | 同上 |
HTTP 侧 /cron/* 一律 actorFromRequestOrContext(req)。
无主 Job 处理
来源
actor 列是 DEFAULT '' 后加的,INV-CRON-10 生效前写入的每一行都无主。
读侧 fail-closed,ListDue 不带 actor
那些行:
- UI 列不出
- by-ID 全
ErrJobNotFound(停不掉删不掉改不掉) - 却每到点照跑照发消息
- 派出的 Run 因 actor 为空只有 admin 看得见
"能跑、关不掉、查不到"比"看不到"坏得多。
ClaimOwnerless 认领
func (m *Manager) ClaimOwnerless(ctx context.Context, actor string) error {
// 只动 actor = '' 的行
// 不碰有主行
// 空 actor 参数直接拒
}
限制:
- 只给单 actor 部署用(桌面三端 boot 各调一次)
- 多 actor Cell 不能盲调(把某员工的任务判给另一个人)
- actor 必须是持久身份(未登录的占位名不行)
Seed 声明与运行时配额
Seed 播种机制
CellSpec.Cron 是出生声明(INV-SEED-01):
- 只在 Cell 首次 boot 播种进
wes_cron_jobs - 此后存储是唯一权威
- 声明不再被读
标记表 wes_spec_seed 与守护的行同层(state.db)。
配额判点
MaxCronJobs 判据落在唯一增长点 cron.Scheduler.Add(INV-QUOTA-05):
- 适配器不知道 cap 数值,只收
QuotaFn(current, delta) error - 覆盖已有名字
delta=0(upsert 是编辑手势)
各产品场景
| 产品 | Cron 边界影响 |
|---|---|
| wescode | actor 恒为 "local",单人无边界问题 |
| wesclaw 桌面 | N=1,boot 调 ClaimOwnerless |
| wescraft 团队 | 多 actor,Cron 边界生效 |
| 企业版 | 部门多员工,定时任务按员工隔离 |