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
JavaScript
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();
});
}