sheercms
Version:
Sheer Cliff CMS is a simple and powerful content management system (CMS) for Node JS.
799 lines (736 loc) • 26.1 kB
JavaScript
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;
}