From f4440c9a03fdae4b63b0f34e444357f2518aca7d Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Fri, 3 May 2024 18:30:19 +0200 Subject: [PATCH] feat: ability to enable/disable email confirmation from env variables --- .env.example | 3 +++ .../src/api/controllers/Authentication.ts | 5 ++--- packages/server/src/config/index.ts | 7 +++++++ .../services/Authentication/AuthApplication.ts | 18 ++++++------------ .../src/services/Authentication/AuthSignup.ts | 13 ++++++++++--- .../Authentication/AuthSignupConfirm.ts | 2 +- .../Authentication/AuthSignupResend.ts | 13 +++++++------ packages/server/src/utils/index.ts | 6 +++--- 8 files changed, 39 insertions(+), 28 deletions(-) diff --git a/.env.example b/.env.example index 7621a6a09..dc38bf3e3 100644 --- a/.env.example +++ b/.env.example @@ -48,6 +48,9 @@ SIGNUP_DISABLED=false SIGNUP_ALLOWED_DOMAINS= SIGNUP_ALLOWED_EMAILS= +# Sign-up Email Confirmation +SIGNUP_EMAIL_CONFIRMATION=false + # API rate limit (points,duration,block duration). API_RATE_LIMIT=120,60,600 diff --git a/packages/server/src/api/controllers/Authentication.ts b/packages/server/src/api/controllers/Authentication.ts index 87ac2166c..ff6576bec 100644 --- a/packages/server/src/api/controllers/Authentication.ts +++ b/packages/server/src/api/controllers/Authentication.ts @@ -220,11 +220,10 @@ export default class AuthenticationController extends BaseController { } /** - * + * Resends the confirmation email to the user. * @param {Request} req * @param {Response}| res * @param {Function} next - * @returns */ private async registerVerifyResendMail( req: Request, @@ -234,7 +233,7 @@ export default class AuthenticationController extends BaseController { const { user } = req; try { - const data = await this.authApplication.signUpConfirm(user.id); + const data = await this.authApplication.signUpConfirmResend(user.id); return res.status(200).send({ type: 'success', diff --git a/packages/server/src/config/index.ts b/packages/server/src/config/index.ts index 6a61da3ca..36dbfd831 100644 --- a/packages/server/src/config/index.ts +++ b/packages/server/src/config/index.ts @@ -153,6 +153,13 @@ module.exports = { ), }, + /** + * Sign-up email confirmation + */ + signupConfirmation: { + enabled: parseBoolean(process.env.SIGNUP_EMAIL_CONFIRMATION, false), + }, + /** * Puppeteer remote browserless connection. */ diff --git a/packages/server/src/services/Authentication/AuthApplication.ts b/packages/server/src/services/Authentication/AuthApplication.ts index 47f2168a3..73f18941f 100644 --- a/packages/server/src/services/Authentication/AuthApplication.ts +++ b/packages/server/src/services/Authentication/AuthApplication.ts @@ -1,4 +1,4 @@ -import { Service, Inject, Container } from 'typedi'; +import { Service, Inject } from 'typedi'; import { IRegisterDTO, ISystemUser, @@ -13,11 +13,6 @@ import { AuthSignupConfirmService } from './AuthSignupConfirm'; import { SystemUser } from '@/system/models'; import { AuthSignupConfirmResend } from './AuthSignupResend'; -interface ISignupConfirmDTO { - token: string; - email: string; -} - @Service() export default class AuthenticationApplication { @Inject() @@ -72,13 +67,12 @@ export default class AuthenticationApplication { } /** - * - * @param {string} email - * @param {string} token - * @returns + * Resends the confirmation email of the given system user. + * @param {number} userId - System user id. + * @returns {Promise} */ - public async signUpConfirmSend(email: string, token: string) { - return this.authSignupConfirmService.signUpConfirm(email, token); + public async signUpConfirmResend(userId: number) { + return this.authSignUpConfirmResendService.signUpConfirmResend(userId); } /** diff --git a/packages/server/src/services/Authentication/AuthSignup.ts b/packages/server/src/services/Authentication/AuthSignup.ts index 2be8c63a2..17c7f574c 100644 --- a/packages/server/src/services/Authentication/AuthSignup.ts +++ b/packages/server/src/services/Authentication/AuthSignup.ts @@ -1,4 +1,4 @@ -import { isEmpty, omit } from 'lodash'; +import { defaultTo, isEmpty, omit } from 'lodash'; import moment from 'moment'; import crypto from 'crypto'; import { ServiceError } from '@/exceptions'; @@ -42,7 +42,13 @@ export class AuthSignupService { await this.validateEmailUniqiness(signupDTO.email); const hashedPassword = await hashPassword(signupDTO.password); - const verifyToken = crypto.randomBytes(64).toString('hex'); + + const verifyTokenCrypto = crypto.randomBytes(64).toString('hex'); + const verifiedEnabed = defaultTo(config.signupConfirmation.enabled, false); + const verifyToken = verifiedEnabed ? verifyTokenCrypto : ''; + const verified = !verifiedEnabed; + + const inviteAcceptedAt = moment().format('YYYY-MM-DD'); // Triggers signin up event. await this.eventPublisher.emitAsync(events.auth.signingUp, { @@ -53,10 +59,11 @@ export class AuthSignupService { const registeredUser = await systemUserRepository.create({ ...omit(signupDTO, 'country'), verifyToken, + verified, active: true, password: hashedPassword, tenantId: tenant.id, - inviteAcceptedAt: moment().format('YYYY-MM-DD'), + inviteAcceptedAt, }); // Triggers signed up event. await this.eventPublisher.emitAsync(events.auth.signUp, { diff --git a/packages/server/src/services/Authentication/AuthSignupConfirm.ts b/packages/server/src/services/Authentication/AuthSignupConfirm.ts index 940b6ae7f..b08a4bd2e 100644 --- a/packages/server/src/services/Authentication/AuthSignupConfirm.ts +++ b/packages/server/src/services/Authentication/AuthSignupConfirm.ts @@ -52,6 +52,6 @@ export class AuthSignupConfirmService { userId, } as IAuthSignUpVerifiedEventPayload); - return updatedUser; + return updatedUser as SystemUser; } } diff --git a/packages/server/src/services/Authentication/AuthSignupResend.ts b/packages/server/src/services/Authentication/AuthSignupResend.ts index 5c764e80c..8edb08f35 100644 --- a/packages/server/src/services/Authentication/AuthSignupResend.ts +++ b/packages/server/src/services/Authentication/AuthSignupResend.ts @@ -1,6 +1,6 @@ +import { Inject, Service } from 'typedi'; import { ServiceError } from '@/exceptions'; import { SystemUser } from '@/system/models'; -import { Inject, Service } from 'typedi'; import { ERRORS } from './_constants'; @Service() @@ -9,18 +9,19 @@ export class AuthSignupConfirmResend { private agenda: any; /** - * - * @param {number} tenantId - * @param {string} email + * Resends the email confirmation of the given user. + * @param {number} userId - User ID. + * @returns {Promise} */ public async signUpConfirmResend(userId: number) { const user = await SystemUser.query().findById(userId).throwIfNotFound(); - // + // Throw error if the user is already verified. if (user.verified) { throw new ServiceError(ERRORS.USER_ALREADY_VERIFIED); } - if (user.verifyToken) { + // Throw error if the verification token is not exist. + if (!user.verifyToken) { throw new ServiceError(ERRORS.USER_ALREADY_VERIFIED); } const payload = { diff --git a/packages/server/src/utils/index.ts b/packages/server/src/utils/index.ts index 919cd7af7..ceef5f572 100644 --- a/packages/server/src/utils/index.ts +++ b/packages/server/src/utils/index.ts @@ -1,6 +1,6 @@ import bcrypt from 'bcryptjs'; import moment from 'moment'; -import _ from 'lodash'; +import _, { isEmpty } from 'lodash'; import path from 'path'; import * as R from 'ramda'; @@ -329,7 +329,7 @@ const booleanValuesRepresentingTrue: string[] = ['true', '1']; const booleanValuesRepresentingFalse: string[] = ['false', '0']; const normalizeValue = (value: any): string => - value.toString().trim().toLowerCase(); + value?.toString().trim().toLowerCase(); const booleanValues: string[] = [ ...booleanValuesRepresentingTrue, @@ -338,7 +338,7 @@ const booleanValues: string[] = [ export const parseBoolean = (value: any, defaultValue: T): T | boolean => { const normalizedValue = normalizeValue(value); - if (booleanValues.indexOf(normalizedValue) === -1) { + if (isEmpty(value) || booleanValues.indexOf(normalizedValue) === -1) { return defaultValue; } return booleanValuesRepresentingTrue.indexOf(normalizedValue) !== -1;