diff --git a/.env.sample b/.env.sample index cc95d5cac4..a6d7cd1734 100644 --- a/.env.sample +++ b/.env.sample @@ -107,3 +107,5 @@ MINIO_DATA_DIR= IMAGE_SIZE_LIMIT_KB=3000 ENCRYPTION_KEY= + +HASH_PEPPER = diff --git a/setup.ts b/setup.ts index 63d8d6726e..f16eba5264 100644 --- a/setup.ts +++ b/setup.ts @@ -403,6 +403,21 @@ export async function setEncryptionKey(): Promise { } } +export async function setHashPepper(): Promise { + try { + if (process.env.HASH_PEPPER) { + console.log("\n Hash Pepper is already present."); + } else { + const hashPepper = crypto.randomBytes(32).toString("hex"); + process.env.HASH_PEPPER = hashPepper; + + console.log("\n Hash Pepper set successfully"); + } + } catch (err) { + console.error("An error occurred", err); + } +} + // Get the super admin email /** * The function `superAdmin` prompts the user for a super admin email, updates a configuration file diff --git a/src/models/User.ts b/src/models/User.ts index 6cb0298c79..0752e32ce2 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -148,23 +148,24 @@ const userSchema = new Schema( type: String, lowercase: true, required: true, - validate: [ - { - validator: function (value: string) { - try { - const decrypted = decryptEmail(value).decrypted; - return [validator.isEmail(decrypted), "invalid email"]; - } catch (error) { - console.error("error decrypting the email", error); - return false; - } - }, + validate: { + validator: function (value: string) { + try { + const decrypted = decryptEmail(value).decrypted; + return validator.isEmail(decrypted); + } catch (error) { + console.error("Error decrypting the email", error); + return false; + } }, - ], + message: "Invalid email", + }, }, hashedEmail: { type: String, required: true, + unique: true, + index: true, }, employmentStatus: { type: String, diff --git a/src/resolvers/Mutation/forgotPassword.ts b/src/resolvers/Mutation/forgotPassword.ts index 371de5378d..52ba649b41 100644 --- a/src/resolvers/Mutation/forgotPassword.ts +++ b/src/resolvers/Mutation/forgotPassword.ts @@ -8,6 +8,7 @@ import { USER_NOT_FOUND_ERROR, } from "../../constants"; import jwt from "jsonwebtoken"; +import crypto from "crypto"; /** * This function enables a user to restore password. @@ -47,7 +48,10 @@ export const forgotPassword: MutationResolvers["forgotPassword"] = async ( throw new Error(INVALID_OTP); } - const hashedEmail = await bcrypt.hash(email.toLowerCase(), 12); + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); const user = await User.findOne({ hashedEmail: hashedEmail }).lean(); diff --git a/src/resolvers/Mutation/login.ts b/src/resolvers/Mutation/login.ts index b7f5b0c1cd..39df105a39 100644 --- a/src/resolvers/Mutation/login.ts +++ b/src/resolvers/Mutation/login.ts @@ -13,6 +13,7 @@ import { createAccessToken, createRefreshToken, } from "../../utilities"; +import crypto from "crypto"; /** * This function enables login. (note: only works when using the last resort SuperAdmin credentials) * @param _parent - parent of current request @@ -23,8 +24,10 @@ import { * @returns Updated user */ export const login: MutationResolvers["login"] = async (_parent, args) => { - const hashedEmail = await bcrypt.hash(args.data.email.toLowerCase(), 12); - + const hashedEmail = crypto + .createHash("sha256") + .update(args.data.email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); let user = await User.findOne({ hashedEmail: hashedEmail, }).lean(); diff --git a/src/resolvers/Mutation/otp.ts b/src/resolvers/Mutation/otp.ts index 98b7b09074..a02c9de4ae 100644 --- a/src/resolvers/Mutation/otp.ts +++ b/src/resolvers/Mutation/otp.ts @@ -5,6 +5,7 @@ import { User } from "../../models"; import { mailer } from "../../utilities"; import { ACCESS_TOKEN_SECRET, USER_NOT_FOUND_ERROR } from "../../constants"; import { logger } from "../../libraries"; +import crypto from "crypto"; /** * This function generates otp. * @param _parent - parent of current request @@ -14,7 +15,11 @@ import { logger } from "../../libraries"; * @returns Email to the user with the otp. */ export const otp: MutationResolvers["otp"] = async (_parent, args) => { - const hashedEmail = await bcrypt.hash(args.data.email.toLowerCase(), 12); + const hashedEmail = crypto + .createHash("sha256") + .update(args.data.email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); + const user = await User.findOne({ hashedEmail: hashedEmail, }).lean(); diff --git a/src/resolvers/Mutation/signUp.ts b/src/resolvers/Mutation/signUp.ts index 379c489cb0..fbd383c886 100644 --- a/src/resolvers/Mutation/signUp.ts +++ b/src/resolvers/Mutation/signUp.ts @@ -23,6 +23,7 @@ import { } from "../../utilities"; import { uploadEncodedImage } from "../../utilities/encodedImageStorage/uploadEncodedImage"; import { encryptEmail } from "../../utilities/encryption"; +import crypto from "crypto"; //import { isValidString } from "../../libraries/validators/validateString"; //import { validatePassword } from "../../libraries/validators/validatePassword"; /** @@ -32,16 +33,17 @@ import { encryptEmail } from "../../utilities/encryption"; * @returns Sign up details. */ export const signUp: MutationResolvers["signUp"] = async (_parent, args) => { - const allUsers = await User.find({}); - for (const user of allUsers) { - const hashedEmail = await bcrypt.hash(args.data.email.toLowerCase(), 12); - if (hashedEmail == user.hashedEmail) { - throw new errors.ConflictError( - requestContext.translate(EMAIL_ALREADY_EXISTS_ERROR.MESSAGE), - EMAIL_ALREADY_EXISTS_ERROR.CODE, - EMAIL_ALREADY_EXISTS_ERROR.PARAM, - ); - } + const hashedEmail = crypto + .createHash("sha256") + .update(args.data.email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); + const existingUser = await User.findOne({ hashedEmail }); + if (existingUser) { + throw new errors.ConflictError( + requestContext.translate(EMAIL_ALREADY_EXISTS_ERROR.MESSAGE), + EMAIL_ALREADY_EXISTS_ERROR.CODE, + EMAIL_ALREADY_EXISTS_ERROR.PARAM, + ); } const organizationFoundInCache = await findOrganizationsInCache([ @@ -70,8 +72,6 @@ export const signUp: MutationResolvers["signUp"] = async (_parent, args) => { const hashedPassword = await bcrypt.hash(args.data.password, 12); - const hashedEmail = await bcrypt.hash(args.data.email.toLowerCase(), 12); - // Upload file let uploadImageFileName = null; if (args.file) { diff --git a/src/resolvers/Mutation/updateUserProfile.ts b/src/resolvers/Mutation/updateUserProfile.ts index 09534708c1..c4887f20ee 100644 --- a/src/resolvers/Mutation/updateUserProfile.ts +++ b/src/resolvers/Mutation/updateUserProfile.ts @@ -10,7 +10,7 @@ import { deleteUserFromCache } from "../../services/UserCache/deleteUserFromCach import { findUserInCache } from "../../services/UserCache/findUserInCache"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; import { uploadEncodedImage } from "../../utilities/encodedImageStorage/uploadEncodedImage"; -import bcrypt from "bcrypt"; +import crypto from "crypto"; /** * This function enables to update user profile. * @param _parent - parent of current request @@ -45,8 +45,10 @@ export const updateUserProfile: MutationResolvers["updateUserProfile"] = async ( ); } - const hashedEmail = bcrypt.hash(args.data?.email.toLowerCase(), 12); - + const hashedEmail = crypto + .createHash("sha256") + .update(args.data?.email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); if (args.data?.email && args.data?.email !== currentUser?.email) { const userWithEmailExists = await User.findOne({ hashedEmail: hashedEmail, diff --git a/src/utilities/encryption.ts b/src/utilities/encryption.ts index 0bb56ae010..2385756aa7 100644 --- a/src/utilities/encryption.ts +++ b/src/utilities/encryption.ts @@ -2,10 +2,13 @@ import crypto from "crypto"; const algorithm = "aes-256-gcm"; -const saltlength = 16; +const authTagLength = 16; +const authTagHexLength = authTagLength * 2; -export function generateRandomSalt(): string { - return crypto.randomBytes(saltlength).toString("hex"); +const saltLength = 16; + +export function generateRandomIV(): string { + return crypto.randomBytes(saltLength).toString("hex"); } export function encryptEmail(email: string): string { @@ -13,9 +16,13 @@ export function encryptEmail(email: string): string { if (!encryptionKey) { throw new Error("Encryption key is not defined."); + } else if (encryptionKey.length !== 64) { + throw new Error( + "Encryption key must be a 256-bit hexadecimal string (64 characters).", + ); } - const iv = generateRandomSalt(); + const iv = generateRandomIV(); const cipher = crypto.createCipheriv( algorithm, Buffer.from(encryptionKey, "hex"), @@ -30,11 +37,11 @@ export function encryptEmail(email: string): string { return iv + authTag.toString("hex") + encrypted.toString("hex"); } -export function decryptEmail(encryptedWithEmailSalt: string): { +export function decryptEmail(encryptedData: string): { decrypted: string; - salt: string; + iv: string; } { - if (encryptedWithEmailSalt.length < saltlength * 2) { + if (encryptedData.length < saltLength * 2) { throw new Error("Invalid encrypted data: input is too short."); } @@ -42,14 +49,18 @@ export function decryptEmail(encryptedWithEmailSalt: string): { if (!encryptionKey) { throw new Error("Encryption key is not defined."); + } else if (encryptionKey.length !== 64) { + throw new Error( + "Encryption key must be a 256-bit hexadecimal string (64 characters).", + ); } - const iv = encryptedWithEmailSalt.slice(0, saltlength * 2); + const iv = encryptedData.slice(0, saltLength * 2); const authTag = Buffer.from( - encryptedWithEmailSalt.slice(saltlength * 2, saltlength * 2 + 32), + encryptedData.slice(saltLength * 2, saltLength * 2 + authTagHexLength), "hex", ); - const encrypted = encryptedWithEmailSalt.slice(saltlength * 2 + 32); + const encrypted = encryptedData.slice(saltLength * 2 + authTagHexLength); const decipher = crypto.createDecipheriv( algorithm, @@ -59,9 +70,14 @@ export function decryptEmail(encryptedWithEmailSalt: string): { decipher.setAuthTag(authTag); - const decrypted = Buffer.concat([ - decipher.update(Buffer.from(encrypted, "hex")), - decipher.final(), - ]).toString("utf8"); - return { decrypted, salt: iv }; + let decrypted; + try { + decrypted = Buffer.concat([ + decipher.update(Buffer.from(encrypted, "hex")), + decipher.final(), + ]).toString("utf8"); + } catch (error) { + throw new Error("Decryption failed: invalid data or authentication tag."); + } + return { decrypted, iv }; } diff --git a/tests/directives/directiveTransformer/roleDirectiveTransformer.spec.ts b/tests/directives/directiveTransformer/roleDirectiveTransformer.spec.ts index aaec9acb5b..11f093db2e 100644 --- a/tests/directives/directiveTransformer/roleDirectiveTransformer.spec.ts +++ b/tests/directives/directiveTransformer/roleDirectiveTransformer.spec.ts @@ -17,8 +17,7 @@ import { User } from "../../../src/models"; import { connect, disconnect } from "../../helpers/db"; import type { TestUserType } from "../../helpers/userAndOrg"; import { encryptEmail } from "../../../src/utilities/encryption"; -import bcrypt from "bcrypt"; - +import crypto from "crypto"; let MONGOOSE_INSTANCE: typeof mongoose; const app = express(); @@ -60,7 +59,10 @@ beforeAll(async () => { MONGOOSE_INSTANCE = await connect(); const email = `email${nanoid().toLowerCase()}@gmail.com`; - const hashedEmail = await bcrypt.hash(email, 12); + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); testUser = await User.create({ userId: new Types.ObjectId().toString(), diff --git a/tests/helpers/advertisement.ts b/tests/helpers/advertisement.ts index 03d9503595..48f556582a 100644 --- a/tests/helpers/advertisement.ts +++ b/tests/helpers/advertisement.ts @@ -4,8 +4,7 @@ import type { InterfaceAdvertisement, InterfaceUser } from "../../src/models"; import { Advertisement, AppUserProfile, User } from "../../src/models"; import { createTestUserAndOrganization } from "./userAndOrg"; import { encryptEmail } from "../../src/utilities/encryption"; -import bcrypt from "bcrypt"; - +import crypto from "crypto"; export type TestAdvertisementType = { _id: string; organizationId: PopulatedDoc; @@ -44,7 +43,10 @@ export type TestSuperAdminType = (InterfaceUser & Document) | null; const email = `email${nanoid().toLowerCase()}@gmail.com`; -const hashedEmail = bcrypt.hash(email, 12); +const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); export const createTestSuperAdmin = async (): Promise => { const testSuperAdmin = await User.create({ diff --git a/tests/helpers/user.ts b/tests/helpers/user.ts index e6c8879f56..5feecf018d 100644 --- a/tests/helpers/user.ts +++ b/tests/helpers/user.ts @@ -3,16 +3,17 @@ import { nanoid } from "nanoid"; import type { InterfaceUser } from "../../src/models"; import { AppUserProfile, User } from "../../src/models"; import { encryptEmail } from "../../src/utilities/encryption"; -import bcrypt from "bcrypt"; - +import crypto from "crypto"; export type TestUserType = | (InterfaceUser & Document) | null; export const createTestUser = async (): Promise => { const email = `email${nanoid().toLowerCase()}@gmail.com`; - const hashedEmail = await bcrypt.hash(email.toLowerCase(), 12); - + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); const testUser = await User.create({ email: encryptEmail(email), password: `pass${nanoid().toLowerCase()}`, diff --git a/tests/helpers/userAndOrg.ts b/tests/helpers/userAndOrg.ts index 18e5e2e545..9941045f4d 100644 --- a/tests/helpers/userAndOrg.ts +++ b/tests/helpers/userAndOrg.ts @@ -8,8 +8,7 @@ import type { } from "../../src/models"; import { AppUserProfile, Organization, User } from "../../src/models"; import { encryptEmail } from "../../src/utilities/encryption"; -import bcrypt from "bcrypt"; - +import crypto from "crypto"; export type TestOrganizationType = // eslint-disable-next-line @typescript-eslint/no-explicit-any (InterfaceOrganization & Document) | null; @@ -22,7 +21,10 @@ export type TestAppUserProfileType = | null; export const createTestUser = async (): Promise => { const email = `email${nanoid().toLowerCase()}@gmail.com`; - const hashedEmail = bcrypt.hash(email, 12); + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); let testUser = await User.create({ email: encryptEmail(email), diff --git a/tests/helpers/userAndUserFamily.ts b/tests/helpers/userAndUserFamily.ts index f59f482045..755ce8a3fa 100644 --- a/tests/helpers/userAndUserFamily.ts +++ b/tests/helpers/userAndUserFamily.ts @@ -3,7 +3,7 @@ import type { InterfaceUser } from "../../src/models"; import { AppUserProfile, User } from "../../src/models"; import type { InterfaceUserFamily } from "../../src/models/userFamily"; import { UserFamily } from "../../src/models/userFamily"; -import bcrypt from "bcrypt"; +import crypto from "crypto"; import type { Document } from "mongoose"; import { encryptEmail } from "../../src/utilities/encryption"; /* eslint-disable */ @@ -17,11 +17,14 @@ export type TestUserType = /* eslint-enable */ export const createTestUserFunc = async (): Promise => { const email = `email${nanoid().toLowerCase()}@gmail.com`; - const hashedEmail = bcrypt.hash(email, 12); + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); const testUser = await User.create({ email: encryptEmail(email), - hashedEmail: hashedEmail, + hashedEmail, password: `pass${nanoid().toLowerCase()}`, firstName: `firstName${nanoid().toLowerCase()}`, lastName: `lastName${nanoid().toLowerCase()}`, diff --git a/tests/resolvers/Mutation/UpdateSessionTimeout.spec.ts b/tests/resolvers/Mutation/UpdateSessionTimeout.spec.ts index 955c4a3682..f0f540192e 100644 --- a/tests/resolvers/Mutation/UpdateSessionTimeout.spec.ts +++ b/tests/resolvers/Mutation/UpdateSessionTimeout.spec.ts @@ -32,6 +32,7 @@ import type { import { requestContext } from "../../../src/libraries"; +import crypto from "crypto"; import bcrypt from "bcryptjs"; import { encryptEmail } from "../../../src/utilities/encryption"; @@ -73,11 +74,13 @@ beforeEach(async () => { const hashedPassword = await bcrypt.hash("password", 12); const email = `email${nanoid().toLowerCase()}@gmail.com`; - const hashedEmail = bcrypt.hash(email, 12); - + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); testUser = await User.create({ email: encryptEmail(email), - hashedEmail: hashedEmail, + hashedEmail, password: hashedPassword, firstName: "firstName", lastName: "lastName", diff --git a/tests/resolvers/Mutation/adminRemoveGroup.spec.ts b/tests/resolvers/Mutation/adminRemoveGroup.spec.ts index e2a1831156..cefc8e82f4 100644 --- a/tests/resolvers/Mutation/adminRemoveGroup.spec.ts +++ b/tests/resolvers/Mutation/adminRemoveGroup.spec.ts @@ -22,7 +22,7 @@ import type { TestOrganizationType, TestUserType, } from "../../helpers/userAndOrg"; -import bcrypt from "bcrypt"; +import crypto from "crypto"; import { encryptEmail } from "../../../src/utilities/encryption"; let testUser: TestUserType; @@ -55,8 +55,10 @@ describe("resolvers -> Mutation -> adminRemoveGroup", () => { }; const email = `email${nanoid().toLowerCase()}@gmail.com`; - const hashedEmail = bcrypt.hash(email, 12); - + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); const newUser = await User.create({ email: encryptEmail(email), hashedEmail: hashedEmail, diff --git a/tests/resolvers/Mutation/blockPluginCreationBySuperadmin.spec.ts b/tests/resolvers/Mutation/blockPluginCreationBySuperadmin.spec.ts index 3a146fb786..044b1907f3 100644 --- a/tests/resolvers/Mutation/blockPluginCreationBySuperadmin.spec.ts +++ b/tests/resolvers/Mutation/blockPluginCreationBySuperadmin.spec.ts @@ -24,8 +24,7 @@ import { blockPluginCreationBySuperadmin as blockPluginCreationBySuperadminResol import type { TestUserType } from "../../helpers/userAndOrg"; import { createTestUser } from "../../helpers/userAndOrg"; import { encryptEmail } from "../../../src/utilities/encryption"; -import bcrypt from "bcrypt"; - +import crypto from "crypto"; let testUser: TestUserType; let MONGOOSE_INSTANCE: typeof mongoose; @@ -66,12 +65,15 @@ describe("resolvers -> Mutation -> blockPluginCreationBySuperadmin", () => { }); it("throws error if user does not have AppUserProfile", async () => { const email = `email${nanoid().toLowerCase()}@gmail.com`; - const hashedEmail = bcrypt.hash(email, 12); + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); try { const newUser = await User.create({ email: encryptEmail(email), - hashedEmail: hashedEmail, + hashedEmail, password: `pass${nanoid().toLowerCase()}`, firstName: `firstName${nanoid().toLowerCase()}`, lastName: `lastName${nanoid().toLowerCase()}`, @@ -105,12 +107,15 @@ describe("resolvers -> Mutation -> blockPluginCreationBySuperadmin", () => { }); it("throws error if current appUser does not have AppUserProfile", async () => { const email = `email${nanoid().toLowerCase()}@gmail.com`; - const hashedEmail = bcrypt.hash(email, 12); + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); try { const newUser = await User.create({ email: encryptEmail(email), - hashedEmail: hashedEmail, + hashedEmail, password: `pass${nanoid().toLowerCase()}`, firstName: `firstName${nanoid().toLowerCase()}`, lastName: `lastName${nanoid().toLowerCase()}`, diff --git a/tests/resolvers/Mutation/createAdmin.spec.ts b/tests/resolvers/Mutation/createAdmin.spec.ts index 81de438caf..af2553dc2d 100644 --- a/tests/resolvers/Mutation/createAdmin.spec.ts +++ b/tests/resolvers/Mutation/createAdmin.spec.ts @@ -21,8 +21,7 @@ import type { } from "../../helpers/userAndOrg"; import { createTestUserAndOrganization } from "../../helpers/userAndOrg"; import { encryptEmail } from "../../../src/utilities/encryption"; -import bcrypt from "bcrypt"; - +import crypto from "crypto"; let testUser: TestUserType; let testOrganization: TestOrganizationType; let MONGOOSE_INSTANCE: typeof mongoose; @@ -153,11 +152,14 @@ describe("resolvers -> Mutation -> createAdmin", () => { }; const email = `email${nanoid().toLowerCase()}@gmail.com`; - const hashedEmail = bcrypt.hash(email, 12); + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); const newUser = await User.create({ email: encryptEmail(email), - hashedEmail: hashedEmail, + hashedEmail, password: `pass${nanoid().toLowerCase()}`, firstName: `firstName${nanoid().toLowerCase()}`, lastName: `lastName${nanoid().toLowerCase()}`, @@ -179,8 +181,10 @@ describe("resolvers -> Mutation -> createAdmin", () => { }); it("throws error if user does not exists", async () => { const email = `email${nanoid().toLowerCase()}@gmail.com`; - const hashedEmail = bcrypt.hash(email, 12); - + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); const newUser = await User.create({ email: encryptEmail(email), hashedEmail: hashedEmail, diff --git a/tests/resolvers/Mutation/createMember.spec.ts b/tests/resolvers/Mutation/createMember.spec.ts index 8d496a059e..3807474fe0 100644 --- a/tests/resolvers/Mutation/createMember.spec.ts +++ b/tests/resolvers/Mutation/createMember.spec.ts @@ -20,8 +20,7 @@ import type { } from "../../helpers/userAndOrg"; import { createTestUserAndOrganization } from "../../helpers/userAndOrg"; import { encryptEmail } from "../../../src/utilities/encryption"; -import bcrypt from "bcrypt"; - +import crypto from "crypto"; let testUser: TestUserType; let testOrganization: TestOrganizationType; let MONGOOSE_INSTANCE: typeof mongoose; @@ -206,10 +205,15 @@ describe("resolvers -> Mutation -> createAdmin", () => { }, ); const email = `email2${nanoid().toLowerCase()}@gmail.com`; - const hashedemail = bcrypt.hash(email, 12); + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); + const testUser2 = await User.create({ email: encryptEmail(email), - password: hashedemail, + hashedEmail, + password: `pass2${nanoid().toLowerCase()}`, firstName: `firstName2${nanoid().toLowerCase()}`, lastName: `lastName2${nanoid().toLowerCase()}`, image: null, diff --git a/tests/resolvers/Mutation/createMessageChat.spec.ts b/tests/resolvers/Mutation/createMessageChat.spec.ts index b5f89a43a1..7863169aa1 100644 --- a/tests/resolvers/Mutation/createMessageChat.spec.ts +++ b/tests/resolvers/Mutation/createMessageChat.spec.ts @@ -22,8 +22,7 @@ import { } from "../../../src/constants"; import type { TestUserType } from "../../helpers/userAndOrg"; import { encryptEmail } from "../../../src/utilities/encryption"; -import bcrypt from "bcrypt"; - +import crypto from "crypto"; let testUsers: TestUserType[]; // let testAppUserProfile: TestAppUserProfileType[]; let MONGOOSE_INSTANCE: typeof mongoose; @@ -128,7 +127,10 @@ describe("resolvers -> Mutation -> createMessageChat", () => { try { const email = `email${nanoid().toLowerCase()}@gmail.com`; - const hashedEmail = bcrypt.hash(email, 12); + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); const newUser = await User.create({ email: encryptEmail(email), hashedEmail: hashedEmail, @@ -167,7 +169,10 @@ describe("resolvers -> Mutation -> createMessageChat", () => { try { const email = "email${nanoid().toLowerCase()}@gmail.com"; - const hashedEmail = bcrypt.hash(email, 12); + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); const newUser = await User.create({ email: encryptEmail(email), hashedEmail: hashedEmail, diff --git a/tests/resolvers/Mutation/createSampleOrganization.spec.ts b/tests/resolvers/Mutation/createSampleOrganization.spec.ts index 3094439713..dc7bdf2875 100644 --- a/tests/resolvers/Mutation/createSampleOrganization.spec.ts +++ b/tests/resolvers/Mutation/createSampleOrganization.spec.ts @@ -21,7 +21,7 @@ import { } from "../../../src/constants"; import { connect, disconnect } from "../../helpers/db"; import { encryptEmail } from "../../../src/utilities/encryption"; -import bcrypt from "bcrypt"; +import crypto from "crypto"; let MONGOOSE_INSTANCE: typeof mongoose; @@ -134,8 +134,12 @@ describe("createSampleOrganization resolver", async () => { const spy = vi .spyOn(requestContext, "translate") .mockImplementationOnce((message) => `Translated ${message}`); + const email = `email${nanoid().toLowerCase()}@gmail.com`; - const hashedEmail = bcrypt.hash(email, 12); + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); const newUser = await User.create({ email: encryptEmail(email), diff --git a/tests/resolvers/Mutation/login.spec.ts b/tests/resolvers/Mutation/login.spec.ts index d3e50b8dd3..fc611a6e77 100644 --- a/tests/resolvers/Mutation/login.spec.ts +++ b/tests/resolvers/Mutation/login.spec.ts @@ -27,6 +27,7 @@ import { connect, disconnect } from "../../helpers/db"; import { createTestEventWithRegistrants } from "../../helpers/eventsWithRegistrants"; import type { TestUserType } from "../../helpers/userAndOrg"; import { decryptEmail, encryptEmail } from "../../../src/utilities/encryption"; +import crypto from "crypto"; let testUser: TestUserType; let MONGOOSE_INSTANCE: typeof mongoose; @@ -95,8 +96,10 @@ describe("resolvers -> Mutation -> login", () => { try { const email = `nonexistentuser${nanoid().toLowerCase()}@gmail.com`; - const hashedEmail = bcrypt.hash(email, 12); - // Create a new user with a unique email + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); // Create a new user with a unique email const newUser = await User.create({ email: encryptEmail(email), hashedEmail: hashedEmail, @@ -137,7 +140,10 @@ describe("resolvers -> Mutation -> login", () => { it("creates a new AppUserProfile for the user if it doesn't exist and associates it with the user", async () => { // Create a new user without an associated AppUserProfile const email = `email${nanoid().toLowerCase()}@gmail.com`; - const hashedEmail = bcrypt.hash(email, 12); + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); const newUser = await User.create({ email: encryptEmail(email), hashedEmail: hashedEmail, @@ -215,7 +221,10 @@ describe("resolvers -> Mutation -> login", () => { }); it("creates a appUserProfile of the user if does not exist", async () => { const email = `email${nanoid().toLowerCase()}@gmail.com`; - const hashedEmail = bcrypt.hash(email, 12); + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); const newUser = await User.create({ email: encryptEmail(email), hashedEmail: hashedEmail, diff --git a/tests/resolvers/Mutation/removeAdmin.spec.ts b/tests/resolvers/Mutation/removeAdmin.spec.ts index fcacc682e4..2bcd3e06cb 100644 --- a/tests/resolvers/Mutation/removeAdmin.spec.ts +++ b/tests/resolvers/Mutation/removeAdmin.spec.ts @@ -33,8 +33,7 @@ import { createTestUserAndOrganization, } from "../../helpers/userAndOrg"; import { encryptEmail } from "../../../src/utilities/encryption"; -import bcrypt from "bcrypt"; - +import crypto from "crypto"; let MONGOOSE_INSTANCE: typeof mongoose; let testUserRemoved: TestUserType; let testUserRemover: TestUserType; @@ -149,11 +148,13 @@ describe("resolvers -> Mutation -> removeAdmin", () => { .mockImplementationOnce((message) => message); try { const email = `email${nanoid().toLowerCase()}@gmail.com`; - const hashedEmail = bcrypt.hash(email, 12); - + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); const newUser = await User.create({ email: encryptEmail(email), - hashedEmail: hashedEmail, + hashedEmail, password: `pass${nanoid().toLowerCase()}`, firstName: `firstName${nanoid().toLowerCase()}`, lastName: `lastName${nanoid().toLowerCase()}`, @@ -192,11 +193,13 @@ describe("resolvers -> Mutation -> removeAdmin", () => { }, }; const email = `email${nanoid().toLowerCase()}@gmail.com`; - const hashedEmail = bcrypt.hash(email, 12); - + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); const newUser = await User.create({ email: encryptEmail(email), - hashedEmail: hashedEmail, + hashedEmail, password: `pass${nanoid().toLowerCase()}`, firstName: `firstName${nanoid().toLowerCase()}`, lastName: `lastName${nanoid().toLowerCase()}`, diff --git a/tests/resolvers/Mutation/resetCommunity.spec.ts b/tests/resolvers/Mutation/resetCommunity.spec.ts index 1aaec235d6..e9c9713f4a 100644 --- a/tests/resolvers/Mutation/resetCommunity.spec.ts +++ b/tests/resolvers/Mutation/resetCommunity.spec.ts @@ -22,8 +22,7 @@ import { AppUserProfile, Community, User } from "../../../src/models"; import { nanoid } from "nanoid"; import { resetCommunity } from "../../../src/resolvers/Mutation/resetCommunity"; import { encryptEmail } from "../../../src/utilities/encryption"; -import bcrypt from "bcrypt"; - +import crypto from "crypto"; let MONGOOSE_INSTANCE: typeof mongoose; let testUser1: TestUserType; let testUser2: TestUserType; @@ -85,7 +84,10 @@ describe("resolvers -> Mutation -> resetCommunity", () => { .mockImplementation((message) => `Translated ${message}`); const email = `email${nanoid().toLowerCase()}@gmail.com`; - const hashedEmail = bcrypt.hash(email, 12); + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); try { const newUser = await User.create({ diff --git a/tests/resolvers/Mutation/signUp.spec.ts b/tests/resolvers/Mutation/signUp.spec.ts index c68711a44a..2e925cd72b 100644 --- a/tests/resolvers/Mutation/signUp.spec.ts +++ b/tests/resolvers/Mutation/signUp.spec.ts @@ -27,8 +27,7 @@ import type { import { createTestUserAndOrganization } from "../../helpers/userAndOrg"; import _ from "lodash"; import { decryptEmail } from "../../../src/utilities/encryption"; -import bcrypt from "bcrypt"; - +import crypto from "crypto"; const testImagePath = `${nanoid().toLowerCase()}test.png`; let MONGOOSE_INSTANCE: typeof mongoose; let testUser: TestUserType; @@ -85,27 +84,17 @@ describe("resolvers -> Mutation -> signUp", () => { const signUpPayload = await signUpResolver?.({}, args, {}); - const allUsers = await User.find({}); - - let createdUser; - - for (const user of allUsers) { - try { - const { decrypted } = decryptEmail(user.email); - if (decrypted == email) { - createdUser = await User.findById(user._id) - .populate("joinedOrganizations") - .populate("registeredEvents") - .populate("membershipRequests") - .populate("organizationsBlockedBy") - .select("-password"); - - break; - } - } catch (error) { - console.error("Error decrypting email:", error); - } - } + const email = `email${nanoid().toLowerCase()}@gmail.com`; + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); + const createdUser = await User.findOne({ hashedEmail: hashedEmail }) + .populate("joinedOrganizations") + .populate("registeredEvents") + .populate("membershipRequests") + .populate("organizationsBlockedBy") + .select("-password"); const createdUserAppProfile = await AppUserProfile.findOne({ userId: createdUser?._id, @@ -132,8 +121,10 @@ describe("resolvers -> Mutation -> signUp", () => { ); const email = `email${nanoid().toLowerCase()}@gmail.com`; - const hashedEmail = bcrypt.hash(email, 12); - + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); const args: MutationSignUpArgs = { data: { email, @@ -160,11 +151,12 @@ describe("resolvers -> Mutation -> signUp", () => { it(`Promotes the user to SUPER ADMIN if the email registering with is same that as provided in configuration file`, async () => { const email = LAST_RESORT_SUPERADMIN_EMAIL; if (!email) { - console.error("LAST_RESORT_SUPERADMIN_EMAIL is undefined"); - throw Error; + throw new Error("LAST_RESORT_SUPERADMIN_EMAIL is undefined"); } - const hashedEmail = bcrypt.hash(email, 12); - + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); const args: MutationSignUpArgs = { data: { email, @@ -189,8 +181,10 @@ describe("resolvers -> Mutation -> signUp", () => { }); it(`Check if the User is not being promoted to SUPER ADMIN automatically`, async () => { const email = `email${nanoid().toLowerCase()}@gmail.com`; - const hashedEmail = bcrypt.hash(email, 12); - + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); const args: MutationSignUpArgs = { data: { email, @@ -282,8 +276,10 @@ describe("resolvers -> Mutation -> signUp", () => { }); it("creates user with joining the organization if userRegistrationRequired is false", async () => { const email = `email${nanoid().toLowerCase()}@gmail.com`; - const hashedEmail = bcrypt.hash(email, 12); - + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); const args: MutationSignUpArgs = { data: { email, @@ -323,8 +319,10 @@ describe("resolvers -> Mutation -> signUp", () => { visibleInSearch: false, }); - const hashedEmail = bcrypt.hash(email, 12); - + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); const args: MutationSignUpArgs = { data: { email, @@ -355,8 +353,10 @@ describe("resolvers -> Mutation -> signUp", () => { }); it("creates appUserProfile with userId === createdUser._id", async () => { const email = `email${nanoid().toLowerCase()}@gmail.com`; - const hashedEmail = bcrypt.hash(email, 12); - + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); const args: MutationSignUpArgs = { data: { email, diff --git a/tests/resolvers/Mutation/updateUserPassword.spec.ts b/tests/resolvers/Mutation/updateUserPassword.spec.ts index 89bffe36b1..1972299858 100644 --- a/tests/resolvers/Mutation/updateUserPassword.spec.ts +++ b/tests/resolvers/Mutation/updateUserPassword.spec.ts @@ -24,6 +24,7 @@ import { import { updateUserPassword as updateUserPasswordResolver } from "../../../src/resolvers/Mutation/updateUserPassword"; import { createTestUser, type TestUserType } from "../../helpers/userAndOrg"; import { encryptEmail } from "../../../src/utilities/encryption"; +import crypto from "crypto"; let MONGOOSE_INSTANCE: typeof mongoose; let testUser: TestUserType; @@ -39,7 +40,10 @@ beforeAll(async () => { hashedPassword = await bcrypt.hash("password", 12); const email = `email${nanoid().toLowerCase()}@gmail.com`; - const hashedEmail = bcrypt.hash(email, 12); + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); testUser = await User.create({ email: encryptEmail(email), hashedEmail: hashedEmail, diff --git a/tests/resolvers/Mutation/updateUserProfile.spec.ts b/tests/resolvers/Mutation/updateUserProfile.spec.ts index 9affbcedad..86b97f5c1d 100644 --- a/tests/resolvers/Mutation/updateUserProfile.spec.ts +++ b/tests/resolvers/Mutation/updateUserProfile.spec.ts @@ -26,8 +26,7 @@ import { updateUserProfile as updateUserProfileResolver } from "../../../src/res import { deleteUserFromCache } from "../../../src/services/UserCache/deleteUserFromCache"; import * as uploadEncodedImage from "../../../src/utilities/encodedImageStorage/uploadEncodedImage"; import { encryptEmail } from "../../../src/utilities/encryption"; -import bcrypt from "bcrypt"; - +import crypto from "crypto"; let MONGOOSE_INSTANCE: typeof mongoose; type UserDocument = InterfaceUser & @@ -45,8 +44,10 @@ beforeAll(async () => { MONGOOSE_INSTANCE = await connect(); const firstEmail = `email${nanoid().toLowerCase()}@gmail.com`; - const hashedFirstEmail = bcrypt.hash(firstEmail, 12); - + const hashedFirstEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); testUser = (await User.create({ email: encryptEmail(firstEmail), hashedEmail: hashedFirstEmail, @@ -77,8 +78,10 @@ beforeAll(async () => { }, })) as UserDocument; - const hashedSecondEmail = bcrypt.hash(email, 12); - + const hashedSecondEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); testUser2 = (await User.create({ email: encryptEmail(email), hashedEmail: hashedSecondEmail, diff --git a/tests/resolvers/Post/creator.spec.ts b/tests/resolvers/Post/creator.spec.ts index cf7f4c0e68..23725c0529 100644 --- a/tests/resolvers/Post/creator.spec.ts +++ b/tests/resolvers/Post/creator.spec.ts @@ -38,14 +38,9 @@ describe("resolvers -> Post -> creatorId", () => { _id: testPost!.creatorId, }).lean(); - expect(creatorIdObject).toBeDefined(); - if (!creatorIdObject) { - throw new Error("creatorIdObject is null or undefined"); - } - - expect(creatorIdObject.email).toBeDefined(); - if (!creatorIdObject.email) { - throw new Error("creatorIdObject.email is null or undefined"); + expect(creatorIdObject?.email).toBeDefined(); + if (creatorIdObject?.email == null) { + throw new Error("creatorIdObject or its email is null or undefined"); } try { diff --git a/tests/resolvers/Query/myLanguage.spec.ts b/tests/resolvers/Query/myLanguage.spec.ts index 9c8b643d6e..de6a6d86b8 100644 --- a/tests/resolvers/Query/myLanguage.spec.ts +++ b/tests/resolvers/Query/myLanguage.spec.ts @@ -9,8 +9,8 @@ import { connect, disconnect } from "../../helpers/db"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; import { createTestUser } from "../../helpers/userAndOrg"; -import bcrypt from "bcrypt"; - +import crypto from "crypto"; +import { encryptEmail } from "../../../src/utilities/encryption"; let MONGOOSE_INSTANCE: typeof mongoose; beforeAll(async () => { @@ -36,10 +36,13 @@ describe("resolvers -> Query -> myLanguage", () => { it(`returns current user's appLanguageCode`, async () => { const email = `email${nanoid().toLowerCase()}@gmail.com`; - const hashedEmail = bcrypt.hash(email, 12); + const hashedEmail = crypto + .createHash("sha256") + .update(email.toLowerCase() + process.env.HASH_PEPPER) + .digest("hex"); const testUser = await User.create({ - email: email, - hashedEmail: hashedEmail, + email: encryptEmail(email), + hashedEmail, password: "password", firstName: "firstName", lastName: "lastName",