diff --git a/openapi.yaml b/openapi.yaml index 576793b6fb..58200ee8d1 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -155,6 +155,46 @@ paths: $ref: '#/components/schemas/Status' response: type: object + '/{uid}/password': + put: + tags: + - users + summary: changes a user's password + parameters: + - in: path + name: uid + schema: + type: integer + required: true + description: uid of the user to update + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + currentPassword: + type: string + description: test + example: oldp455word + newPassword: + type: string + example: s3cre7password + required: + - newPassword + responses: + '200': + description: user profile updated + content: + application/json: + schema: + type: object + properties: + status: + $ref: '#/components/schemas/Status' + response: + type: object components: schemas: Status: diff --git a/public/src/client/account/edit/password.js b/public/src/client/account/edit/password.js index 964af3053e..fc6b9ef4d4 100644 --- a/public/src/client/account/edit/password.js +++ b/public/src/client/account/edit/password.js @@ -67,11 +67,22 @@ define('forum/account/edit/password', ['forum/account/header', 'translator', 'zx var btn = $(this); if (passwordvalid && passwordsmatch) { btn.addClass('disabled').find('i').removeClass('hide'); - socket.emit('user.changePassword', { - currentPassword: currentPassword.val(), - newPassword: password.val(), - uid: ajaxify.data.theirid, - }, function (err) { + $.ajax({ + url: config.relative_path + '/api/v1/users/' + ajaxify.data.theirid + '/password', + method: 'put', + data: { + currentPassword: currentPassword.val(), + newPassword: password.val(), + }, + }).done(function () { + if (parseInt(app.user.uid, 10) === parseInt(ajaxify.data.uid, 10)) { + window.location.href = config.relative_path + '/login'; + } else { + ajaxify.go('user/' + ajaxify.data.userslug + '/edit'); + } + }).fail(function (ev) { + app.alertError(ev.responseJSON.status.message); + }).always(function () { btn.removeClass('disabled').find('i').addClass('hide'); currentPassword.val(''); password.val(''); @@ -80,15 +91,6 @@ define('forum/account/edit/password', ['forum/account/header', 'translator', 'zx password_confirm_notify.parent().removeClass('show-success show-danger'); passwordsmatch = false; passwordvalid = false; - - if (err) { - return app.alertError(err.message); - } - if (parseInt(app.user.uid, 10) === parseInt(ajaxify.data.uid, 10)) { - window.location.href = config.relative_path + '/login'; - } else { - ajaxify.go('user/' + ajaxify.data.userslug + '/edit'); - } }); } else { if (!passwordsmatch) { diff --git a/src/controllers/write/users.js b/src/controllers/write/users.js index 0b996c4396..7af31dde67 100644 --- a/src/controllers/write/users.js +++ b/src/controllers/write/users.js @@ -106,3 +106,16 @@ async function processDeletion(uid, req, res) { email: userData.email, }); } + +Users.changePassword = async (req, res) => { + req.body.uid = req.params.uid; + await user.changePassword(req.user.uid, Object.assign(req.body, { ip: req.ip })); + await events.log({ + type: 'password-change', + uid: req.user.uid, + targetUid: req.params.uid, + ip: req.ip, + }); + + helpers.formatApiResponse(200, res); +}; diff --git a/src/routes/write/users.js b/src/routes/write/users.js index 045dae2abb..18f027b67d 100644 --- a/src/routes/write/users.js +++ b/src/routes/write/users.js @@ -23,42 +23,11 @@ function authenticatedRoutes() { setupApiRoute(router, '/', middleware, [...middlewares, middleware.checkRequired.bind(null, ['username']), middleware.isAdmin], 'post', controllers.write.users.create); setupApiRoute(router, '/', middleware, [...middlewares, middleware.checkRequired.bind(null, ['uids']), middleware.isAdmin, middleware.exposePrivileges], 'delete', controllers.write.users.deleteMany); + setupApiRoute(router, '/:uid', middleware, [...middlewares], 'put', controllers.write.users.update); setupApiRoute(router, '/:uid', middleware, [...middlewares, middleware.exposePrivileges], 'delete', controllers.write.users.delete); - // app.route('/:uid') - // .delete(apiMiddleware.requireUser, apiMiddleware.exposeAdmin, function(req, res) { - // if (parseInt(req.params.uid, 10) !== parseInt(req.user.uid, 10) && !res.locals.isAdmin) { - // return errorHandler.respond(401, res); - // } - - // // Clear out any user tokens belonging to the to-be-deleted user - // async.waterfall([ - // async.apply(auth.getTokens, req.params.uid), - // function(tokens, next) { - // async.each(tokens, function(token, next) { - // auth.revokeToken(token, 'user', next); - // }, next); - // }, - // async.apply(Users.delete, req.user.uid, req.params.uid) - // ], function(err) { - // return errorHandler.handle(err, res); - // }); - // }); - - // app.put('/:uid/password', apiMiddleware.requireUser, apiMiddleware.exposeAdmin, function(req, res) { - // if (parseInt(req.params.uid, 10) !== parseInt(req.user.uid, 10) && !res.locals.isAdmin) { - // return errorHandler.respond(401, res); - // } - - // Users.changePassword(req.user.uid, { - // uid: req.params.uid, - // currentPassword: req.body.current || '', - // newPassword: req.body['new'] || '' - // }, function(err) { - // errorHandler.handle(err, res); - // }); - // }); + setupApiRoute(router, '/:uid/password', middleware, [...middlewares, middleware.checkRequired.bind(null, ['newPassword'])], 'put', controllers.write.users.changePassword); // app.put('/:uid/follow', apiMiddleware.requireUser, function(req, res) { // Users.follow(req.user.uid, req.params.uid, function(err) { diff --git a/src/socket.io/user/profile.js b/src/socket.io/user/profile.js index 45e2c3bcee..4e575ade02 100644 --- a/src/socket.io/user/profile.js +++ b/src/socket.io/user/profile.js @@ -78,6 +78,8 @@ module.exports = function (SocketUser) { } SocketUser.changePassword = async function (socket, data) { + sockets.warnDeprecated(socket, 'PUT /api/v1/users/:uid/password'); + if (!socket.uid) { throw new Error('[[error:invalid-uid]]'); }