diff --git a/packages/component-fixture-manager/src/fixtures/users.js b/packages/component-fixture-manager/src/fixtures/users.js index 67ecfa32f8fdea77d8bed466eb85dfd563daf1e4..5d8872ba8cd34c9c9b63aec724df9b38a5dfec29 100644 --- a/packages/component-fixture-manager/src/fixtures/users.js +++ b/packages/component-fixture-manager/src/fixtures/users.js @@ -85,6 +85,7 @@ const users = { isConfirmed: false, updateProperties: jest.fn(() => users.user), teams: [], + confirmationToken: chance.hash(), }, author: { type: 'user', @@ -102,6 +103,7 @@ const users = { passwordResetToken: chance.hash(), passwordResetTimestamp: Date.now(), teams: [authorTeamID], + confirmationToken: chance.hash(), }, reviewer: { type: 'user', diff --git a/packages/component-user-manager/src/Users.js b/packages/component-user-manager/src/Users.js index c86df66c9d97bc496731d743faf7831c6f94b832..1c5c159ddfd35f3d654dc3e593c1c74acaed283e 100644 --- a/packages/component-user-manager/src/Users.js +++ b/packages/component-user-manager/src/Users.js @@ -38,7 +38,33 @@ const Invite = app => { '/api/users/reset-password', require('./routes/users/resetPassword')(app.locals.models), ) - + /** + * @api {post} /api/users/confirm Confirm user + * @apiGroup Users + * @apiParamExample {json} Body + * { + * "userId": "valid-user-id", + * "confirmationToken": "12312321" + * } + * @apiSuccessExample {json} Success + * HTTP/1.1 200 OK + * { + * "id": "a6184463-b17a-42f8-b02b-ae1d755cdc6b", + * "type": "user", + * "admin": false, + * "email": "email@example.com", + * "teams": [], + * "username": "email@example.com", + * "fragments": [], + * "collections": [], + * "isConfirmed": true, + * "editorInChief": false, + * "handlingEditor": false + * } + * @apiErrorExample {json} Forgot Password errors + * HTTP/1.1 400 Bad Request + * HTTP/1.1 404 Not Found + */ app.post( '/api/users/confirm', require('./routes/users/confirm')(app.locals.models), diff --git a/packages/component-user-manager/src/routes/users/confirm.js b/packages/component-user-manager/src/routes/users/confirm.js index 2675e160efbd20e970c6488240b704a90464a80e..ace2592bff912eaa12bb8b6ac0a7c26fee52c61e 100644 --- a/packages/component-user-manager/src/routes/users/confirm.js +++ b/packages/component-user-manager/src/routes/users/confirm.js @@ -16,7 +16,7 @@ module.exports = ({ User }) => async (req, res) => { } if (user.isConfirmed) - return res.status(400).json({ error: 'User is already confirmed' }) + return res.status(400).json({ error: 'User is already confirmed.' }) user.isConfirmed = true delete user.confirmationToken @@ -27,6 +27,9 @@ module.exports = ({ User }) => async (req, res) => { token: token.create(user), }) } catch (e) { - throw 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/confirm.test.js b/packages/component-user-manager/src/tests/users/confirm.test.js new file mode 100644 index 0000000000000000000000000000000000000000..6f139d1f227c51ea1f8e990464921a7a1da01068 --- /dev/null +++ b/packages/component-user-manager/src/tests/users/confirm.test.js @@ -0,0 +1,83 @@ +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, author } = fixtures.users +jest.mock('pubsweet-component-mail-service', () => ({ + sendSimpleEmail: jest.fn(), + sendNotificationEmail: jest.fn(), +})) + +const reqBody = { + userId: user.id, + confirmationToken: user.confirmationToken, +} + +const notFoundError = new Error() +notFoundError.name = 'NotFoundError' +notFoundError.status = 404 +const forgotPasswordPath = '../../routes/users/confirm' + +describe('Users confirm 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.userId + const req = httpMocks.createRequest({ body }) + + const res = httpMocks.createResponse() + await require(forgotPasswordPath)(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 confirmation token does not match', async () => { + body.confirmationToken = 'invalid-token' + + const req = httpMocks.createRequest({ body }) + const res = httpMocks.createResponse() + await require(forgotPasswordPath)(models)(req, res) + expect(res.statusCode).toBe(400) + const data = JSON.parse(res._getData()) + expect(data.error).toEqual('Wrong confirmation token.') + }) + it('should return an error when the user does not exist', async () => { + body.userId = 'invalid-user-id' + + const req = httpMocks.createRequest({ body }) + const res = httpMocks.createResponse() + await require(forgotPasswordPath)(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 user is already confirmed', async () => { + body.userId = author.id + body.confirmationToken = author.confirmationToken + const req = httpMocks.createRequest({ body }) + const res = httpMocks.createResponse() + await require(forgotPasswordPath)(models)(req, res) + expect(res.statusCode).toBe(400) + const data = JSON.parse(res._getData()) + expect(data.error).toEqual('User is already confirmed.') + }) + it('should return success when the body is correct', async () => { + const req = httpMocks.createRequest({ body }) + const res = httpMocks.createResponse() + await require(forgotPasswordPath)(models)(req, res) + const data = JSON.parse(res._getData()) + expect(data.token).toBeDefined() + }) +}) diff --git a/packages/xpub-faraday/config/default.js b/packages/xpub-faraday/config/default.js index c282bbfb1667d42dadfa2b1e00a47c0ede992835..19ac346db1ac276c83197297b728bbcf89cb83cc 100644 --- a/packages/xpub-faraday/config/default.js +++ b/packages/xpub-faraday/config/default.js @@ -40,6 +40,7 @@ module.exports = { port: 3000, logger, uploads: 'uploads', + secret: 'SECRET', }, 'pubsweet-client': { API_ENDPOINT: '/api',