feat: ability to enable/disable email confirmation from env variables

This commit is contained in:
Ahmed Bouhuolia
2024-05-03 18:30:19 +02:00
parent cb88c234d1
commit f4440c9a03
8 changed files with 39 additions and 28 deletions

View File

@@ -48,6 +48,9 @@ SIGNUP_DISABLED=false
SIGNUP_ALLOWED_DOMAINS= SIGNUP_ALLOWED_DOMAINS=
SIGNUP_ALLOWED_EMAILS= SIGNUP_ALLOWED_EMAILS=
# Sign-up Email Confirmation
SIGNUP_EMAIL_CONFIRMATION=false
# API rate limit (points,duration,block duration). # API rate limit (points,duration,block duration).
API_RATE_LIMIT=120,60,600 API_RATE_LIMIT=120,60,600

View File

@@ -220,11 +220,10 @@ export default class AuthenticationController extends BaseController {
} }
/** /**
* * Resends the confirmation email to the user.
* @param {Request} req * @param {Request} req
* @param {Response}| res * @param {Response}| res
* @param {Function} next * @param {Function} next
* @returns
*/ */
private async registerVerifyResendMail( private async registerVerifyResendMail(
req: Request, req: Request,
@@ -234,7 +233,7 @@ export default class AuthenticationController extends BaseController {
const { user } = req; const { user } = req;
try { try {
const data = await this.authApplication.signUpConfirm(user.id); const data = await this.authApplication.signUpConfirmResend(user.id);
return res.status(200).send({ return res.status(200).send({
type: 'success', type: 'success',

View File

@@ -153,6 +153,13 @@ module.exports = {
), ),
}, },
/**
* Sign-up email confirmation
*/
signupConfirmation: {
enabled: parseBoolean<boolean>(process.env.SIGNUP_EMAIL_CONFIRMATION, false),
},
/** /**
* Puppeteer remote browserless connection. * Puppeteer remote browserless connection.
*/ */

View File

@@ -1,4 +1,4 @@
import { Service, Inject, Container } from 'typedi'; import { Service, Inject } from 'typedi';
import { import {
IRegisterDTO, IRegisterDTO,
ISystemUser, ISystemUser,
@@ -13,11 +13,6 @@ import { AuthSignupConfirmService } from './AuthSignupConfirm';
import { SystemUser } from '@/system/models'; import { SystemUser } from '@/system/models';
import { AuthSignupConfirmResend } from './AuthSignupResend'; import { AuthSignupConfirmResend } from './AuthSignupResend';
interface ISignupConfirmDTO {
token: string;
email: string;
}
@Service() @Service()
export default class AuthenticationApplication { export default class AuthenticationApplication {
@Inject() @Inject()
@@ -72,13 +67,12 @@ export default class AuthenticationApplication {
} }
/** /**
* * Resends the confirmation email of the given system user.
* @param {string} email * @param {number} userId - System user id.
* @param {string} token * @returns {Promise<void>}
* @returns
*/ */
public async signUpConfirmSend(email: string, token: string) { public async signUpConfirmResend(userId: number) {
return this.authSignupConfirmService.signUpConfirm(email, token); return this.authSignUpConfirmResendService.signUpConfirmResend(userId);
} }
/** /**

View File

@@ -1,4 +1,4 @@
import { isEmpty, omit } from 'lodash'; import { defaultTo, isEmpty, omit } from 'lodash';
import moment from 'moment'; import moment from 'moment';
import crypto from 'crypto'; import crypto from 'crypto';
import { ServiceError } from '@/exceptions'; import { ServiceError } from '@/exceptions';
@@ -42,7 +42,13 @@ export class AuthSignupService {
await this.validateEmailUniqiness(signupDTO.email); await this.validateEmailUniqiness(signupDTO.email);
const hashedPassword = await hashPassword(signupDTO.password); 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. // Triggers signin up event.
await this.eventPublisher.emitAsync(events.auth.signingUp, { await this.eventPublisher.emitAsync(events.auth.signingUp, {
@@ -53,10 +59,11 @@ export class AuthSignupService {
const registeredUser = await systemUserRepository.create({ const registeredUser = await systemUserRepository.create({
...omit(signupDTO, 'country'), ...omit(signupDTO, 'country'),
verifyToken, verifyToken,
verified,
active: true, active: true,
password: hashedPassword, password: hashedPassword,
tenantId: tenant.id, tenantId: tenant.id,
inviteAcceptedAt: moment().format('YYYY-MM-DD'), inviteAcceptedAt,
}); });
// Triggers signed up event. // Triggers signed up event.
await this.eventPublisher.emitAsync(events.auth.signUp, { await this.eventPublisher.emitAsync(events.auth.signUp, {

View File

@@ -52,6 +52,6 @@ export class AuthSignupConfirmService {
userId, userId,
} as IAuthSignUpVerifiedEventPayload); } as IAuthSignUpVerifiedEventPayload);
return updatedUser; return updatedUser as SystemUser;
} }
} }

View File

@@ -1,6 +1,6 @@
import { Inject, Service } from 'typedi';
import { ServiceError } from '@/exceptions'; import { ServiceError } from '@/exceptions';
import { SystemUser } from '@/system/models'; import { SystemUser } from '@/system/models';
import { Inject, Service } from 'typedi';
import { ERRORS } from './_constants'; import { ERRORS } from './_constants';
@Service() @Service()
@@ -9,18 +9,19 @@ export class AuthSignupConfirmResend {
private agenda: any; private agenda: any;
/** /**
* * Resends the email confirmation of the given user.
* @param {number} tenantId * @param {number} userId - User ID.
* @param {string} email * @returns {Promise<void>}
*/ */
public async signUpConfirmResend(userId: number) { public async signUpConfirmResend(userId: number) {
const user = await SystemUser.query().findById(userId).throwIfNotFound(); const user = await SystemUser.query().findById(userId).throwIfNotFound();
// // Throw error if the user is already verified.
if (user.verified) { if (user.verified) {
throw new ServiceError(ERRORS.USER_ALREADY_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); throw new ServiceError(ERRORS.USER_ALREADY_VERIFIED);
} }
const payload = { const payload = {

View File

@@ -1,6 +1,6 @@
import bcrypt from 'bcryptjs'; import bcrypt from 'bcryptjs';
import moment from 'moment'; import moment from 'moment';
import _ from 'lodash'; import _, { isEmpty } from 'lodash';
import path from 'path'; import path from 'path';
import * as R from 'ramda'; import * as R from 'ramda';
@@ -329,7 +329,7 @@ const booleanValuesRepresentingTrue: string[] = ['true', '1'];
const booleanValuesRepresentingFalse: string[] = ['false', '0']; const booleanValuesRepresentingFalse: string[] = ['false', '0'];
const normalizeValue = (value: any): string => const normalizeValue = (value: any): string =>
value.toString().trim().toLowerCase(); value?.toString().trim().toLowerCase();
const booleanValues: string[] = [ const booleanValues: string[] = [
...booleanValuesRepresentingTrue, ...booleanValuesRepresentingTrue,
@@ -338,7 +338,7 @@ const booleanValues: string[] = [
export const parseBoolean = <T>(value: any, defaultValue: T): T | boolean => { export const parseBoolean = <T>(value: any, defaultValue: T): T | boolean => {
const normalizedValue = normalizeValue(value); const normalizedValue = normalizeValue(value);
if (booleanValues.indexOf(normalizedValue) === -1) { if (isEmpty(value) || booleanValues.indexOf(normalizedValue) === -1) {
return defaultValue; return defaultValue;
} }
return booleanValuesRepresentingTrue.indexOf(normalizedValue) !== -1; return booleanValuesRepresentingTrue.indexOf(normalizedValue) !== -1;