diff --git a/docs/releases.md b/docs/releases.md index a76ec6a3..959fbb83 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -1381,6 +1381,7 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release * Write VAPID keys to file in `ntfy webpush --output-file` ([#1138](https://github.com/binwiederhier/ntfy/pull/1138), thanks to [@nogweii](https://github.com/nogweii)) * Add Docker major/minor version to image tags ([#1271](https://github.com/binwiederhier/ntfy/pull/1271), thanks to [@RoboMagus](https://github.com/RoboMagus)) * Add `latest` subscription param for grabbing just the most recent message ([#1216](https://github.com/binwiederhier/ntfy/pull/1216), thanks to [@wunter8](https://github.com/wunter8)) +* You can now change passwords via the accounts API (thanks to [@wunter8](https://github.com/wunter8) for implementing) **Bug fixes + maintenance:** diff --git a/server/server_admin.go b/server/server_admin.go index ac295718..0e7e311e 100644 --- a/server/server_admin.go +++ b/server/server_admin.go @@ -49,6 +49,12 @@ func (s *Server) handleUsersAdd(w http.ResponseWriter, r *http.Request, v *visit if err != nil && !errors.Is(err, user.ErrUserNotFound) { return err } else if u != nil { + if req.Force == true { + if err := s.userManager.ChangePassword(req.Username, req.Password); err != nil { + return err + } + return s.writeJSON(w, newSuccessResponse()) + } return errHTTPConflictUserExists } var tier *user.Tier diff --git a/server/server_admin_test.go b/server/server_admin_test.go index c2f8f95a..70574efe 100644 --- a/server/server_admin_test.go +++ b/server/server_admin_test.go @@ -49,6 +49,52 @@ func TestUser_AddRemove(t *testing.T) { "Authorization": util.BasicAuth("phil", "phil"), }) require.Equal(t, 200, rr.Code) + + // Check user was deleted + 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, "emma", users[1].Name) + require.Equal(t, user.Everyone, users[2].Name) +} + +func TestUser_ChangePassword(t *testing.T) { + s := newTestServer(t, newTestConfigWithAuthFile(t)) + defer s.closeDatabases() + + // Create admin + require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleAdmin)) + + // Create user via API + rr := request(t, s, "PUT", "/v1/users", `{"username": "ben", "password": "ben"}`, map[string]string{ + "Authorization": util.BasicAuth("phil", "phil"), + }) + require.Equal(t, 200, rr.Code) + + // Try to login with first password + rr = request(t, s, "POST", "/v1/account/token", "", map[string]string{ + "Authorization": util.BasicAuth("ben", "ben"), + }) + require.Equal(t, 200, rr.Code) + + // Change password via API + rr = request(t, s, "PUT", "/v1/users", `{"username": "ben", "password": "ben-two", "force":true}`, map[string]string{ + "Authorization": util.BasicAuth("phil", "phil"), + }) + require.Equal(t, 200, rr.Code) + + // Make sure first password fails + rr = request(t, s, "POST", "/v1/account/token", "", map[string]string{ + "Authorization": util.BasicAuth("ben", "ben"), + }) + require.Equal(t, 401, rr.Code) + + // Try to login with second password + rr = request(t, s, "POST", "/v1/account/token", "", map[string]string{ + "Authorization": util.BasicAuth("ben", "ben-two"), + }) + require.Equal(t, 200, rr.Code) } func TestUser_AddRemove_Failures(t *testing.T) { diff --git a/server/types.go b/server/types.go index c6bdb4d1..1b95a73d 100644 --- a/server/types.go +++ b/server/types.go @@ -257,6 +257,7 @@ type apiUserAddRequest struct { Username string `json:"username"` Password string `json:"password"` Tier string `json:"tier"` + Force bool `json:"force"` // Used to change passwords/override existing user // Do not add 'role' here. We don't want to add admins via the API. }