import * as plugins from '../plugins.js'; import { RegistrationSessionManager } from './classes.registrationsessionmanager.js'; import { logger } from './logging.js'; import { User } from './classes.user.js'; /** * a RegistrationSession is a in memory session for signing up */ export class RegistrationSession { // ====== // STATIC // ====== public static async createRegistrationSessionForEmail( registrationSessionManageremailArg: RegistrationSessionManager, emailArg: string ) { const newRegistrationSession = new RegistrationSession( registrationSessionManageremailArg, emailArg ); const emailValidationResult = await newRegistrationSession .validateEMailAddress() .catch((error) => { throw new plugins.typedrequest.TypedResponseError( 'Error occured during email provider & dns validation' ); }); if (!emailValidationResult?.valid) { newRegistrationSession.destroy(); throw new plugins.typedrequest.TypedResponseError( 'Email Address is not valid. Please use a correctly formated email address' ); } if (emailValidationResult.disposable) { newRegistrationSession.destroy(); throw new plugins.typedrequest.TypedResponseError( 'Email is disposable. Please use a non disposable email address.' ); } console.log( `${newRegistrationSession.emailAddress} is valid. Continuing registration process!` ); await newRegistrationSession.sendTokenValidationEmail(); console.log(`Successfully sent email validation email`); return newRegistrationSession; } // ======== // INSTANCE // ======== public registrationSessionManagerRef: RegistrationSessionManager; public emailAddress: string; /** * only used during testing */ public unhashedEmailToken?: string; public hashedEmailToken: string; private smsvalidationCounter = 0; public smsCode: string; /** * the status of the registration. should progress in a linear fashion. */ public status: 'announced' | 'emailValidated' | 'mobileVerified' | 'registered' | 'failed' = 'announced'; public collectedData: { userData: plugins.lointReception.data.IUser['data']; } = { userData: { username: null, connectedOrgs: [], email: null, name: null, status: null, mobileNumber: null, password: null, passwordHash: null, }, }; constructor( registrationSessionManagerRefArg: RegistrationSessionManager, emailAddressArg: string ) { this.registrationSessionManagerRef = registrationSessionManagerRefArg; this.emailAddress = emailAddressArg; this.registrationSessionManagerRef.registrationSessions.addToMap(this.emailAddress, this); // lets destroy this after 10 minutes, // works in unrefed mode so not blocking node exiting. plugins.smartdelay.delayFor(600000, null, true).then(() => this.destroy()); } /** * validates a token by comparing its hash against the stored hashed token * @param tokenArg */ public validateEmailToken(tokenArg: string): boolean { const result = this.hashedEmailToken === plugins.smarthash.sha256FromStringSync(tokenArg); if (result && this.status === 'announced') { this.status = 'emailValidated'; this.collectedData.userData.email = this.emailAddress; } if (!result && this.status === 'announced') { this.status = 'failed'; } return result; } /** validates the sms code */ public validateSmsCode(smsCodeArg: string) { this.smsvalidationCounter++; const result = this.smsCode === smsCodeArg; if (this.status === 'emailValidated' && result) { this.status = 'mobileVerified'; return result; } else { if (this.smsvalidationCounter === 5) { this.destroy(); throw new plugins.typedrequest.TypedResponseError( 'Registration cancelled due to repeated wrong verification code submission' ); } return false; } } /** * validate the email address with provider and dns sanity checks * @returns */ public async validateEMailAddress(): Promise { console.log(`validating email ${this.emailAddress}`); const result = await new plugins.smartmail.EmailAddressValidator().validate(this.emailAddress); return result; } /** * send the validation email */ public async sendTokenValidationEmail() { const uuidToSend = plugins.smartunique.uuid4(); this.unhashedEmailToken = uuidToSend; this.hashedEmailToken = plugins.smarthash.sha256FromStringSync(uuidToSend); this.registrationSessionManagerRef.receptionRef.receptionMailer.sendRegistrationEmail( this, uuidToSend ); logger.log('info', `sent a validation email with a verification code to ${this.emailAddress}`); } /** * validate the mobile number of someone */ public async sendValidationSms() { this.smsCode = await this.registrationSessionManagerRef.receptionRef.szPlatformClient.smsConnector.sendSmsVerifcation( { fromName: this.registrationSessionManagerRef.receptionRef.options.name, toNumber: parseInt(this.collectedData.userData.mobileNumber), } ); } /** * this method can be called when this registrationsession is validated * and all data has been set */ public async manifestUserWithAccountData(): Promise { if (this.status !== 'mobileVerified') { throw new plugins.typedrequest.TypedResponseError( 'You can only manifest user that have a validated email Address and Mobile Number' ); } if (!this.collectedData) { throw new Error('You have to set the accountdata first'); } const manifestedUser = await this.registrationSessionManagerRef.receptionRef.userManager.CUser.createNewUserForUserData( this.collectedData.userData ); return manifestedUser; } /** * destroys the registrationsession */ public destroy() { this.registrationSessionManagerRef.registrationSessions.removeFromMap(this.emailAddress); } }