fix(medusa): hide password hash (#429)

* auth tests

* customer auth tests

* user auth test

* store auth snapshot

* auth snapshot

* auth with deleted password hashes

* manual field input for test scripts

* fix circleci with double retrieve of user and customer

* add email validation to user

* fix: cleanup

Co-authored-by: Sebastian Rindom <skrindom@gmail.com>
This commit is contained in:
pKorsholm
2021-09-30 12:19:37 +02:00
committed by GitHub
parent 9b64828ec3
commit cd4afd1576
8 changed files with 186 additions and 13 deletions

View File

@@ -0,0 +1,16 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`/admin/discounts creates admin session correctly 1`] = `
Object {
"api_token": "test_token",
"created_at": Any<String>,
"deleted_at": null,
"email": "admin@medusa.js",
"first_name": null,
"id": "admin_user",
"last_name": null,
"metadata": null,
"updated_at": Any<String>,
}
`;

View File

@@ -0,0 +1,54 @@
const path = require("path")
const { Region, DiscountRule, Discount } = require("@medusajs/medusa")
const setupServer = require("../../../helpers/setup-server")
const { useApi } = require("../../../helpers/use-api")
const { initDb, useDb } = require("../../../helpers/use-db")
const adminSeeder = require("../../helpers/admin-seeder")
const { exportAllDeclaration } = require("@babel/types")
jest.setTimeout(30000)
describe("/admin/auth", () => {
let medusaProcess
let dbConnection
beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", ".."))
dbConnection = await initDb({ cwd })
medusaProcess = await setupServer({ cwd })
try {
await adminSeeder(dbConnection)
} catch (e) {
throw e
}
})
afterAll(async () => {
const db = useDb()
await db.shutdown()
medusaProcess.kill()
})
it("creates admin session correctly", async () => {
const api = useApi()
const response = await api
.post("/admin/auth", {
email: "admin@medusa.js",
password: "secret_password",
})
.catch((err) => {
console.log(err)
})
expect(response.status).toEqual(200)
expect(response.data.user.password_hash).toEqual(undefined)
expect(response.data.user).toMatchSnapshot({
email: "admin@medusa.js",
created_at: expect.any(String),
updated_at: expect.any(String),
})
})
})

View File

@@ -0,0 +1,18 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`/admin/discounts creates store session correctly 1`] = `
Object {
"billing_address_id": null,
"created_at": Any<String>,
"deleted_at": null,
"email": "test@testesen.dk",
"first_name": "test",
"has_account": true,
"id": Any<String>,
"last_name": "testesen",
"metadata": null,
"orders": Array [],
"phone": "12345678",
"updated_at": Any<String>,
}
`;

View File

@@ -0,0 +1,64 @@
const path = require("path")
const { Region, DiscountRule, Discount } = require("@medusajs/medusa")
const setupServer = require("../../../helpers/setup-server")
const { useApi } = require("../../../helpers/use-api")
const { initDb, useDb } = require("../../../helpers/use-db")
const adminSeeder = require("../../helpers/admin-seeder")
const { exportAllDeclaration } = require("@babel/types")
jest.setTimeout(30000)
describe("/admin/auth", () => {
let medusaProcess
let dbConnection
beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", ".."))
dbConnection = await initDb({ cwd })
medusaProcess = await setupServer({ cwd })
})
afterAll(async () => {
const db = useDb()
await db.shutdown()
medusaProcess.kill()
})
it("creates store session correctly", async () => {
const api = useApi()
await api
.post("/store/customers", {
email: "test@testesen.dk",
password: "secret_password",
first_name: "test",
last_name: "testesen",
phone: "12345678",
})
.catch((err) => {
console.log(err)
})
const response = await api
.post("/store/auth", {
email: "test@testesen.dk",
password: "secret_password",
})
.catch((err) => {
console.log(err)
})
expect(response.status).toEqual(200)
expect(response.data.customer.password_hash).toEqual(undefined)
expect(response.data.customer).toMatchSnapshot({
id: expect.any(String),
created_at: expect.any(String),
updated_at: expect.any(String),
first_name: "test",
last_name: "testesen",
phone: "12345678",
email: "test@testesen.dk",
})
})
})

View File

@@ -46,7 +46,7 @@ export class Customer {
)
shipping_addresses: Address[]
@Column({ nullable: true })
@Column({ nullable: true, select: false })
password_hash: string
@Column({ nullable: true })

View File

@@ -26,7 +26,7 @@ export class User {
@Column({ nullable: true })
last_name: string
@Column({ nullable: true })
@Column({ nullable: true, select: false })
password_hash: string
@Column({ nullable: true })

View File

@@ -76,12 +76,17 @@ class AuthService extends BaseService {
*/
async authenticate(email, password) {
try {
const user = await this.userService_.retrieveByEmail(email)
const userPasswordHash = await this.userService_.retrieveByEmail(email, {
select: ["password_hash"],
})
const passwordsMatch = await this.comparePassword_(
password,
user.password_hash
userPasswordHash.password_hash
)
if (passwordsMatch) {
const user = await this.userService_.retrieveByEmail(email)
return {
success: true,
user,
@@ -113,8 +118,13 @@ class AuthService extends BaseService {
*/
async authenticateCustomer(email, password) {
try {
const customer = await this.customerService_.retrieveByEmail(email)
if (!customer.password_hash) {
const customerPasswordHash = await this.customerService_.retrieveByEmail(
email,
{
select: ["password_hash"],
}
)
if (!customerPasswordHash.password_hash) {
return {
success: false,
error: "Invalid email or password",
@@ -123,9 +133,11 @@ class AuthService extends BaseService {
const passwordsMatch = await this.comparePassword_(
password,
customer.password_hash
customerPasswordHash.password_hash
)
if (passwordsMatch) {
const customer = await this.customerService_.retrieveByEmail(email)
return {
success: true,
customer,

View File

@@ -48,7 +48,18 @@ class UserService extends BaseService {
* @return {string} the validated email
*/
validateEmail_(email) {
return email
const schema = Validator.string()
.email()
.required()
const { value, error } = schema.validate(email)
if (error) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"The email is not valid"
)
}
return value.toLowerCase()
}
/**
@@ -114,13 +125,11 @@ class UserService extends BaseService {
* @param {string} email - the email of the user to get.
* @return {Promise<User>} the user document.
*/
async retrieveByEmail(email, relations = []) {
async retrieveByEmail(email, config = {}) {
const userRepo = this.manager_.getCustomRepository(this.userRepository_)
const user = await userRepo.findOne({
where: { email },
relations,
})
const query = this.buildQuery_({ email: email.toLowerCase() }, config)
const user = await userRepo.findOne(query)
if (!user) {
throw new MedusaError(