diff --git a/packages/component-user-manager/src/Users.js b/packages/component-user-manager/src/Users.js index 3a6774b2386056c681551de4afae99247d656845..d3a71ed9de7a52e50d913683cbf2027b70914a78 100644 --- a/packages/component-user-manager/src/Users.js +++ b/packages/component-user-manager/src/Users.js @@ -1,6 +1,6 @@ const bodyParser = require('body-parser') -const Invite = app => { +const Users = app => { app.use(bodyParser.json()) const authBearer = app.locals.passport.authenticate('bearer', { session: false, @@ -125,4 +125,4 @@ const Invite = app => { ) } -module.exports = Invite +module.exports = Users diff --git a/packages/component-user-manager/src/routes/users/changePassword.js b/packages/component-user-manager/src/routes/users/changePassword.js new file mode 100644 index 0000000000000000000000000000000000000000..d1b49ae03fb012260a14a324a72640c1b4d48280 --- /dev/null +++ b/packages/component-user-manager/src/routes/users/changePassword.js @@ -0,0 +1,33 @@ +const { services } = require('pubsweet-component-helper-service') +const { token } = require('pubsweet-server/src/authentication') + +module.exports = models => async (req, res) => { + const { password, newPassword } = req.body + if (!services.checkForUndefinedParams(password, newPassword)) + return res.status(400).json({ error: 'Missing required params.' }) + + if (newPassword.length < 7) + return res + .status(400) + .json({ error: 'Password needs to be at least 7 characters long.' }) + + let user + try { + user = await models.User.find(req.user) + if (!user.validPassword(password)) { + return res.status(400).json({ error: 'Wrong username or password.' }) + } + user.password = newPassword + user = await user.save() + + return res.status(200).json({ + ...user, + token: token.create(user), + }) + } catch (e) { + const notFoundError = await services.handleNotFoundError(e, 'User') + return res.status(notFoundError.status).json({ + error: notFoundError.message, + }) + } +} diff --git a/packages/component-user-manager/src/tests/users/changePassword.test.js b/packages/component-user-manager/src/tests/users/changePassword.test.js new file mode 100644 index 0000000000000000000000000000000000000000..693dc6563d6c4dd7eab1a4b77cdc402531087e57 --- /dev/null +++ b/packages/component-user-manager/src/tests/users/changePassword.test.js @@ -0,0 +1,96 @@ +process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0' +process.env.SUPPRESS_NO_CONFIG_WARNING = true + +const httpMocks = require('node-mocks-http') +const cloneDeep = require('lodash/cloneDeep') +const fixturesService = require('pubsweet-component-fixture-service') + +const { Model, fixtures } = fixturesService + +const { user } = fixtures.users +jest.mock('pubsweet-component-mail-service', () => ({ + sendSimpleEmail: jest.fn(), + sendNotificationEmail: jest.fn(), +})) + +const reqBody = { + password: 'password', + newPassword: 'newPassword', +} + +const notFoundError = new Error() +notFoundError.name = 'NotFoundError' +notFoundError.status = 404 +const changePasswordPath = '../../routes/users/changePassword' + +describe('Users password reset route handler', () => { + let testFixtures = {} + let body = {} + let models + beforeEach(() => { + testFixtures = cloneDeep(fixtures) + body = cloneDeep(reqBody) + models = Model.build(testFixtures) + }) + it('should return an error when some parameters are missing', async () => { + delete body.password + const req = httpMocks.createRequest({ body }) + req.user = user.id + + const res = httpMocks.createResponse() + await require(changePasswordPath)(models)(req, res) + + expect(res.statusCode).toBe(400) + const data = JSON.parse(res._getData()) + expect(data.error).toEqual('Missing required params.') + }) + it('should return an error when the password is too small', async () => { + body.newPassword = 'small' + const req = httpMocks.createRequest({ body }) + req.user = user.id + + const res = httpMocks.createResponse() + await require(changePasswordPath)(models)(req, res) + expect(res.statusCode).toBe(400) + const data = JSON.parse(res._getData()) + expect(data.error).toEqual( + 'Password needs to be at least 7 characters long.', + ) + }) + it('should return an error when user is not found', async () => { + const req = httpMocks.createRequest({ body }) + req.user = 'invalid-user-id' + + const res = httpMocks.createResponse() + await require(changePasswordPath)(models)(req, res) + expect(res.statusCode).toBe(404) + const data = JSON.parse(res._getData()) + expect(data.error).toEqual('User not found') + }) + it('should return an error when the current password is incorrect', async () => { + body.password = 'invalid-password' + const req = httpMocks.createRequest({ body }) + req.user = user.id + + const res = httpMocks.createResponse() + await require(changePasswordPath)(models)(req, res) + expect(res.statusCode).toBe(400) + const data = JSON.parse(res._getData()) + expect(data.error).toEqual('Wrong username or password.') + }) + it('should return success when the body is correct', async () => { + const req = httpMocks.createRequest({ body }) + req.user = user.id + + const res = httpMocks.createResponse() + await require(changePasswordPath)(models)(req, res) + + expect(res.statusCode).toBe(200) + const savedUser = JSON.parse(res._getData()) + + savedUser.validPassword = user.validPassword + expect(savedUser.token).not.toEqual(user.token) + + expect(savedUser.validPassword(body.newPassword)).toBeTruthy() + }) +})