297 lines
7.8 KiB
TypeScript
297 lines
7.8 KiB
TypeScript
import { Request, Response, Router } from 'express';
|
|
import { check, ValidationChain } from 'express-validator';
|
|
import { Service, Inject } from 'typedi';
|
|
import BaseController from '@/api/controllers/BaseController';
|
|
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
|
import { ILoginDTO, ISystemUser, IRegisterDTO } from '@/interfaces';
|
|
import { ServiceError, ServiceErrors } from '@/exceptions';
|
|
import { DATATYPES_LENGTH } from '@/data/DataTypes';
|
|
import LoginThrottlerMiddleware from '@/api/middleware/LoginThrottlerMiddleware';
|
|
import AuthenticationApplication from '@/services/Authentication/AuthApplication';
|
|
|
|
@Service()
|
|
export default class AuthenticationController extends BaseController {
|
|
@Inject()
|
|
private authApplication: AuthenticationApplication;
|
|
|
|
/**
|
|
* Constructor method.
|
|
*/
|
|
public router() {
|
|
const router = Router();
|
|
|
|
router.post(
|
|
'/login',
|
|
this.loginSchema,
|
|
this.validationResult,
|
|
LoginThrottlerMiddleware,
|
|
asyncMiddleware(this.login.bind(this)),
|
|
this.handlerErrors
|
|
);
|
|
router.post(
|
|
'/register',
|
|
this.registerSchema,
|
|
this.validationResult,
|
|
asyncMiddleware(this.register.bind(this)),
|
|
this.handlerErrors
|
|
);
|
|
router.post(
|
|
'/send_reset_password',
|
|
this.sendResetPasswordSchema,
|
|
this.validationResult,
|
|
asyncMiddleware(this.sendResetPassword.bind(this)),
|
|
this.handlerErrors
|
|
);
|
|
router.post(
|
|
'/reset/:token',
|
|
this.resetPasswordSchema,
|
|
this.validationResult,
|
|
asyncMiddleware(this.resetPassword.bind(this)),
|
|
this.handlerErrors
|
|
);
|
|
router.get('/meta', asyncMiddleware(this.getAuthMeta.bind(this)));
|
|
return router;
|
|
}
|
|
|
|
/**
|
|
* Login validation schema.
|
|
* @returns {ValidationChain[]}
|
|
*/
|
|
private get loginSchema(): ValidationChain[] {
|
|
return [
|
|
check('crediential').exists().isEmail(),
|
|
check('password').exists().isLength({ min: 5 }),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Register validation schema.
|
|
* @returns {ValidationChain[]}
|
|
*/
|
|
private get registerSchema(): ValidationChain[] {
|
|
return [
|
|
check('first_name')
|
|
.exists()
|
|
.isString()
|
|
.trim()
|
|
.escape()
|
|
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
|
check('last_name')
|
|
.exists()
|
|
.isString()
|
|
.trim()
|
|
.escape()
|
|
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
|
check('email')
|
|
.exists()
|
|
.isString()
|
|
.isEmail()
|
|
.trim()
|
|
.escape()
|
|
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
|
check('password')
|
|
.exists()
|
|
.isString()
|
|
.isLength({ min: 6 })
|
|
.trim()
|
|
.escape()
|
|
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Reset password schema.
|
|
* @returns {ValidationChain[]}
|
|
*/
|
|
private get resetPasswordSchema(): ValidationChain[] {
|
|
return [
|
|
check('password')
|
|
.exists()
|
|
.isLength({ min: 6 })
|
|
.custom((value, { req }) => {
|
|
if (value !== req.body.confirm_password) {
|
|
throw new Error("Passwords don't match");
|
|
} else {
|
|
return value;
|
|
}
|
|
}),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Send reset password validation schema.
|
|
* @returns {ValidationChain[]}
|
|
*/
|
|
private get sendResetPasswordSchema(): ValidationChain[] {
|
|
return [check('email').exists().isEmail().trim().escape()];
|
|
}
|
|
|
|
/**
|
|
* Handle user login.
|
|
* @param {Request} req
|
|
* @param {Response} res
|
|
*/
|
|
private async login(req: Request, res: Response, next: Function): Response {
|
|
const userDTO: ILoginDTO = this.matchedBodyData(req);
|
|
|
|
try {
|
|
const { token, user, tenant } = await this.authApplication.signIn(
|
|
userDTO.crediential,
|
|
userDTO.password
|
|
);
|
|
return res.status(200).send({ token, user, tenant });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Organization register handler.
|
|
* @param {Request} req
|
|
* @param {Response} res
|
|
*/
|
|
private async register(req: Request, res: Response, next: Function) {
|
|
const registerDTO: IRegisterDTO = this.matchedBodyData(req);
|
|
|
|
try {
|
|
await this.authApplication.signUp(registerDTO);
|
|
|
|
return res.status(200).send({
|
|
type: 'success',
|
|
code: 'REGISTER.SUCCESS',
|
|
message: 'Register organization has been success.',
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send reset password handler
|
|
* @param {Request} req
|
|
* @param {Response} res
|
|
*/
|
|
private async sendResetPassword(req: Request, res: Response, next: Function) {
|
|
const { email } = this.matchedBodyData(req);
|
|
|
|
try {
|
|
await this.authApplication.sendResetPassword(email);
|
|
|
|
return res.status(200).send({
|
|
code: 'SEND_RESET_PASSWORD_SUCCESS',
|
|
message: 'The reset password message has been sent successfully.',
|
|
});
|
|
} catch (error) {
|
|
if (error instanceof ServiceError) {
|
|
}
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reset password handler
|
|
* @param {Request} req
|
|
* @param {Response} res
|
|
*/
|
|
private async resetPassword(req: Request, res: Response, next: Function) {
|
|
const { token } = req.params;
|
|
const { password } = req.body;
|
|
|
|
try {
|
|
await this.authApplication.resetPassword(token, password);
|
|
|
|
return res.status(200).send({
|
|
type: 'RESET_PASSWORD_SUCCESS',
|
|
message: 'The password has been reset successfully.',
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieves the authentication meta for SPA.
|
|
* @param {Request} req
|
|
* @param {Response} res
|
|
* @param {Function} next
|
|
* @returns {Response|void}
|
|
*/
|
|
private async getAuthMeta(req: Request, res: Response, next: Function) {
|
|
try {
|
|
const meta = await this.authApplication.getAuthMeta();
|
|
|
|
return res.status(200).send({ meta });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles the service errors.
|
|
*/
|
|
private handlerErrors(error, req: Request, res: Response, next: Function) {
|
|
if (error instanceof ServiceError) {
|
|
if (
|
|
['INVALID_DETAILS', 'invalid_password'].indexOf(error.errorType) !== -1
|
|
) {
|
|
return res.boom.badRequest(null, {
|
|
errors: [{ type: 'INVALID_DETAILS', code: 100 }],
|
|
});
|
|
}
|
|
if (error.errorType === 'USER_INACTIVE') {
|
|
return res.boom.badRequest(null, {
|
|
errors: [{ type: 'USER_INACTIVE', code: 200 }],
|
|
});
|
|
}
|
|
if (
|
|
error.errorType === 'TOKEN_INVALID' ||
|
|
error.errorType === 'TOKEN_EXPIRED'
|
|
) {
|
|
return res.boom.badRequest(null, {
|
|
errors: [{ type: 'TOKEN_INVALID', code: 300 }],
|
|
});
|
|
}
|
|
if (error.errorType === 'USER_NOT_FOUND') {
|
|
return res.boom.badRequest(null, {
|
|
errors: [{ type: 'USER_NOT_FOUND', code: 400 }],
|
|
});
|
|
}
|
|
if (error.errorType === 'EMAIL_NOT_FOUND') {
|
|
return res.status(400).send({
|
|
errors: [{ type: 'EMAIL.NOT.REGISTERED', code: 500 }],
|
|
});
|
|
}
|
|
if (error.errorType === 'EMAIL_EXISTS') {
|
|
return res.status(400).send({
|
|
errors: [{ type: 'EMAIL.EXISTS', code: 600 }],
|
|
});
|
|
}
|
|
if (error.errorType === 'SIGNUP_RESTRICTED') {
|
|
return res.status(400).send({
|
|
errors: [
|
|
{
|
|
type: 'SIGNUP_RESTRICTED',
|
|
message:
|
|
'Sign-up is restricted no one can sign-up to the system.',
|
|
code: 700,
|
|
},
|
|
],
|
|
});
|
|
}
|
|
if (error.errorType === 'SIGNUP_RESTRICTED_NOT_ALLOWED') {
|
|
return res.status(400).send({
|
|
errors: [
|
|
{
|
|
type: 'SIGNUP_RESTRICTED_NOT_ALLOWED',
|
|
message:
|
|
'Sign-up is restricted the given email address is not allowed to sign-up.',
|
|
code: 710,
|
|
},
|
|
],
|
|
});
|
|
}
|
|
}
|
|
next(error);
|
|
}
|
|
}
|