认证与 RBAC
登录时序
Signal sync_oauth_identity_and_maybe_admin 写入 OAuthIdentity,并可能授予 FIRST_ADMIN_EMAIL 对应 admin。
Membership 约束
Membership.clean():
- chair 必须有
department_id - admin 必须
department为空
Postgres 对 NULL 的 UNIQUE 行为特殊,因此另有 uq_membership_global_user_role。ensure_global_admin_membership 会合并重复全局 admin 行。
组织身份激活码
管理员在 /admin/activation-codes/ 创建 → 用户在资料页或 /accounts/activation/redeem/ 兑换一次 → 获得指定部门的 user 或 chair。加密:accounts/activation_codes.py。
Django Admin vs Portal Admin
| 路径 | 用途 |
|---|---|
/admin/ | 业务后台(用户、激活码、招新周期、审计) |
/manage/ | Django Admin;需要 is_staff |
不要把两者当成同一个「后台」。