diff --git a/packages/medusa/src/api/routes/admin/users/__tests__/reset-password-token.js b/packages/medusa/src/api/routes/admin/users/__tests__/reset-password-token.js index cdef1b12f4..106a65db87 100644 --- a/packages/medusa/src/api/routes/admin/users/__tests__/reset-password-token.js +++ b/packages/medusa/src/api/routes/admin/users/__tests__/reset-password-token.js @@ -2,26 +2,28 @@ import { IdMap } from "medusa-test-utils" import { request } from "../../../../../helpers/test-request" import { UserServiceMock } from "../../../../../services/__mocks__/user" -describe("POST /admin/users/:id/reset-password-token", () => { +describe("POST /admin/users/password-token", () => { describe("successfully generates reset password token", () => { let subject beforeAll(async () => { - subject = await request( - "POST", - `/admin/users/${IdMap.getId("test-user")}/reset-password-token`, - { - adminSession: { - jwt: { - userId: IdMap.getId("admin_user"), - }, + subject = await request("POST", `/admin/users/password-token`, { + payload: { + email: "vandijk@test.dk", + }, + adminSession: { + jwt: { + userId: IdMap.getId("admin_user"), }, - } - ) + }, + }) }) - afterAll(() => { - jest.clearAllMocks() + it("calls UserService retrieve", () => { + expect(UserServiceMock.retrieveByEmail).toHaveBeenCalledTimes(1) + expect(UserServiceMock.retrieveByEmail).toHaveBeenCalledWith( + "vandijk@test.dk" + ) }) it("calls UserService generateResetPasswordToken", () => { @@ -29,12 +31,12 @@ describe("POST /admin/users/:id/reset-password-token", () => { 1 ) expect(UserServiceMock.generateResetPasswordToken).toHaveBeenCalledWith( - IdMap.getId("test-user") + IdMap.getId("vandijk") ) }) - it("returns 200", () => { - expect(subject.status).toEqual(200) + it("returns 204", () => { + expect(subject.status).toEqual(204) }) }) }) diff --git a/packages/medusa/src/api/routes/admin/users/__tests__/reset-password.js b/packages/medusa/src/api/routes/admin/users/__tests__/reset-password.js index 332e1c8fc5..4d28a805d5 100644 --- a/packages/medusa/src/api/routes/admin/users/__tests__/reset-password.js +++ b/packages/medusa/src/api/routes/admin/users/__tests__/reset-password.js @@ -3,7 +3,7 @@ import jwt from "jsonwebtoken" import { request } from "../../../../../helpers/test-request" import { UserServiceMock } from "../../../../../services/__mocks__/user" -describe("POST /admin/users/:id/reset-password", () => { +describe("POST /admin/users/reset-password", () => { describe("successfully resets password", () => { let subject @@ -13,42 +13,37 @@ describe("POST /admin/users/:id/reset-password", () => { it("calls UserService setPassword", async () => { const exp = Math.floor(Date.now() / 1000) + 60 * 15 - const token = jwt.sign( - { - userId: "test-user-id", - name: "Oliver Juhl", - email: "oliver@test.dk", - exp, - }, - "123456789hash" - ) - subject = await request( - "POST", - `/admin/users/test-user-id/reset-password`, - { - payload: { - token, - password: "new-password", - }, - adminSession: { - jwt: { - userId: IdMap.getId("admin_user"), + subject = await request("POST", `/admin/users/reset-password`, { + payload: { + email: "vandijk@test.dk", + token: jwt.sign( + { + user_id: IdMap.getId("vandijk"), + name: "Virgil Van Dijk", + email: "vandijk@test.dk", + exp, }, + "1234" + ), + password: "new-password", + }, + adminSession: { + jwt: { + userId: IdMap.getId("admin_user"), }, - } - ) + }, + }) expect(UserServiceMock.setPassword).toHaveBeenCalledTimes(1) expect(UserServiceMock.setPassword).toHaveBeenCalledWith( - "test-user-id", + IdMap.getId("vandijk"), "new-password" ) }) it("returns updated user", () => { - expect(subject.body._id).toEqual("test-user-id") + expect(subject.body._id).toEqual(IdMap.getId("vandijk")) }) - it("returns 200", () => { expect(subject.status).toEqual(200) }) diff --git a/packages/medusa/src/api/routes/admin/users/index.js b/packages/medusa/src/api/routes/admin/users/index.js index 9e8b036175..03e4db1e77 100644 --- a/packages/medusa/src/api/routes/admin/users/index.js +++ b/packages/medusa/src/api/routes/admin/users/index.js @@ -10,20 +10,20 @@ export default app => { route.get("/:user_id", middlewares.wrap(require("./get-user").default)) route.post("/", middlewares.wrap(require("./create-user").default)) - route.post("/:user_id", middlewares.wrap(require("./update-user").default)) route.post( - "/:user_id/set-password", - middlewares.wrap(require("./set-password").default) - ) - route.post( - "/:user_id/reset-password-token", + "/password-token", middlewares.wrap(require("./reset-password-token").default) ) route.post( - "/:user_id/reset-password", + "/reset-password", middlewares.wrap(require("./reset-password").default) ) + route.post("/:user_id", middlewares.wrap(require("./update-user").default)) + route.post( + "/:user_id/set-password", + middlewares.wrap(require("./set-password").default) + ) route.delete("/:user_id", middlewares.wrap(require("./delete-user").default)) diff --git a/packages/medusa/src/api/routes/admin/users/reset-password-token.js b/packages/medusa/src/api/routes/admin/users/reset-password-token.js index b1537cedc6..a1c24443fe 100644 --- a/packages/medusa/src/api/routes/admin/users/reset-password-token.js +++ b/packages/medusa/src/api/routes/admin/users/reset-password-token.js @@ -1,9 +1,24 @@ +import { MedusaError, Validator } from "medusa-core-utils" + export default async (req, res) => { - const { user_id } = req.params + const schema = Validator.object().keys({ + email: Validator.string() + .email() + .required(), + }) + + const { value, error } = schema.validate(req.body) + if (error) { + throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details) + } + try { const userService = req.scope.resolve("userService") - const token = await userService.generateResetPasswordToken(user_id) - res.json(token) + const user = await userService.retrieveByEmail(value.email) + + await userService.generateResetPasswordToken(user._id) + + res.sendStatus(204) } catch (error) { throw error } diff --git a/packages/medusa/src/api/routes/admin/users/reset-password.js b/packages/medusa/src/api/routes/admin/users/reset-password.js index a0e599a85a..c3867a9616 100644 --- a/packages/medusa/src/api/routes/admin/users/reset-password.js +++ b/packages/medusa/src/api/routes/admin/users/reset-password.js @@ -2,8 +2,10 @@ import { MedusaError, Validator } from "medusa-core-utils" import jwt from "jsonwebtoken" export default async (req, res) => { - const { user_id } = req.params const schema = Validator.object().keys({ + email: Validator.string() + .email() + .required(), token: Validator.string().required(), password: Validator.string().required(), }) @@ -15,11 +17,12 @@ export default async (req, res) => { try { const userService = req.scope.resolve("userService") - let user = await userService.retrieve(user_id) + let user = await userService.retrieveByEmail(value.email) const decodedToken = await jwt.verify(value.token, user.password_hash) - if (!decodedToken) { + if (!decodedToken || decodedToken.user_id !== user._id) { res.status(401).send("Invalid or expired password reset token") + return } await userService.setPassword(user._id, value.password) diff --git a/packages/medusa/src/services/__mocks__/user.js b/packages/medusa/src/services/__mocks__/user.js index be214c6355..dbf74989e0 100644 --- a/packages/medusa/src/services/__mocks__/user.js +++ b/packages/medusa/src/services/__mocks__/user.js @@ -8,6 +8,11 @@ export const users = { email: "oliver@test.dk", password_hash: "hashed123456789", }, + vanDijk: { + _id: IdMap.getId("vandijk"), + email: "vandijk@test.dk", + password_hash: "hashed123456789", + }, jwtUser: { _id: "test-user-id", email: "oliver@test.dk", @@ -43,6 +48,9 @@ export const UserServiceMock = { if (userId === IdMap.getId("test-user")) { return Promise.resolve(users.testUser) } + if (userId === IdMap.getId("vandijk")) { + return Promise.resolve(users.vanDijk) + } // used for jwt token tests if (userId === "test-user-id") { return Promise.resolve(users.jwtUser) @@ -66,6 +74,13 @@ export const UserServiceMock = { return Promise.resolve(undefined) }), retrieveByEmail: jest.fn().mockImplementation(email => { + if (email === "vandijk@test.dk") { + return Promise.resolve({ + _id: IdMap.getId("vandijk"), + email, + password_hash: "1234", + }) + } if (email === "oliver@test.dk") { return bcrypt .hash("123456789", 10) diff --git a/packages/medusa/yarn.lock b/packages/medusa/yarn.lock index a9d4ee9261..db34658fff 100644 --- a/packages/medusa/yarn.lock +++ b/packages/medusa/yarn.lock @@ -4522,6 +4522,14 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= +medusa-core-utils@^0.3.0: + version "0.1.39" + resolved "https://registry.yarnpkg.com/medusa-core-utils/-/medusa-core-utils-0.1.39.tgz#d57816c9bd43f9a92883650c1e66add1665291df" + integrity sha512-R8+U1ile7if+nR6Cjh5exunx0ETV0OfkWUUBUpz1KmHSDv0V0CcvQqU9lcZesPFDEbu3Y2iEjsCqidVA4nG2nQ== + dependencies: + "@hapi/joi" "^16.1.8" + joi-objectid "^3.0.1" + medusa-interfaces@^0.1.27: version "0.1.27" resolved "https://registry.yarnpkg.com/medusa-interfaces/-/medusa-interfaces-0.1.27.tgz#e77f9a9f82a7118eac8b35c1498ef8a5cec78898" @@ -4529,6 +4537,13 @@ medusa-interfaces@^0.1.27: dependencies: mongoose "^5.8.0" +medusa-test-utils@^0.3.0: + version "0.1.39" + resolved "https://registry.yarnpkg.com/medusa-test-utils/-/medusa-test-utils-0.1.39.tgz#b7c166006a2fa4f02e52ab3bfafc19a3ae787f3e" + integrity sha512-M/Br8/HYvl7x2oLnme4NxdQwoyV0XUyOWiCyvPp7q1HUTB684lhJf1MikZVrcSjsh2L1rpyi3GRbKdf4cpJWvw== + dependencies: + mongoose "^5.8.0" + memory-pager@^1.0.2: version "1.5.0" resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5"