UNPKG

alphascript-server

Version:

CRUD operations for mongo and other functionalities to get started quickly in any CMS project

361 lines (308 loc) 12.1 kB
var mongoose = require('mongoose'); var passport = require('passport'); var LocalStrategy = require('passport-local').Strategy; var randToken = require('rand-token').uid; var fs = require('fs'); var api = require('../../'); var middleware = { ensureAdmin: ensureAdmin, ensureLoggedIn: ensureLoggedIn, ensureOwner: ensureOwner, ensureLicensed: ensureLicensed }; var method = { filterUser: defaultFilterUser, logout: defaultLogout, login: defaultLogin, serializeUser: defaultSerializeUser, deserializeUser: defaultDeserializeUser, strategy: new LocalStrategy({ passReqToCallback: true }, function (req, username, password, callback) { var userPopulate = typeof api.entityMap.user.populate === 'function' ? api.entityMap.user.populate(req) : api.entityMap.user.populate; api.common.User.findOne({ 'username': username }).populate(userPopulate).exec(function (err, user) { if (err) { api.error.log(err); return callback(err); } if (!user) return callback(null, false); if (user.password !== password) return callback(null, false); callback(null, user); }); }), activateAccount: defaultActivateAccount, changePassword: defaultChangePassword, resetPassword: defaultResetPassword }; module.exports = { strategy: method.strategy, serializeUser: method.serializeUser, deserializeUser: method.deserializeUser, login: method.login, logout: method.logout, activateAccount: method.activateAccount, resetPassword: method.resetPassword, changePassword: method.changePassword, user: function (req, res) { if (!req.user) return res.json({ user: null }); //delete private data from user before sending return res.json(method.filterUser(req.user)); }, middleware: middleware, registerMiddleware: function (key, override) { if (typeof key !== 'string') return console.log('middleware key of unexpected type ignored (not a string)'); if (typeof override !== 'function') return console.log(key + ': middleware of unexpected type ignored (not a function)'); console.log(key + ': middleware overwritten'); middleware[key] = override; }, method: method, overrideMethod: function (key, override) { if (typeof key !== 'string') return console.log('override method key must be string'); if (!method[key]) return console.log('cannot override method key ' + key); if (key === 'strategy') { if (!(override instanceof passport.Strategy)) return console.log('override method Strategy of unexpected type ignored (not a passport.Strategy)'); } else if (typeof override !== 'function') return console.log(key + ': override method of unexpected type ignored (not a function)'); method[key] = override; console.log('auth method override: ' + key); }, ensureEligible: function (req, res, next) { //ignore files if (!req.url.startsWith('/api/')) return next(); //set configs var urlParams = req.url.substring(5).split('/'); var level1 = urlParams[0]; var level2 = urlParams[1]; //anonymous if (typeof api.permissionMap[level1] === 'undefined') return next(); if (typeof api.permissionMap[level1][level2] === 'undefined') return next(); //logged in ensureLoggedIn(req, res, function (err) { if (err) return next(err); //run other authentication middleware runMiddleware(req, res, api.permissionMap[level1][level2].middleware, 0, function (err) { if (err) return next(err); //bypass if admin if (req.user.role.admin) return next(); //check for dynamic permissions var clearance = req.user.role.permissions || {}; if (typeof clearance[level1] === 'undefined') return next(); if (typeof clearance[level1][level2] === 'undefined') return next(); return clearance[level1][level2] ? next() : next('Acesso proíbido'); }); }); }, getPermissionMap: function (req, res) { var data = {}; for (var entity in api.permissionMap) { for (var action in api.permissionMap[entity]) { if (typeof api.permissionMap[entity][action].label === 'undefined') continue; if (!data[entity]) { data[entity] = { label: api.permissionMap[entity].label || entity }; } data[entity][action] = { label: api.permissionMap[entity][action].label }; } } res.json(data); }, denyAccess: function (req, res) { return res.status(403).send("Acesso negado"); }, denyAnonymousAccess: function (req, res, next) { if (!req.user) return res.status(403).send("Acesso anónimo negado"); next(); }, logErrors: function (err, req, res, next) { api.error.log(err); next(err); }, clientErrorHandler: function (err, req, res, next) { if (req.xhr) { res.status(500).send("Ocorreu um erro inesperado, por favor tente novamente"); } else { res.status(500).send(err.message || err); } }, errorHandler: function (err, req, res, next) { if (res.headersSent) { return next(err); } res.status(500).send(err.message || err); } }; function runMiddleware(req, res, steps, index, callback) { if (!(steps instanceof Array)) return callback(); if (steps.length <= index) return callback(); var step = middleware[steps[index]]; if (typeof step !== 'function') return callback('Erro na configuração de middleware (permissions.json): ' + req.url + ' (' + steps[index] + ')'); middleware[steps[index]](req, res, function (err) { return err ? callback(err) : runMiddleware(req, res, steps, index + 1, callback); }); } function defaultFilterUser(user) { if (!user) return { user: null }; return { user: { _id: user._id, username: user.username, email: user.email, role: user.role, isFirstLogin: user.isFirstLogin, owner: user.owner, token: user.token, license: user.license } }; } function defaultLogout(req, res) { if (!req.user) return res.end(); api.common.User.findOneAndUpdate({ _id: req.user._id }, { $unset: { token: 1 } }, function (err) { if (err) { api.error.log(err); return res.status(500).send('Não foi possível comunicar a sua saída'); } api.activity.log('logout', req.user.username, req); req.logout(); req.session.destroy(function (err) { return res.end(); }); }); } function defaultLogin(req, res, next) { passport.authenticate('local', function (err, user, info) { if (err) { api.error.log(err); return next(err); } if (!user) return res.status(400).send('Nome de utilizador e palavra-chave inválidos'); if (typeof user.toObject === 'function') user = user.toObject(); user.token = randToken(8); api.common.User.findOneAndUpdate({ _id: user._id }, { $set: { token: user.token } }, function (err) { if (err) { api.error.log(err); return next(err); } req.login(method.filterUser(user).user, function (err) { if (err) { api.error.log(err); return next(err); } api.activity.log('login', user.username, req); return res.json(method.filterUser(user)); }); }); })(req, res, next); } function defaultSerializeUser(req, user, callback) { callback(null, user._id + '|' + user.token); } function defaultResetPassword(req, res) { if (!req.body.username || !req.body.email) { return res.status(403).send('Não é possível fazer a recuperação da password. Email e nome de utilizador inválidos.'); } var query = { $and: [{ username: req.body.username }, { email: req.body.email }] }; var update = { $set: { changepw: true, activation: randToken(64) }, $unset: { password: 1 } }; api.common.User.findOneAndUpdate(query, update, { new: true }, function (err, user) { if (err) { api.error.log(err); return res.status(403).send('Não é possível fazer a recuperação da password. Email e nome de utilizador inválidos.'); } if (!user) return res.status(403).send('Não é possível fazer a recuperação da password. Email e nome de utilizador inválidos.'); api.message.send('reset', { user: user }, function (err) { if (err) { api.error.log(err); return res.status(500).send('Não foi possível enviar o email de recuperação, por favor tente novamente'); } api.activity.log('resetPassword', user.email, req); res.end(); }); }); } function defaultActivateAccount(req, res) { if (!req.body.code || !req.body.email) { return res.status(500).send("Código de ativação inválido"); } var userPopulate = typeof api.entityMap.user.populate === 'function' ? api.entityMap.user.populate(req) : api.entityMap.user.populate; api.common.User.findOne({ $and: [{ email: req.body.email, activation: req.body.code }] }).populate(userPopulate).exec(function (err, user) { if (err) { api.error.log(err); return res.status(500).send(api.error.DB_GENERIC); } if (!user) return res.status(404).send("Código de ativação inválido"); if (!!user.password) return res.status(400).send("Conta já ativada"); api.common.User.update({ _id: user._id }, { $unset: { password: 1 } }, function (err) { if (err) { api.error.log(err); return res.status(500).send(api.error.DB_GENERIC); } api.activity.log('activate', user.username, req); res.json(method.filterUser(user).user); }); }); } function defaultDeserializeUser(req, id, callback) { var parts = id.split('|'); if (parts.length !== 2) return callback("Sem sessão iniciada"); try { mongoose.Types.ObjectId(parts[0]); } catch (e) { return callback("Sem sessão iniciada"); } var userPopulate = typeof api.entityMap.user.populate === 'function' ? api.entityMap.user.populate(req) : api.entityMap.user.populate; api.common.User.findOne({ _id: parts[0] }).populate(userPopulate).lean().exec(function (err, user) { if (err) { api.error.log(err); return callback(api.error.DB_GENERIC); } if (!user) return callback("Sem sessão iniciada"); if (!user.owner) return callback("Sem sessão iniciada"); if (user.token !== parts[1]) { req.logout(); req.session.destroy(function (err) { if (err) api.error.log(err); callback("Alguem entrou com a sua conta noutro dispositivo"); }); } else { api.entityMap.license.isLicensed(user.owner._id, function (err, licenses, isValid) { if (err) return callback(err); user.license = isValid ? licenses[0] : null; callback(null, method.filterUser(user).user); }); } }); } function defaultChangePassword(req, res) { if (!req.body._id || !req.body.newPassword) return res.status(500).send("Não é possível fazer a alteração de password"); var update = { $set: { password: req.body.newPassword, changepw: false }, $unset: { activation: 1 } }; api.common.User.findOneAndUpdate({ _id: req.body._id }, update, function (err) { if (err) { api.error.log(err); return res.status(400).send('Não foi possível fazer a alteração de password'); } api.activity.log('changePassword', req.body.email, req); res.end(); }); } function ensureLoggedIn(req, res, next) { if (!req.isAuthenticated()) return next("Sem sessão iniciada"); if (!req.user) return next("Sem sessão iniciada"); if (!req.user.username || !req.user.role) return next("Sem sessão iniciada"); next(); } function ensureAdmin(req, res, next) { if (!req.user.role.admin) return next("Utilizador sem privilegios de administração"); next(); } function ensureOwner(req, res, next) { if (req.user.owner.username.toString() !== req.user.username.toString()) return next('Página reservada ao detentor'); return next(); } function ensureLicensed(req, res, next) { //todo api.entityMap.license._isValid(moduleId, function (err, isValid) { if (err) return next(err); if (!isValid) return next("Sem licenciamento"); next(); }); }