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) + } +}