From e36e4856c9d04b2ad0e8ad5d080809d45c7b6cd0 Mon Sep 17 00:00:00 2001 From: Hunter Kehoe Date: Thu, 22 May 2025 19:57:57 -0600 Subject: [PATCH] allow changing password or tier with user PUT --- server/server_admin.go | 18 +++++++++------ server/server_admin_test.go | 46 +++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/server/server_admin.go b/server/server_admin.go index e8e91320..7c78adb3 100644 --- a/server/server_admin.go +++ b/server/server_admin.go @@ -74,8 +74,10 @@ func (s *Server) handleUsersUpdate(w http.ResponseWriter, r *http.Request, v *vi req, err := readJSONWithLimit[apiUserAddOrUpdateRequest](r.Body, jsonBodyBytesLimit, false) if err != nil { return err - } else if !user.AllowedUsername(req.Username) || req.Password == "" { - return errHTTPBadRequest.Wrap("username invalid, or password missing") + } else if !user.AllowedUsername(req.Username) { + return errHTTPBadRequest.Wrap("username invalid") + } else if req.Password == "" && req.Tier == "" { + return errHTTPBadRequest.Wrap("need to provide at least one of \"password\" or \"tier\"") } u, err := s.userManager.User(req.Username) if err != nil && !errors.Is(err, user.ErrUserNotFound) { @@ -84,10 +86,15 @@ func (s *Server) handleUsersUpdate(w http.ResponseWriter, r *http.Request, v *vi if u.IsAdmin() { return errHTTPForbidden } - if err := s.userManager.ChangePassword(req.Username, req.Password); err != nil { + if req.Password != "" { + if err := s.userManager.ChangePassword(req.Username, req.Password); err != nil { + return err + } + } + } else { + if err := s.userManager.AddUser(req.Username, req.Password, user.RoleUser); err != nil { return err } - return s.writeJSON(w, newSuccessResponse()) } var tier *user.Tier if req.Tier != "" { @@ -98,9 +105,6 @@ func (s *Server) handleUsersUpdate(w http.ResponseWriter, r *http.Request, v *vi return err } } - if err := s.userManager.AddUser(req.Username, req.Password, user.RoleUser); err != nil { - return err - } if tier != nil { if err := s.userManager.ChangeTier(req.Username, req.Tier); err != nil { return err diff --git a/server/server_admin_test.go b/server/server_admin_test.go index 194b0a18..80da3224 100644 --- a/server/server_admin_test.go +++ b/server/server_admin_test.go @@ -57,6 +57,12 @@ func TestUser_AddRemove(t *testing.T) { require.Equal(t, "phil", users[0].Name) require.Equal(t, "emma", users[1].Name) require.Equal(t, user.Everyone, users[2].Name) + + // Reject invalid user change + rr = request(t, s, "PUT", "/v1/users", `{"username": "ben"}`, map[string]string{ + "Authorization": util.BasicAuth("phil", "phil"), + }) + require.Equal(t, 400, rr.Code) } func TestUser_ChangeUserPassword(t *testing.T) { @@ -97,6 +103,46 @@ func TestUser_ChangeUserPassword(t *testing.T) { require.Equal(t, 200, rr.Code) } +func TestUser_ChangeUserTier(t *testing.T) { + s := newTestServer(t, newTestConfigWithAuthFile(t)) + defer s.closeDatabases() + + // Create admin, tier + require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleAdmin)) + require.Nil(t, s.userManager.AddTier(&user.Tier{ + Code: "tier1", + })) + require.Nil(t, s.userManager.AddTier(&user.Tier{ + Code: "tier2", + })) + + // Create user with tier via API + rr := request(t, s, "POST", "/v1/users", `{"username": "ben", "password":"ben", "tier": "tier1"}`, map[string]string{ + "Authorization": util.BasicAuth("phil", "phil"), + }) + require.Equal(t, 200, rr.Code) + + // Check users + users, err := s.userManager.Users() + require.Nil(t, err) + require.Equal(t, 3, len(users)) + require.Equal(t, "phil", users[0].Name) + require.Equal(t, "ben", users[1].Name) + require.Equal(t, user.RoleUser, users[1].Role) + require.Equal(t, "tier1", users[1].Tier.Code) + + // Change user tier via API + rr = request(t, s, "PUT", "/v1/users", `{"username": "ben", "tier": "tier2"}`, map[string]string{ + "Authorization": util.BasicAuth("phil", "phil"), + }) + require.Equal(t, 200, rr.Code) + + // Check users again + users, err = s.userManager.Users() + require.Nil(t, err) + require.Equal(t, "tier2", users[1].Tier.Code) +} + func TestUser_DontChangeAdminPassword(t *testing.T) { s := newTestServer(t, newTestConfigWithAuthFile(t)) defer s.closeDatabases()