Files
app/ts/reception/classes.registrationsession.ts
T

201 lines
6.1 KiB
TypeScript
Raw Permalink Normal View History

2024-09-29 13:56:38 +02:00
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<plugins.smartmail.IEmailValidationResult> {
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() {
if (!process.env.TEST_MODE) {
this.smsCode =
await this.registrationSessionManagerRef.receptionRef.loleSmsClientInstance.sendSmsVerifcation(
{
fromName: 'w...global',
toNumber: parseInt(this.collectedData.userData.mobileNumber),
}
);
} else {
console.log('Not sending SMS in automated test mode');
this.smsCode = '123456';
}
}
/**
* this method can be called when this registrationsession is validated
* and all data has been set
*/
public async manifestUserWithAccountData(): Promise<User> {
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);
}
}