From e3cdde3c9ecdd3b7428b943c261c55d283a7e455 Mon Sep 17 00:00:00 2001 From: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> Date: Tue, 14 Apr 2020 21:44:05 +0200 Subject: [PATCH] Fixes according to issue #21 (#35) --- .../medusa/src/services/__mocks__/user.js | 32 +++++++++++ .../medusa/src/services/__tests__/auth.js | 53 ++++++++++--------- packages/medusa/src/services/auth.js | 38 +++++++------ packages/medusa/src/services/user.js | 45 +++++++++++++++- 4 files changed, 126 insertions(+), 42 deletions(-) create mode 100644 packages/medusa/src/services/__mocks__/user.js diff --git a/packages/medusa/src/services/__mocks__/user.js b/packages/medusa/src/services/__mocks__/user.js new file mode 100644 index 0000000000..eebce34d44 --- /dev/null +++ b/packages/medusa/src/services/__mocks__/user.js @@ -0,0 +1,32 @@ +import bcrypt from "bcrypt" + +export const users = { + user1: { + email: "oliver@test.dk", + password_hash: "123456789", + api_token: "123456789", + }, +} + +export const UserServiceMock = { + retrieveByApiToken: jest.fn().mockImplementation(token => { + if (token === "123456789") { + return Promise.resolve(users.user1) + } + return Promise.resolve(undefined) + }), + retrieveByEmail: jest.fn().mockImplementation(email => { + if (email === "oliver@test.dk") { + return bcrypt + .hash("123456789", 10) + .then(hash => ({ email, password_hash: hash })) + } + return Promise.resolve(undefined) + }), +} + +const mock = jest.fn().mockImplementation(() => { + return UserServiceMock +}) + +export default mock diff --git a/packages/medusa/src/services/__tests__/auth.js b/packages/medusa/src/services/__tests__/auth.js index 3ab3206a66..02f6cbd88b 100644 --- a/packages/medusa/src/services/__tests__/auth.js +++ b/packages/medusa/src/services/__tests__/auth.js @@ -1,44 +1,49 @@ import bcrypt from "bcrypt" import AuthService from "../auth" - -const UserModelMock = { - findOne: opt => { - return bcrypt - .hash("123456", 10) - .then(hash => ({ email: "email@mail.com", passwordHash: hash })) - }, -} +import { users, UserServiceMock } from "../__mocks__/user" describe("AuthService", () => { - describe("constructor", () => { - let authService - beforeAll(() => { - authService = new AuthService({ userModel: UserModelMock }) - }) - - it("assigns userModel", () => { - expect(authService.userModel_).toEqual(UserModelMock) - }) - }) - describe("authenticate", () => { let authService + authService = new AuthService({ userService: UserServiceMock }) beforeEach(() => { - authService = new AuthService({ userModel: UserModelMock }) + jest.clearAllMocks() }) - it("returns success when passwords match", async () => { - const result = await authService.authenticate("email@mail.com", "123456") + it("returns success and user when passwords match", async () => { + const result = await authService.authenticate( + "oliver@test.dk", + "123456789" + ) expect(result.success).toEqual(true) - expect(result.user.email).toEqual("email@mail.com") + expect(result.user.email).toEqual("oliver@test.dk") }) it("returns failure when passwords don't match", async () => { - const result = await authService.authenticate("email@mail.com", "not") + const result = await authService.authenticate( + "oliver@test.dk", + "invalid-password" + ) expect(result.success).toEqual(false) + expect(result.error).toEqual("Invalid email or password") expect(result.user).toEqual(undefined) }) }) + + describe("authenticateAPIToken", () => { + let authService + authService = new AuthService({ userService: UserServiceMock }) + beforeEach(() => { + jest.clearAllMocks() + }) + + it("returns success and user when passwords match", async () => { + const result = await authService.authenticateAPIToken("123456789") + + expect(result.success).toEqual(true) + expect(result.user).toEqual(users.user1) + }) + }) }) diff --git a/packages/medusa/src/services/auth.js b/packages/medusa/src/services/auth.js index 68c3eac135..a6954554b9 100644 --- a/packages/medusa/src/services/auth.js +++ b/packages/medusa/src/services/auth.js @@ -6,11 +6,10 @@ import { BaseService } from "medusa-interfaces" * @implements BaseService */ class AuthService extends BaseService { - /** @param { userModel: (UserModel) } */ - constructor({ userModel }) { + constructor({ userService }) { super() - /** @private @const {UserModel} */ - this.userModel_ = userModel + /** @private @const {UserService} */ + this.userService_ = userService } /** @@ -26,14 +25,13 @@ class AuthService extends BaseService { * error: a string with the error message */ async authenticateAPIToken(token) { - const user = await this.userModel_.findOne({ api_token: token }) - - if (user) { + try { + const user = await this.userService_.retrieveByApiToken(token) return { success: true, user, } - } else { + } catch (error) { return { success: false, error: "Invalid API Token", @@ -48,19 +46,27 @@ class AuthService extends BaseService { * @return {{ success: (bool), user: (object | undefined) }} * success: whether authentication succeeded * user: the user document if authentication succeded + * error: a string with the error message */ async authenticate(email, password) { - const user = await this.userModel_.findOne({ email }) - const passwordsMatch = await bcrypt.compare(password, user.passwordHash) - - if (passwordsMatch) { - return { - success: true, - user, + try { + const user = await this.userService_.retrieveByEmail(email) + const passwordsMatch = await bcrypt.compare(password, user.password_hash) + if (passwordsMatch) { + return { + success: true, + user, + } + } else { + return { + success: false, + error: "Invalid email or password", + } } - } else { + } catch (error) { return { success: false, + error: "Invalid email or password", } } } diff --git a/packages/medusa/src/services/user.js b/packages/medusa/src/services/user.js index beb5cae2a1..db004b3731 100644 --- a/packages/medusa/src/services/user.js +++ b/packages/medusa/src/services/user.js @@ -1,4 +1,3 @@ -import mongoose from "mongoose" import _ from "lodash" import bcrypt from "bcrypt" import jwt from "jsonwebtoken" @@ -83,7 +82,49 @@ class UserService extends BaseService { if (!user) { throw new MedusaError( MedusaError.Types.NOT_FOUND, - `User with ${userId} was not found` + `User with id: ${userId} was not found` + ) + } + return user + } + + /** + * Gets a user by api token. + * Throws in case of DB Error and if user was not found. + * @param {string} apiToken - the token of the user to get. + * @return {Promise} the user document. + */ + async retrieveByApiToken(apiToken) { + const user = await this.userModel_ + .findOne({ api_token: apiToken }) + .catch(err => { + throw new MedusaError(MedusaError.Types.DB_ERROR, err.message) + }) + + if (!user) { + throw new MedusaError( + MedusaError.Types.NOT_FOUND, + `User with api token: ${apiToken} was not found` + ) + } + return user + } + + /** + * Gets a user by email. + * Throws in case of DB Error and if user was not found. + * @param {string} email - the email of the user to get. + * @return {Promise} the user document. + */ + async retrieveByEmail(email) { + const user = await this.userModel_.findOne({ email }).catch(err => { + throw new MedusaError(MedusaError.Types.DB_ERROR, err.message) + }) + + if (!user) { + throw new MedusaError( + MedusaError.Types.NOT_FOUND, + `User with email: ${email} was not found` ) } return user