yekonga-server
Version:
Yekonga Server
582 lines (482 loc) • 22.3 kB
JavaScript
// @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.debug(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.debug(objectValueId, ids.length);
// console.debug(`\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;
}
}