UNPKG

joola.io.engine

Version:
236 lines (197 loc) 6.95 kB
/** * joola.io * * Copyright Joola Smart Solutions, Ltd. <info@joo.la> * * Licensed under GNU General Public License 3.0 or later. * Some rights reserved. See LICENSE, AUTHORS. * * @license GPL-3.0+ <http://spdx.org/licenses/GPL-3.0+> */ var auth = require('../lib/auth/manager'), query = require('./query'), util = require('util'); function ErrorTemplate(err) { this.name = 'joola.io.engine Exception'; this.code = 500; if (err.message) this.message = err.message; else this.message = err || ''; if (err.stack) this.stack = err.stack; else this.stack = null; } ErrorTemplate.prototype = Error.prototype; exports.ErrorTemplate = ErrorTemplate function AuthErrorTemplate(err) { this.name = 'joola.io.engine Authentication Exception'; this.code = 401; if (err.message) this.message = err.message; else this.message = err || ''; if (err.stack) this.stack = err.stack; else this.stack = null; } AuthErrorTemplate.prototype = ErrorTemplate.prototype; exports.AuthErrorTemplate = AuthErrorTemplate; exports.responseError = function (err, req, res) { if (err.code && err.code == 401) res.status(401); else res.status(500); res.json(err); }; exports.responseSuccess = function (data, req, res) { var response = {}; if (req.token) res.setHeader('joola-token', req.token); if (!req.params.minres) { response.id = require('node-uuid').v4(); response.timestamp = new Date(); response.success = true; response.details = {}; response.details.request = req.debug; } response = _.extend(response, data); //response.data = data; res.status(200); res.json(response); }; exports.index = function (req, res) { res.render('index'); }; exports.validateToken = function (req, res, callback) { var failed = function (err) { if (req.method == 'OPTIONS' || req.url.substring(req.url.length - 3) == 'ico') { return callback(null, true); } else { if (err) { console.log(err); joola.logger.warn('Failed to validate security token: ' + err); } else joola.logger.warn('Detected a connection without a valid token accessing action: ' + req.params.resource + '/' + (req.params.action ? req.params.action : '') + '.'); return callback(null, false); } }; var validate = function (token, forceValidated) { auth.validateToken(token, function (err, user, _token) { if ((joola.config.server.bypassToken != null && token == joola.config.server.bypassToken) || joola.config.auth.store == 'none' || forceValidated) { if (!_token) _token = '1234567890'; user = { id: 1, displayName: 'Anonymous User', _roles: ['admin', 'user'] }; } else if (err) { return failed(err); } if (user) { joola.logger.silly('Token [' + _token + '] validated.'); req.user = user; req.token = _token; return callback(null, true); } else { joola.logger.error('Token [' + token + '] is invalid.'); return failed(); } }); }; var approvedActions = []; approvedActions.push('auth/login'); approvedActions.push('auth/loginSSO'); approvedActions.push('auth/loginNeeded'); approvedActions.push('auth/checkToken'); var token = req.headers['joola-token']; if (token == null || typeof(token) == 'undefined' || token == 'undefined') token = req.query.token; if (approvedActions.indexOf(req.params.resource + (req.params.action ? '/' + req.params.action : '')) != -1) { joola.logger.silly('Approved action detected, bypassing token inspection.'); //return callback(null, true); return validate(token, true); } if (token || joola.config.auth.store == 'none') return validate(token); else return failed(); }; exports.validateAction = function (action, req, res, callback) { if (req.method == 'OPTIONS') return callback(null, true); var required = action.inputs.required; var optional = action.inputs.optional; var _params = []; _params.resource = req.params.resource; _params.action = req.params.action; _params.minres = req.params.minres; required.forEach(function (param) { if (!req.params[param]) return callback(new ErrorTemplate('Required param [' + param + '] is missing.'), false); _params[param ] = req.params[param]; }); optional.forEach(function (param) { if (req.params[param]) _params[param] = req.params[param]; else _params[param] = ''; }); req.debug = {}; Object.keys(_params).forEach(function (key) { req.debug[key] = _params[key]; }); req.params = _params; if (!req.user) return callback(new ErrorTemplate('Request for action by unauthenticated user.'), false); var userPermissions = []; req.user._roles.forEach(function (role) { joola.config.auth.roles.forEach(function (roleToCompareAgainst) { if (role === roleToCompareAgainst.id) { userPermissions = userPermissions.concat(roleToCompareAgainst.permissions); } }); }); if (util.isArray(action.permission)) action.permission = action.permission[0]; if (userPermissions.indexOf(action.permission) == -1) return callback(new ErrorTemplate('Missing permission to run this action. [action:' + action.name + '][permission:' + action.permission + '][roles:' + JSON.stringify(req.user._roles) + ']'), false); return callback(null, true); }; exports.route = function (req, res) { var modulename = req.params.resource; var module; var action = req.params.action; if (['png', 'ico'].indexOf(req.url.substring(req.url.length - 3)) > -1) return exports.responseError(new ErrorTemplate('Not implemented.'), req, res); _.extend(req.params, req.query); exports.validateToken(req, res, function (err, validated) { if (err) return exports.responseError(err, req, res); else if (!validated) return exports.responseError(new AuthErrorTemplate('Authentication failed.'), req, res); joola.logger.silly('Token verified ' + req.token); if (!modulename) return exports.responseError(new ErrorTemplate('Module not specified.'), req, res); try { module = require('./' + modulename); } catch (ex) { return exports.responseError(ex, req, res); } if (!action) action = 'index'; try { action = module[action]; exports.validateAction(action, req, res, function (err, validated) { if (err) return exports.responseError(err, req, res); else if (!validated) return exports.responseError(new ErrorTemplate('Failed to validate action.'), req, res); joola.logger.silly('Routing [' + action.name + ']...'); return action.run(req, res); }); } catch (ex) { console.log('err', ex); console.log(ex.stack); return exports.responseError(ex, req, res); } }); };