UNPKG

sheercms

Version:

Sheer Cliff CMS is a simple and powerful content management system (CMS) for Node JS.

799 lines (736 loc) 26.1 kB
var logger = require('../logger'); var models = require('./users-models'); var api = require('../api'); var config = require('../config'); var crypto = require('../crypto'); var jwt = require('jwt-simple'); module.exports.models = models; module.exports.getMockAdmin = function() { var user = { username: 'admin', roles: ['cms', 'admin'] }; return user; }; /* callback: err, success */ module.exports.login = function(req, res, username, password, callback) { try { api.users.authenticate(username, password, function(err, user) { if (err) { if (callback) callback(err, false); } else if (user) { //create the JWT token var expires = new Date(); expires.setMinutes(expires.getMinutes() + config.settings.token_expire_minutes); var token = jwt.encode({ id: user.username, exp: expires }, config.settings.token_secret); logger.info("User login successful: " + user.username); res.cookie(config.settings.cms_token_cookie, token); if (callback) callback(null, true); } else { var msg = "User account not found: " + username; logger.warn(msg); if (callback) callback(new Error(msg), false); } }); } catch(e) { logger.error(e); if (callback) callback(e); } }; /* callback: (err, user) */ module.exports.createUser = function(user, currentUser, callback) { try { if (currentUser == null) { var e = new Error("The currentUser parameter is required"); logger.error(e); if (callback) callback(e); return; } //the current user must be an admin to create new users if (module.exports.isAdmin(currentUser) === false) { var e = new Error("The currentUser must be in the 'admin' role"); logger.error(e); if (callback) callback(e); return; } //validate the password if (user.password !== user.confirmPassword) { var e = new Error("The 'password' and 'confirm password' do not match."); logger.warn(e); if (callback) callback(e); return; } if (!isPasswordValid(user.password)) { var e = new Error("The password does not meet the requirements."); logger.warn(e); if (callback) callback(e); return; } //new user models.User.findOne({username: user.username}).exec(function (err, acct) { try { if (err) { logger.error(err); if (callback) callback(err); } else if (acct == null) { logger.info("Creating new user: " + user.username); crypto.hashPassword(user.password, function(err, result) { if (err) { if (callback) callback(err); } else { acct = new models.User({ username: user.username, password: result, firstName: user.firstName, lastName: user.lastName, email: user.email, roles: user.roles, active: user.active, protected: user.protected || false }); try { acct.save(function (err) { if (err) { logger.error(err); if (callback) callback(err); } else { if (callback) callback(null, module.exports.mapUser(acct)); } }); } catch (e) { logger.error(e); if (callback) callback(e); } } }); } else { var e = new Error("A user with this username already exists: " + user.username); logger.warn(e); if (callback) callback(e); } } catch(e) { logger.error(e); if (callback) callback(e); } }); } catch(e){ logger.error(e); if (callback) callback(e); } }; /* Make sure the password meets the password strength requirements */ function isPasswordValid(pwd) { if (pwd) { var expr = config.settings.password_regex; return pwd.match(expr); } else return false; } /* callback: (err, user) */ module.exports.updateUser = function(user, currentUser, changePassword, callback) { try { if (currentUser == null) { var e = new Error("The currentUser parameter is required"); logger.error(e); if (callback) callback(e); return; } //existing user models.User.findOne({username: user.username}).exec(function (err, acct) { if (err) { logger.error(err); if (callback) callback(err); } else if (acct == null) { var e = new Error("User account not found: " + user.username); logger.error(e); if (callback) callback(e); } else { //only allow update if it's the current user or an admin logged in if (module.exports.isAdmin(currentUser) || currentUser.username == acct.username) { //update the user properties acct.email = user.email; acct.firstName = user.firstName; acct.lastName = user.lastName; //only change the roles if logged in as admin if (module.exports.isAdmin(currentUser)) { acct.roles = user.roles; acct.active = user.active; } if (changePassword === true) { //validate the password if (user.password !== user.confirmPassword) { var e = new Error("The 'password' and 'confirm password' do not match."); logger.warn(e); if (callback) callback(e); return; } if (!isPasswordValid(user.password)) { var e = new Error("The password does not meet the requirements."); logger.warn(e); if (callback) callback(e); return; } //update the password too crypto.hashPassword(user.password, function(err, result) { if (err) { if (callback) callback(e); } else { acct.password = result; try { acct.save(function (err) { if (err) { logger.error(err); if (callback) callback(err); } else { if (callback) callback(null, module.exports.mapUser(acct)); } }); } catch (e) { logger.error(e); if (callback) callback(e); } } }); } else { try { acct.save(function (err) { if (err) { logger.error(err); if (callback) callback(err); } else { if (callback) callback(null, module.exports.mapUser(acct)); } }); } catch (e) { logger.error(e); if (callback) callback(e); } } } else { var e = new Error("The current user does not have permission to update this account."); logger.error(e); if (callback) callback(e); } } }); } catch(e){ logger.error(e); if (callback) callback(e); } }; /* callback: (err, user) */ module.exports.updateProfile = function(user, currentUser, changePassword, callback) { try { if (currentUser == null) { var e = new Error("The currentUser parameter is required"); logger.error(e); if (callback) callback(e); return; } //existing user models.User.findOne({username: user.username}).exec(function (err, acct) { if (err) { logger.error(err); if (callback) callback(err); } else if (acct == null) { var e = new Error("User account not found: " + user.username); logger.error(e); if (callback) callback(e); } else { //only allow update if it's the current user if (currentUser.username == acct.username) { //update the user properties acct.email = user.email; acct.firstName = user.firstName; acct.lastName = user.lastName; if (changePassword === true) { //validate the password if (user.password !== user.confirmPassword) { var e = new Error("The 'password' and 'confirm password' do not match."); logger.warn(e); if (callback) callback(e); return; } if (!isPasswordValid(user.password)) { var e = new Error("The password does not meet the requirements."); logger.warn(e); if (callback) callback(e); return; } //update the password too crypto.hashPassword(user.password, function(err, result) { if (err) { if (callback) callback(e); } else { acct.password = result; try { acct.save(function (err) { if (err) { logger.error(err); if (callback) callback(err); } else { if (callback) callback(null, module.exports.mapUser(acct)); } }); } catch (e) { logger.error(e); if (callback) callback(e); } } }); } else { try { acct.save(function (err) { if (err) { logger.error(err); if (callback) callback(err); } else { if (callback) callback(null, module.exports.mapUser(acct)); } }); } catch (e) { logger.error(e); if (callback) callback(e); } } } else { var e = new Error("The current user does not have permission to update this account."); logger.error(e); if (callback) callback(e); } } }); } catch(e){ logger.error(e); if (callback) callback(e); } }; /* callback: (err, user) */ module.exports.createAdminUser = function(callback) { //make sure it doesn't already exist models.User.findOne({username: 'admin'}).exec(function(err, acct) { if (err) { logger.error(err); if (callback) callback(err); } else if (acct) { if (callback) callback(null, module.exports.mapUser(acct)); } else if (acct == null) { logger.info("Creating 'admin' user."); crypto.hashPassword(config.settings.default_admin_password, function(err, result) { if (err) { if (callback) callback(err); } else { acct = new models.User({ username: "admin", password: result, firstName: "Admin", lastName: "Admin", email: "", roles: ["admin", "cms"], active: true, protected: true }); try { acct.save(function (err) { if (err) { logger.error(err); if (callback) callback(err); } else { if (callback) callback(null, module.exports.mapUser(acct)); } }); } catch(e) { logger.error(e); if (callback) callback(e); } } }); } }); }; /* callback: (err) */ module.exports.deleteUser = function(username, callback) { models.User.findOne({username: username}).exec(function(err, result) { if (err) { logger.error(err); if (callback) callback(err); } else if (result) { if (result.protected === true) { var e = new Error("This user cannot be deleted because it is protected: " + username); logger.warn(e.message); if (callback) callback(e); } else { result.remove(function (err) { if (err) { logger.error(err); if (callback) callback(err); } else { if (callback) callback(null); } }); } } else { var e = new Error("A user with this username was not found: " + username); logger.error(e); if (callback) callback(e); } }); }; module.exports.isAdmin = function (user) { if (user) { if (user.username === 'admin') return true; else { //see if the user has the 'admin' role return module.exports.hasRole(user, 'admin'); } } return false; }; module.exports.isCMSUser = function (user) { //see if the user has the 'admin' role return module.exports.hasRole(user, 'cms'); }; module.exports.hasRole = function(user, roleName) { if (user.roles) { for (var i = 0; i < user.roles.length; i++) { if (user.roles[i] === roleName) return true; } } return false; }; /* callback: (err, user) */ module.exports.getUser = function(username, callback) { if (callback) { models.User.findOne({username: username}).lean().exec(function (err, result) { if (err) { logger.error(err); callback(err); } else { callback(null, module.exports.mapUser(result)); //map user will remove the password field } }); } }; /* callback: (err, user) */ module.exports.authenticate = function(username, password, callback) { if (callback) { try { logger.debug("Authenticating user: " + username); models.User.findOne({username: username}).lean().exec(function (err, user) { if (err) { logger.error(err); callback(err); } else if (user && user.active === true) { crypto.testPassword(password, user.password, function (err, isMatch) { if (err) callback(err); else { if (isMatch === true) callback(null, module.exports.mapUser(user)); else callback(new Error("Invalid username or password")); } }); } else if (user == null) { var e = new Error("User not found: " + username); logger.warn("users-api.authenticate: " + e.message); callback(e); } else if (user.active !== true) { var e = new Error("User not active: " + username); logger.warn("users-api.authenticate: " + e.message); callback(e); } }); } catch(e) { logger.error(e); callback(e); } } }; /* role is optional callback: (err, users) */ module.exports.getUsers = function(role, callback) { if (callback) { if (role && role.length > 0) { models.User.find({roles: role}).sort("username").lean().exec(function (err, users) { if (err) { logger.error(err); callback(err); } else { callback(null, module.exports.mapUsers(users)); } }); } else { models.User.find().sort("username").lean().exec(function (err, users) { if (err) { logger.error(err); callback(err); } else { callback(null, module.exports.mapUsers(users)); } }); } } }; module.exports.mapUsers = function(users) { var arr = []; if (users) { for (var i = 0; i < users.length; i++) { arr.push(module.exports.mapUser(users[i])); } } return arr; } module.exports.mapUser = function(user) { if (!user) return null; return { _id: user._id.toString(), username: user.username, firstName: user.firstName, lastName: user.lastName, email: user.email, active: user.active, roles: user.roles, protected: user.protected }; } /*=================== ROLES =====================*/ /* callback: (err, role) */ module.exports.createRole = function(name, group, description, protect, callback) { try { //role names should be lowercase name = name.toLowerCase(); logger.info("Creating role: " + name); //make sure a role with this name doesn't already exist models.Role.findOne({name: name}).exec(function (err, role) { if (err) { logger.error(err); if (callback) callback(err); } else if (role) { var e = new Error("A role with this name already exists: " + name); logger.warn(e.message); //not a real error if (callback) callback(e); } else { var dt = new Date(); role = new models.Role({ name: name, group: group, desc: description, created: dt, protected: protect }); try { role.save(function (err) { if (err) { logger.error(err); if (callback) callback(err); } else { if (callback) callback(null, mapRole(role)); } }); } catch (e) { logger.error(e); if (callback) callback(e); } } }); } catch(e) { logger.error(e); if (callback) callback(e); } }; /* callback: (err, role) */ module.exports.updateRole = function(role, callback) { try { logger.info("Updating role: " + role.name); models.Role.findOne({name: role.name}).exec(function (err, dbRole) { if (err) { logger.error(err); if (callback) callback(err); } else if (dbRole) { dbRole.desc = role.desc; dbRole.group = role.group; dbRole.modified = new Date(); try { dbRole.save(function (err) { if (err) { logger.error(err); if (callback) callback(err); } else { if (callback) callback(null, mapRole(dbRole)); } }); } catch (e) { logger.error(e); if (callback) callback(e); } } else { var e = new Error("A role with this name was not found: " + role.name); logger.error(e); if (callback) callback(e); } }); } catch(e) { logger.error(e); if (callback) callback(e); } }; /* callback: (err) */ module.exports.deleteRole = function(name, callback) { try { logger.info("Deleting role: " + name); models.Role.findOne({name: name}).exec(function (err, role) { if (err) { logger.error(err); if (callback) callback(err); } else if (role) { if (role.protected === true) { var e = new Error("The role cannot be deleted because it is protected: " + name); logger.warn(e.message); if (callback) callback(e); } else { role.remove(function(err) { if (err) { logger.error(err); if (callback) callback(err); } else { if (callback) callback(null); } }); } } else { var e = new Error("Role not found to remove: " + name); logger.warn(e.message); if (callback) callback(e); } }); } catch(e) { logger.error(e); if (callback) callback(e); } }; /* callback: (err, roles) */ module.exports.getRoles = function(callback) { if (callback) { try { logger.debug("users-api.getRoles()"); models.Role.find().sort("name").lean().exec(function (err, roles) { if (err) { logger.error(err); callback(err); } else { callback(null, roles); } }); } catch (e) { logger.error(e); if (callback) callback(e); } } }; function mapRole(role) { if (role) { var r = { name: role.name, desc: role.desc, created: role.created, modified: role.modified, group: role.group, protected: role.protected }; return r; } else return null; }