UNPKG

yekonga-server

Version:
582 lines (482 loc) 22.3 kB
// @ts-nocheck /*global Yekonga, serverLibrary */ const H = Yekonga.Helper; const config = Yekonga.Config; const bcrypt = serverLibrary.bcrypt; const jwt = serverLibrary.jwt; const URL = serverLibrary.url; const gqlParse = serverLibrary.gqlParse; // @ts-ignore const ObjectId = serverLibrary.mongodb.ObjectId; const { ServerResponse } = serverLibrary.http; const userAgent = require('./utils/userAgent'); function getIpAddress(req) { var ip = null; if(typeof req.header == 'function') { ip = req.header('x-forwarded-for'); } else if(req.headers && req.headers['x-forwarded-for']) { ip = req.headers['x-forwarded-for']; } else if(req.socket && req.socket.remoteAddress) { ip = req.socket.remoteAddress; } if(ip) { ip = ip.split(',')[0]; ip = ip.split(':').pop(); } return ip; } module.exports = class Middleware { static setYekonga(req, res, next) { if (!req.Yekonga) { req.Yekonga = {}; } if (typeof req.next == 'function') { return req.next(); } else if (typeof next == 'function') { return next(req); } } static globalRequestContext(req, res, next) { if (typeof req.next == 'function') { return req.next(); } else if (typeof next == 'function') { return next(); } }; static async init(req, res, next) { if (req.method == 'OPTIONS') { var origin = (req.headers)? req.headers.origin: ((req.handshake && req.handshake.headers)? req.handshake.headers.origin: null); if(typeof res.setHeader == 'function') { res.setHeader('access-control-allow-origin', "*"); if (origin) { res.setHeader('access-control-allow-origin', origin); } return res.status(200).json(); } } const client = {} try { client.body = req.body; client.headers = (req.headers)? req.headers: ((req.handshake && req.handshake.headers)? req.handshake.headers: {} ); client.host = (client.headers['x-forwarded-host']) ? client.headers['x-forwarded-host'] : client.headers['host']; client.proto = (client.headers['x-forwarded-proto']) ? client.headers['x-forwarded-proto'] : client.headers['proto']; client.origin = (client.headers['x-forwarded-origin']) ? client.headers['x-forwarded-origin'] : client.headers['origin']; client.ipAddress = getIpAddress(req); client.userAgent = client.headers['user-agent']; client.browser = userAgent.detect(client.userAgent); client.subdomain = null; if(!client.proto) { client.proto = "http"; } if(client.proto && client.proto.includes(',')) { client.proto = client.proto.split(',').shift(); } let domainUrl = client.headers.referer; if(!domainUrl) domainUrl = client.origin; if(!domainUrl) { domainUrl = `${client.proto}://${client.host}` } if (domainUrl) { var urlData = URL.parse(domainUrl, true); var currentDomain = urlData.hostname; if(currentDomain) { client.origin = currentDomain; if( urlData.protocol && urlData.protocol.endsWith(':')){ client.proto = urlData.protocol.split(":")[0]; } var domainAlias = Array.isArray(Yekonga.Config.domainAlias)? Yekonga.Config.domainAlias: []; if(Yekonga.Config.domain) { domainAlias.push(Yekonga.Config.domain) } for (const appDomain of domainAlias) { if (appDomain && currentDomain && currentDomain.endsWith(`.${appDomain}`)) { var length = (currentDomain.length - appDomain.length - 1); client.subdomain = currentDomain.substr(0, length); break; } } } } else { client.origin = client.host; } } catch (error) { console.error('Init Middleware', error); } Yekonga.Client = client; req.Yekonga.Client = client; if (typeof req.next == 'function') { return req.next(); } else if (typeof next == 'function') { return next(); } } static async authorization(req, res, next) { var result = null; if (typeof Yekonga.CloudService.Auth.authorization == 'function') { await Yekonga.CloudService.Auth.authorization(req, res); } else { await Middleware.setLoginUser(req, res, next); } await Middleware.setPermissions(req, res, next); if (typeof req.next == 'function') { return req.next(); } else if (typeof next == 'function') { return next(); } } static async customBeforeAllMiddleware(req, res, next) { if (Array.isArray(Yekonga.CloudService.beforeAllMiddleware)) { for (const row of Yekonga.CloudService.beforeAllMiddleware) { if (typeof row == 'function') { await row(req, res, next); } else { console.error(`Init Middleware is not a function`); } } } if (typeof req.next == 'function') { return req.next(); } else if (typeof next == 'function') { return next(); } } static async customInitMiddleware(req, res, next) { if (Array.isArray(Yekonga.CloudService.initMiddleware)) { for (const row of Yekonga.CloudService.initMiddleware) { if (typeof row == 'function') { await row(req, res, next); } else { console.error(`Init Middleware is not a function`); } } } if (typeof req.next == 'function') { return req.next(); } else if (typeof next == 'function') { return next(); } } static async customMiddleware(req, res, next) { if (Array.isArray(Yekonga.CloudService.Middleware)) { for (const row of Yekonga.CloudService.Middleware) { if (typeof row == 'function') { await row(req, res, next); } else { console.error(`Middleware is not a function`); } } } if (typeof req.next == 'function') { return req.next(); } else if (typeof next == 'function') { return next(); } } static async setToken(req, res, next) { var token = null; var body = req.body ? req.body : {}; var query = req.query ? req.query : {}; var payload = null; Yekonga.Auth = null; Yekonga.tokenPayload = null; try { if (req && typeof req.header == 'function' && req.header('Authorization')) { var headerToken = req.header('Authorization'); token = headerToken.replace("Bearer", "").trim(); } else if (req && req.headers && req.headers['authorization']) { var headerToken = req.headers['authorization']; token = headerToken.replace("Bearer", "").trim(); } else if (req && req.handshake && req.handshake.headers && req.handshake.headers['authorization']) { var headerToken = req.handshake.headers['authorization']; token = headerToken.replace("Bearer", "").trim(); } else if (body.token) { token = body.token; } else if (query.token) { token = query.token; } if (token && token.trim() != '') { var algorithm = Yekonga.Config.authentication.algorithm; payload = jwt.decode(token, Yekonga.Config.authentication.tokenSecret, algorithm); if (req && payload && !payload.error && payload.userId) { if ( payload.domain === req.Yekonga.Client.origin && payload.tokenKey === Yekonga.Config.tokenKey && Yekonga.Helper.getTimestampInt(payload.expiredTime) > Yekonga.Helper.getTimestampInt() ) { req.Yekonga.tokenPayload = payload; } else if (payload.tokenKey !== Yekonga.Config.tokenKey) { return res.status(401).json({ message: `Invalid token key or token expired`, type: 'AuthenticationError' }); } else { return res.status(401).json({ message: `Can not use token from other origin`, type: 'AuthenticationError' }); } } } } catch (error) { console.error('Token Decode', error); } if (req && typeof req.next == 'function') { return req.next(); } else if (typeof next == 'function') { return next(); } return payload; } static async setLoginUser(req, res, next, isMiddleware = false) { const payload = req.Yekonga.tokenPayload; if (payload) { let user = await Yekonga.Model.User.findOne({ userId: payload.userId }, true); if (user) { if (user.isBanned) { return res.status(401).json({ message: `You are banned to access ${Yekonga.Config.appName}`, type: 'AuthenticationError' }); } req.auth = payload; req.Yekonga.Auth = { userId: payload.userId, profileId: payload.profileId, isAdmin: (user.role == 1 || user.role == 'admin'), isManager: (user.role == 2 || user.role == 'manager'), payload: payload, user: user, } Yekonga.Model.User.update({ lastActive: Yekonga.Helper.getTimestamp() }, { userId: user.userId }, null, true); } } if (isMiddleware) { if (typeof req.next == 'function') { return req.next(); } else if (typeof next == 'function') { return next(); } } } static async setPermissions(req, res, next, isMiddleware = false) { const Auth = req.Yekonga.Auth; let data = []; if (Auth) { req.Yekonga.Auth.Permissions = []; var userGroups = await Yekonga.Model.AuthUserGroup.find({ userId: Auth.userId, profileId: Auth.profileId }, null, true); if (userGroups && userGroups.length) { var p1 = [], g1 = []; for (const item of userGroups) { p1.push(item[Yekonga.Helper.getVariable('auth_permission_id')]); p1.push(item[Yekonga.Helper.getVariable('auth_group_id')]); } var whereGroupPermissions = {} whereGroupPermissions[Yekonga.Helper.getVariable('auth_group_id')] = { 'in': g1 }; whereGroupPermissions[Yekonga.Helper.getVariable('profile_id')] = Auth.profileId; var groupPermissions = await Yekonga.Model.AuthGroupPermission.find( whereGroupPermissions, null, true ); var p2 = groupPermissions.map((item) => { return item[Yekonga.Helper.getVariable('auth_permission_id')]; }); for (const p of p2) { p1.push(p); } var wherePermissions = {}; wherePermissions[Yekonga.Helper.getVariable('auth_permission_id')] = { 'in': p1 }; var permissions = await Yekonga.Model.AuthPermission.find( wherePermissions, null, true ); data = permissions.map((item) => { var value = item.name; if (item.group && item.group.trim() != '') { value = `${item.group}.${value}`; } if (item.namespace && item.namespace.trim() != '') { value = `${item.namespace}.${value}`; } return value; }); if (req.Yekonga.Auth && req.Yekonga.Auth.Permissions) { req.Yekonga.Auth.Permissions = Yekonga.Helper.copyJson(data); } } } if (isMiddleware) { if (typeof req.next == 'function') { return req.next(); } else if (typeof next == 'function') { return next(); } } } static async applicationKey(req, res, next) { var key = null; if (Yekonga.Config.enableAppKey) { if (req.header('APPLICATION_KEY')) { key = req.header('APPLICATION_KEY'); if (Yekonga.Config.appId == key) { return next(); } return res.status(401).json({ message: "Application KEY invalid", type: 'AuthenticationError' }); } return res.status(401).json({ message: "Application KEY not provided", type: 'AuthenticationError' }); } return next(); } static headers(req, res, next) { var origin = req.headers.origin; res.setHeader('Access-Control-Allow-Origin', "*"); if (origin) { res.setHeader('Access-Control-Allow-Origin', origin); } res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With, x-csrf-token, upgrade-insecure-requests'); res.setHeader('Access-Control-Allow-Credentials', 'true'); res.setHeader('Keep-Alive', 'timeout=5, max=98'); res.setHeader('Connection', 'Keep-Alive'); return next(); } static apiOptions(req, res, next) { if (req.method == 'OPTIONS') return res.status(200).json(null); if (Yekonga.Config.endToEndEncryption) { const apiAuthRoute = Yekonga.Helper.getApiAuthRoute(); const apiRoute = Yekonga.Helper.getApiRoute(); const list = [apiAuthRoute, apiRoute]; if (list.includes(req.url)) { req.body = Yekonga.Helper.decryptUrl(req.body); } return req.next(); } return next(); } static async setCurrentProfile(req, res, next) { const domain = req.Yekonga.Client.origin; const subdomain = req.Yekonga.Client.subdomain; var profile = await Yekonga.Model.Profile.findOne({ domain }, null, true); if (!profile) { profile = await Yekonga.Model.Profile.findOne({ subdomain }, null, true); } if (profile) { Yekonga.profileId = profile.profileId; req.Yekonga.profileId = profile.profileId; } else { Yekonga.profileId = null; req.Yekonga.profileId = null; } if (typeof req.next == 'function') { return req.next(); } else if (typeof next == 'function') { return next(); } } static async preloadGraphqlRelatedQuery(req, res, next) { try { var route = Yekonga.Helper.getApiRoute(); if (req.path == route && req.body && req.body.query) { const document = gqlParse(req.body.query); const args = Middleware.getArguments(document, {}); for (const key in args) { if (Object.hasOwnProperty.call(args, key)) { const elem = args[key]; if (elem.arguments.where) { for (const field in elem.arguments.where) { if (Yekonga.Graphql.Reference[key] && !Yekonga.Graphql.Reference[key].fields.includes(field)) { var data = {} data[field] = elem.arguments.where[field]; await Middleware.runPreloadGraphqlRelatedQuery(req, res, next, key, data); } } } } } } } catch (error) { console.error('Preload Graphql Related Query', error) } if (typeof req.next == 'function') { return req.next(); } else if (typeof next == 'function') { return next(); } } static async runPreloadGraphqlRelatedQuery(req, res, next, action, where) { // console.log(where); if (!Yekonga.Temporary) Yekonga.Temporary = {}; if (!req.Yekonga.Temporary) req.Yekonga.Temporary = {}; let ids = []; let elem = Yekonga.Graphql.Reference[action]; let objectValueId = Yekonga.Helper.getObjectUUID(where); for (const key in where) { if (Object.hasOwnProperty.call(where, key)) { if (elem.parents[key] || elem.children[key]) { let localSecondaryKey = null; let localModelClass = null; if (elem.parents[key]) { localSecondaryKey = elem.parents[key].secondaryKey; localModelClass = Yekonga.Helper.getClass(elem.parents[key].collection); } else if (elem.children[key]) { localSecondaryKey = elem.children[key].foreignKey; localModelClass = Yekonga.Helper.getClass(elem.children[key].collection); } let sourceIds = await Yekonga.Model[localModelClass].find( where[key], { select: [localSecondaryKey], Auth: req.Yekonga.Auth, req, res }, true ); for (let i = 0; i < sourceIds.length; i++) { var v = sourceIds[i][localSecondaryKey]; if (typeof v == 'string' && v.length === 24) { v = new ObjectId(v); } if (!(typeof v === 'undefined' || v === null)) { if (!ids.includes(v)) { ids.push(v); } } } global.Yekonga.Temporary[objectValueId] = (ids.length) ? ids : [Yekonga.Helper.uuid()]; req.Yekonga.Temporary[objectValueId] = (ids.length) ? ids : [Yekonga.Helper.uuid()]; } } } // console.log(objectValueId, ids.length); // console.log(`\n ============================ \n`); } static getArguments(document, data = {}) { if (Array.isArray(document.definitions)) { for (let i = 0; i < document.definitions.length; i++) { const element = document.definitions[i]; data = Middleware.getArguments(element, data); } } else { if (document.selectionSet && document.selectionSet.selections) { if (Array.isArray(document.selectionSet.selections)) { for (let i = 0; i < document.selectionSet.selections.length; i++) { const element = document.selectionSet.selections[i]; data = Middleware.getArguments(element, data); } } } if (Array.isArray(document.arguments)) { for (let i = 0; i < document.arguments.length; i++) { const element = document.arguments[i]; if (!data[document.name.value]) data[document.name.value] = {}; if (!data[document.name.value].arguments) data[document.name.value].arguments = {}; data[document.name.value].arguments[element.name.value] = (element.value.fields) ? Middleware.getArguments(element, {}) : element.value.value; } } if (document.value && Array.isArray(document.value.fields)) { for (let i = 0; i < document.value.fields.length; i++) { const element = document.value.fields[i]; data[element.name.value] = element.value.value; if (element.value.fields) { data[element.name.value] = Middleware.getArguments(element, {}); } else if (element.value.values) { data[element.name.value] = element.value.values.map(e => e.value); } } } } return data; } }