Files
go_blog/handlers
dsh f39f0f4320 fix: SECURITY_TODO #30 最后管理员防线事务化,消除 TOCTOU 竞态
UserUpdate/UserDelete 的 adminCount<=1 计数与 Save/Delete 非原子,两个
并发的'降级/删除倒数第二位管理员'请求可同时通过检查,站点失去管理员
(MySQL 下真实窗口;SQLite 单写锁下窗口极小)。

- handlers/admin_user.go: 检查+写入包进 db.Transaction;
  ensureNotLastAdmin 在事务内锁定管理员集合后再计数:
  MySQL 用 SELECT ... FOR UPDATE(gorm clause.Locking)串行化
  并发事务;SQLite 无 FOR UPDATE,叠加进程内互斥锁 lastAdminMu
  (应用按设计单实例部署),计数在写锁串行化后重读
- 顺带修正:原实现中 f.Role 覆盖 user.Role 后检查恒为 true 的语义
  保留(基于原角色判定),避免降级检查被新值掩盖
- 测试: TestConcurrentLastAdminDowngrade(两位管理员并发降级:
  恰好 1 成功 1 拒绝、最终至少保留一位管理员;-race 通过)
2026-08-27 21:45:03 +08:00
..
2026-08-27 19:03:03 +08:00
2026-08-27 19:03:03 +08:00
2026-08-27 19:03:03 +08:00