From f7f8264ec0254ce2335e65858e6cb81893c7096b Mon Sep 17 00:00:00 2001 From: naiba Date: Sun, 31 May 2026 12:05:11 +0000 Subject: [PATCH] fix(auth): always serialize User.Role so admin (role 0) is not omitted The Role field used json:"role,omitempty". Admin is RoleAdmin = 0, so an admin profile serialized without a `role` key. The admin-frontend gates the admin menu (user management, settings) on `role === 0`, and its normalizeRole helper defaults a missing role to non-admin, so admins lost the admin menu. Drop omitempty so role 0 is always sent. Add a regression test. --- model/user.go | 2 +- model/user_role_json_test.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 model/user_role_json_test.go diff --git a/model/user.go b/model/user.go index a72b0932..73f7bf40 100644 --- a/model/user.go +++ b/model/user.go @@ -25,7 +25,7 @@ type User struct { Common Username string `json:"username,omitempty" gorm:"uniqueIndex"` Password string `json:"password,omitempty" gorm:"type:char(72)"` - Role Role `json:"role,omitempty"` + Role Role `json:"role"` AgentSecret string `json:"agent_secret,omitempty" gorm:"type:char(32)"` RejectPassword bool `json:"reject_password,omitempty"` TokenVersion uint64 `json:"-" gorm:"not null;default:0"` diff --git a/model/user_role_json_test.go b/model/user_role_json_test.go new file mode 100644 index 00000000..5ef1fcf3 --- /dev/null +++ b/model/user_role_json_test.go @@ -0,0 +1,32 @@ +package model + +import ( + "encoding/json" + "testing" +) + +// RoleAdmin is the zero value (0). The Role field must NOT use json:",omitempty" +// or an admin profile would serialize without a `role` key, and the frontend +// (which gates the admin menu on `role === 0`) would treat the admin as a +// regular user. Guard against a regression that drops the field for admins. +func TestUserRoleSerializedForAdmin(t *testing.T) { + u := User{Common: Common{ID: 1}, Username: "admin", Role: RoleAdmin} + + b, err := json.Marshal(u) + if err != nil { + t.Fatalf("marshal user: %v", err) + } + + var decoded map[string]json.RawMessage + if err := json.Unmarshal(b, &decoded); err != nil { + t.Fatalf("unmarshal user: %v", err) + } + + raw, ok := decoded["role"] + if !ok { + t.Fatalf("admin user JSON must include the `role` field, got: %s", b) + } + if string(raw) != "0" { + t.Fatalf("admin user `role` must serialize as 0, got: %s", raw) + } +}