174 lines
5.7 KiB
JavaScript
174 lines
5.7 KiB
JavaScript
const
|
|
env = require('../helpers/env'),
|
|
jwt = require('../helpers/jwt_async.js'),
|
|
utils = require('../helpers/utils.js'),
|
|
cache = require('../helpers/mem_cache'),
|
|
subUtil = require('../helpers/subscription_util'),
|
|
error = require('../helpers/error'),
|
|
{ Errors, Fields } = require('../helpers/constants'),
|
|
{ SubType, SubFields } = require('../model/subscription'),
|
|
Vehicle = require('../model/vehicle'),
|
|
ObjectId = require('mongodb').ObjectId;
|
|
|
|
function getRoutePath(req) {
|
|
return req && req.baseUrl + req.path;
|
|
}
|
|
|
|
function isSecuredRoute(routePath) {
|
|
const nonSecurePaths = ['/login', '/siteVer', '/mailPwdReset', '/resetPassword'];
|
|
return (routePath && !nonSecurePaths.includes(routePath.substring(routePath.lastIndexOf('/'))));
|
|
}
|
|
|
|
/** Check on every request and authenticate and authorize users. This middleware must be call first to ensure user authentication and related user session data */
|
|
async function checkUser(req, res, next) {
|
|
// console.log(req.url);
|
|
if (!isSecuredRoute(getRoutePath(req))) return next();
|
|
|
|
const token = req.headers.authorization ? req.headers.authorization.split(' ') : null;
|
|
if (!token || token.length !== 2 || token[0].toLowerCase() !== 'bearer')
|
|
return error.handleResErr(res, Errors.NO_ACCESS, 401);
|
|
|
|
const bearer = token[1];
|
|
try {
|
|
|
|
const decodedToken = await jwt.verifyAsync(bearer, env.TOKEN_SECRET);
|
|
|
|
/* Setup for current logged in user */
|
|
req.uid = decodedToken.uid; // Set req User id
|
|
if (!ObjectId.isValid(req.uid))
|
|
return error.handleResErr(res, Errors.NO_ACCESS, 401);
|
|
req.ut = decodedToken.ut; // Set req User type
|
|
|
|
// Rebuild the in-memory cache for this user in case of server restarted or the user's cache has expired
|
|
const userInfo = cache.get(req.uid);
|
|
if (userInfo && userInfo[Fields.MARKED_DELETE]) {
|
|
return error.handleResErr(res, err, 401);
|
|
} else if (!userInfo || cache.isExpired(req.uid, env.MAX_SESSION_SECS)) {
|
|
const curUserInfo = await cache.loadUser(req.uid);
|
|
|
|
if (!curUserInfo && curUserInfo[Fields.MARKED_DELETE]) {
|
|
return error.handleResErr(res, err, 401);
|
|
}
|
|
|
|
req.userInfo = curUserInfo;
|
|
return next();
|
|
} else {
|
|
req.userInfo = userInfo;
|
|
return next();
|
|
}
|
|
} catch (err) {
|
|
if (err.name && err.name === 'TokenExpiredError') {
|
|
const decodedPkg = await jwt.decodeAsync(bearer);
|
|
// Handle the case the user logout when token already expired => should still allow clearing temp data
|
|
if (req.path.endsWith('/clearTempData')) {
|
|
req.uid = decodedPkg.uid;
|
|
if (!ObjectId.isValid(req.uid))
|
|
return error.handleResErr(res, Errors.NO_ACCESS, 401);
|
|
req.ut = decodedPkg.ut;
|
|
|
|
return next();
|
|
}
|
|
return error.handleResErr(res, Errors.TOKEN_EXPIRED, 401);
|
|
} else {
|
|
return error.handleResErr(res, Errors.NO_ACCESS, 401);
|
|
}
|
|
}
|
|
}
|
|
|
|
function getUserInfo(req) {
|
|
if (!req) throw new Error(Errors.INVALID_INPUT);
|
|
|
|
const userInfo = req.userInfo;
|
|
if (!userInfo || !userInfo.membership || utils.isEmptyArray(userInfo.membership.subscriptions))
|
|
throw new Error(Errors.SUBSCRIPTION_NOT_FOUND);
|
|
return userInfo;
|
|
}
|
|
|
|
/** Check for require any subscription. It requires to be called after checkUser middleware */
|
|
async function checkRqAnySubscription(req, res, next) {
|
|
const routeUrl = getRoutePath(req);
|
|
if (!isSecuredRoute(routeUrl)) return next();
|
|
try {
|
|
getUserInfo(req);
|
|
return next();
|
|
} catch (err) {
|
|
return error.handleResErr(res, err);
|
|
}
|
|
}
|
|
|
|
/** Check for require any subscription. It requires to be called after checkUser middleware */
|
|
async function checkRqPkgSubscription(req, res, next) {
|
|
const routeUrl = getRoutePath(req);
|
|
if (!isSecuredRoute(routeUrl)) return next();
|
|
try {
|
|
const userInfo = getUserInfo(req);
|
|
|
|
if (!(userInfo.membership.subscriptions.some(sub => sub.type === SubType.PACKAGE))) {
|
|
return error.handleResErr(res, Errors.PKG_SUBSCRIPTION_NOT_FOUND);
|
|
}
|
|
return next();
|
|
} catch (err) {
|
|
return error.handleResErr(res, err);
|
|
}
|
|
}
|
|
|
|
// async function checkRqTrackingSubscription(req, res, next) {
|
|
// // TODO: Handle the check for subscription for addon(s) by name.
|
|
// }
|
|
|
|
async function checkACsLimits(userInfo) {
|
|
if (!userInfo) throw new Error(Errors.NO_ACCESS);
|
|
|
|
const pkgSub = subUtil.getPkgSubfromUserInfo(userInfo);
|
|
|
|
const numOfAC = await Vehicle.countDocuments({ parent: userInfo.puid, [Fields.MARKED_DELETE]: { $in: [false, null] } });
|
|
const maxVehicles = subUtil.getSubMetaField(pkgSub, SubFields.MAX_VEHICLES) || 0;
|
|
|
|
if (numOfAC && numOfAC >= maxVehicles) {
|
|
throw new Error(Errors.REACHED_VEHICLES_LIMIT);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
async function checkUsageLimits(userInfo) {
|
|
if (!userInfo) throw new Error(Errors.NO_ACCESS);
|
|
|
|
const pkgSub = subUtil.getPkgSubfromUserInfo(userInfo);
|
|
|
|
const priceMaxAcres = subUtil.getSubMetaField(pkgSub, SubFields.MAX_ACRES) || 0;
|
|
if (priceMaxAcres) {
|
|
const ttSprArea = await subUtil.calcTotalAreaByUser(ObjectId(userInfo.puid), pkgSub.periodStart, pkgSub.periodEnd);
|
|
if (ttSprArea && utils.haToAcre(ttSprArea) >= priceMaxAcres) {
|
|
throw new Error(Errors.REACHED_AREA_LIMIT);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
async function checkReqACsLimits(req, res, next) {
|
|
try {
|
|
const userInfo = getUserInfo(req);
|
|
await checkACsLimits(userInfo);
|
|
|
|
return next && (next());
|
|
} catch (err) {
|
|
return error.handleResErr(res, err);
|
|
}
|
|
}
|
|
|
|
async function checkReqUsageLimits(req, res, next) {
|
|
try {
|
|
const userInfo = getUserInfo(req);
|
|
await checkUsageLimits(userInfo);
|
|
|
|
return next && (next());
|
|
} catch (err) {
|
|
return error.handleResErr(res, err);
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
isSecuredRoute, getUserInfo, checkUser, checkACsLimits, checkUsageLimits,
|
|
checkRqAnySubscription, checkRqPkgSubscription, checkReqACsLimits, checkReqUsageLimits,
|
|
};
|