修复升级后站主降级为 reader:EnsureOwner 纠正历史身份行的角色
多用户之前以 admin 会话评论会留下 provider=admin、role=reader 的历史行, 升级后 EnsureOwner 找到该行时未纠正角色,站主登录即拿 reader 角色, 用户管理 403。现在 GetOwner 优先取 owner 行,EnsureOwner 登录即把 历史行纠正成 owner。存量会话角色每请求查库,重启后重登一次即恢复。
This commit is contained in:
1 parent
1b5022aefb
commit
0fb897c0b9
2 files changed
+50
-1
No files matched your search
@@ -1897,6 +1897,15 @@ func (s *Store) EnsureOwner(handle string) (model.Reader, error) {
|
||||
}
|
||||
cur.Handle = handle
|
||||
}
|
||||
// 老库升级路径:站主在评论区的历史身份行 role 可能是 reader
|
||||
// (多用户之前用 admin 会话评论时自动建的)。登录即纠正成 owner,
|
||||
// 否则升级后站主拿到的会话是 reader 角色,用户管理直接 403。
|
||||
if cur.Role != model.RoleOwner {
|
||||
if _, uerr := s.db.Exec(s.db.Q(`UPDATE users SET role = 'owner' WHERE id = ?`), cur.ID); uerr != nil {
|
||||
return model.Reader{}, uerr
|
||||
}
|
||||
cur.Role = model.RoleOwner
|
||||
}
|
||||
return cur, nil
|
||||
}
|
||||
if !errors.Is(err, ErrNotFound) {
|
||||
@@ -1913,7 +1922,8 @@ func (s *Store) EnsureOwner(handle string) (model.Reader, error) {
|
||||
// (老库里站主行只有 provider='admin',没有 role)。
|
||||
func (s *Store) GetOwner() (model.Reader, error) {
|
||||
r, err := scanReader(s.db.QueryRow(s.db.Q(
|
||||
`SELECT ` + userCols + ` FROM users WHERE role = 'owner' OR provider = 'admin' ORDER BY id LIMIT 1`)))
|
||||
`SELECT ` + userCols + ` FROM users WHERE role = 'owner' OR provider = 'admin'
|
||||
ORDER BY CASE WHEN role = 'owner' THEN 0 ELSE 1 END, id LIMIT 1`)))
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return model.Reader{}, ErrNotFound
|
||||
}
|
||||
|
||||
@@ -490,3 +490,42 @@ func TestProjectCRUD(t *testing.T) {
|
||||
t.Errorf("after delete: got %v, want ErrNotFound", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 老库升级:多用户之前以 admin 会话评论会留下 provider=admin、role=reader
|
||||
// 的历史身份行。升级后 EnsureOwner 必须把它纠正成 owner——否则站主登录
|
||||
// 拿到的是 reader 角色,用户管理直接 403(真实事故)。
|
||||
func TestEnsureOwnerPromotesLegacyReaderRow(t *testing.T) {
|
||||
d, err := db.Open("sqlite", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer d.Close()
|
||||
st, err := New(d)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// 模拟老库:站主发过评论,留下 role=reader 的 admin 身份行
|
||||
if _, err := st.UpsertReader(model.Reader{Provider: "admin", Handle: "admin", Name: "站主"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
legacy, err := st.GetStaffByHandle("admin")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if legacy.Role != model.RoleReader {
|
||||
t.Fatalf("setup: legacy role = %q, want reader", legacy.Role)
|
||||
}
|
||||
|
||||
owner, err := st.EnsureOwner("admin")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if owner.Role != model.RoleOwner {
|
||||
t.Fatalf("after EnsureOwner: role = %q, want owner", owner.Role)
|
||||
}
|
||||
// 行上是真的改了,不是只改返回值
|
||||
again, _ := st.GetStaffByHandle("admin")
|
||||
if again.Role != model.RoleOwner {
|
||||
t.Fatalf("persisted role = %q, want owner", again.Role)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user