704 lines
20 KiB
JavaScript
704 lines
20 KiB
JavaScript
const Authentication = require('../modules/authentication');
|
|
const Mongoose = require('mongoose');
|
|
const Reset = require('./reset');
|
|
const Settings = require('./settings');
|
|
|
|
const UserSchema = new Mongoose.Schema({
|
|
userName: { type: String, required: true, unique: true },
|
|
password: { type: String },
|
|
name: { first: { type: String, required: true }, last: { type: String, required: true } },
|
|
title: String,
|
|
email: { type: String, required: true, unique: true },
|
|
permission: { type: Mongoose.Schema.Types.ObjectId, ref: 'roles', required: true, default: '59e6e1ab9bd9c04c803a0bc0' },
|
|
avatar: String,
|
|
settings: [ Settings.schema ],
|
|
forceReset: { type: Boolean, default: true },
|
|
updated_at: { type: Date, default: Date.now }
|
|
});
|
|
|
|
UserSchema.pre('findOneAndUpdate', function (next) {
|
|
var self = this;
|
|
|
|
this.update({}, { $set: { updated_at: Date.now() } });
|
|
|
|
if (this._update.$set.settings && this._update.$set.settings.length) {
|
|
|
|
}
|
|
|
|
if (this._update.$set.password && this._update.$set.confirmPassword && (this._update.$set.password == this._update.$set.confirmPassword)) {
|
|
delete this._update.$set.confirmPassword;
|
|
|
|
if (this._update.$set.currentPassword) {
|
|
confirmPassword(this._conditions.userName, this._update.$set.currentPassword, (err, valid) => {
|
|
if (err || !valid) {
|
|
err = new Error({ success: false, message: 'There was an error validating the current password.', err: (err || null) });
|
|
next(err);
|
|
}
|
|
|
|
if (valid) {
|
|
delete this._update.$set.currentPassword;
|
|
hashPassword(this._update.$set.password, (err, hashedPassword) => {
|
|
self.update({}, { $set: { password: hashedPassword } });
|
|
self.update({}, { $set: { forceReset: false } });
|
|
next();
|
|
});
|
|
}
|
|
});
|
|
} else {
|
|
hashPassword(this._update.$set.password, (err, hashedPassword) => {
|
|
self.update({}, { $set: { password: hashedPassword } });
|
|
self.update({}, { $set: { forceReset: false } });
|
|
next();
|
|
});
|
|
}
|
|
} else if (this._update.$set.password && this._update.$set.confirmPassword && (this._update.$set.password != this._update.$set.confirmPassword)) {
|
|
let err = new Error({ success: false, message: 'There was an error saving the updated password.'});
|
|
next(err);
|
|
} else if (!this._update.$set.password && !this._update.$set.confirmPassword) {
|
|
next();
|
|
}
|
|
});
|
|
|
|
UserSchema.post('save', function (err, res, next) {
|
|
if (err.name === 'MongoError' && err.code === 11000) {
|
|
next(new Error('There was a duplicate key error'));
|
|
} else if (err) {
|
|
next(err);
|
|
} else {
|
|
next();
|
|
}
|
|
});
|
|
|
|
const UserModel = Mongoose.model('users', UserSchema);
|
|
|
|
function hashPassword (password, callback) {
|
|
callback = callback || false;
|
|
|
|
Authentication.hashPassword(password, (err, password) => {
|
|
if (err !== null) {
|
|
err = new Error({ success: false, message: 'There was an error hashing the updated password.', err: err });
|
|
console.error('[updateUser:hashPassword] ', err);
|
|
|
|
if (callback) {
|
|
callback(err, null);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (password) {
|
|
var result = password.toString('hex');
|
|
|
|
if (callback) {
|
|
callback(null, result);
|
|
} else {
|
|
return result;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function confirmPassword (username, passwordToValidate, callback) {
|
|
callback = callback || false;
|
|
|
|
UserModel.findById({ userName: username }, (err, user) => {
|
|
if (err !== null) {
|
|
err = new Error({ success: false, message: 'There was an error locating the user.', err: (err || null) });
|
|
console.error('[updateUser:confirmPassword] ', err);
|
|
|
|
if (callback) {
|
|
callback(err, null);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (user) {
|
|
Authentication.verifyPassword(passwordToValidate, Buffer.from(storedUser.password, 'hex'), (err, valid) => {
|
|
if (err !== null || !valid) {
|
|
err = new Error({ success: false, message: (!err && !valid ? 'The current password was incorrect.' : 'There was an error attempting to validate the password.'), err: (err || null) });
|
|
console.error('[updateUser:confirmPassword] ', { err: err, valid: valid });
|
|
|
|
if (callback) {
|
|
callback(err, null);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (valid) {
|
|
if (callback) {
|
|
callback(null, valid);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
module.exports = {
|
|
adminUpdatePassword: (e, username, password) => {
|
|
const promise = new Promise((resolve, reject) => {
|
|
UserModel.findOneAndUpdate({ userName: username }, { $set: password }, { new: true }, (err, result) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
if (result) {
|
|
resolve(result);
|
|
}
|
|
});
|
|
});
|
|
|
|
promise.then((result) => {
|
|
if (e) {
|
|
e.emit('adminUpdatePassword', null, result);
|
|
} else {
|
|
return result;
|
|
}
|
|
})
|
|
.catch((err) => {
|
|
if (e) {
|
|
e.emit('adminUpdatePassword', err, null);
|
|
} else {
|
|
return err;
|
|
}
|
|
});
|
|
},
|
|
|
|
authenticateUser: (e, login, headers) => {
|
|
const promise = new Promise((resolve, reject) => {
|
|
var loginObject, user;
|
|
|
|
UserModel
|
|
.findOne({ userName: login.userName }, 'userName name title email password permission avatar settings forceReset')
|
|
.populate('permission', 'name disabled manageRoles manageUsers manageCategories manageAppPreferences deleteVendor addVendorTag addVendorSample addVendorComment editVendor addNewVendor viewPrivateDetails viewPublicDetails')
|
|
.exec((err, result) => {
|
|
if (err) {
|
|
loginObject = {
|
|
status: 200,
|
|
authorized: false,
|
|
err: {
|
|
id: '005',
|
|
code: '[UMAU005]',
|
|
string: 'There was an error authenticating the user.'
|
|
}
|
|
}
|
|
console.log('[UserModel::authenticateUser] Error finding user', { err: err, username: login.userName });
|
|
resolve(loginObject);
|
|
}
|
|
|
|
if (result) {
|
|
user = result;
|
|
|
|
if (user && !user.permission.disabled) {
|
|
if (user.forceReset) {
|
|
loginObject = {
|
|
status: 200,
|
|
authorized: false,
|
|
err: {
|
|
id: '003',
|
|
code: '[UMAU003]',
|
|
string: 'A password reset has been mandated. Please check your email for a password reset link or request a new one from the login screen.'
|
|
}
|
|
};
|
|
resolve(loginObject);
|
|
}
|
|
else {
|
|
try {
|
|
Authentication.verifyPassword(login.password, Buffer.from(user.password, 'hex'), (err, valid) => {
|
|
if (err) {
|
|
console.log('[UserModel::authenticateUser] Error validating password', { err: err, user: user });
|
|
reject(err);
|
|
}
|
|
|
|
loginObject = {
|
|
status: 200,
|
|
authorized: valid
|
|
};
|
|
|
|
if (valid) {
|
|
loginObject.user = {
|
|
_id: user._id,
|
|
uid: user._id,
|
|
userName: user.userName,
|
|
name: user.name,
|
|
title: user.title,
|
|
email: user.email,
|
|
permission: user.permission,
|
|
settings: user.settings,
|
|
avatar: user.avatar
|
|
};
|
|
|
|
loginObject.timestamp = Date.now();
|
|
|
|
console.log('[UserModel::authenticateUser] User Validated', { user: user, loginObject: loginObject });
|
|
resolve(loginObject);
|
|
} else {
|
|
loginObject.err = {
|
|
id: '002',
|
|
code: '[UMAU002]',
|
|
string: 'The user id or password you entered was invalid.'
|
|
};
|
|
|
|
console.log('[UserModel::authenticateUser] Invalid Password', { user: user, loginObject: loginObject });
|
|
resolve(loginObject);
|
|
}
|
|
});
|
|
}
|
|
catch (err) {
|
|
loginObject = {
|
|
status: 200,
|
|
authorized: false,
|
|
err: {
|
|
id: '004',
|
|
code: '[UMAU004]',
|
|
string: 'There was an error authenticating the user, please contact an administrator.'
|
|
}
|
|
};
|
|
|
|
console.log('[UserModel::authenticateUser] Error verifying password', { err: err, user: user });
|
|
resolve(loginObject);
|
|
}
|
|
}
|
|
}
|
|
else if (user && user.permission.disabled) {
|
|
loginObject = {
|
|
status: 200,
|
|
authorized: false,
|
|
err: {
|
|
id: '000',
|
|
code: '[UMAU000]',
|
|
string: 'The user is not authorized, please contact an administrator.'
|
|
}
|
|
};
|
|
console.log('[UserModel::authenticateUser] The user is disabled', { err: err, user: user });
|
|
resolve(loginObject);
|
|
}
|
|
else {
|
|
loginObject = {
|
|
status: 200,
|
|
authorized: false,
|
|
err: {
|
|
id: '001',
|
|
code: '[UMAU001]',
|
|
string: 'The user id or password you entered was invalid.'
|
|
}
|
|
};
|
|
console.log('[UserModel::authenticateUser] The user does not exist', { err: err, user: user });
|
|
resolve(loginObject);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
promise.then((result) => {
|
|
e.emit('authenticateUser', null, result);
|
|
})
|
|
.catch((err) => {
|
|
e.emit('authenticateUser', err, null);
|
|
});
|
|
},
|
|
|
|
createUser: (e, user) => {
|
|
const promise = new Promise((resolve, reject) => {
|
|
var userInstance = new UserModel(user);
|
|
|
|
userInstance.save((err, result) => {
|
|
console.log('createUser', { err: err, result: result, user: userInstance });
|
|
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
if (result) {
|
|
Reset.sendNewUser(null, result);
|
|
resolve(result);
|
|
}
|
|
});
|
|
});
|
|
|
|
promise.then((result) => {
|
|
e.emit('createUser', null, result);
|
|
})
|
|
.catch((err) => {
|
|
e.emit('createUser', err, null);
|
|
});
|
|
},
|
|
|
|
createUserGod: (e, user) => {
|
|
const promise = new Promise((resolve, reject) => {
|
|
hashPassword(user.password, (err, hashedPassword) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
if (hashedPassword) {
|
|
user.password = hashedPassword;
|
|
|
|
var userInstance = new UserModel(user);
|
|
|
|
userInstance.save((err, result) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
if (result) {
|
|
resolve(result);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
promise.then((result) => {
|
|
e.emit('createUserGod', null, result);
|
|
})
|
|
.catch((err) => {
|
|
e.emit('createUserGod', err, null);
|
|
});
|
|
},
|
|
|
|
createUserSetting: (e, userId, data) => {
|
|
const promise = new Promise((resolve, reject) => {
|
|
UserModel.findByIdAndUpdate(userId, { $push: { settings: data } }, (err, result) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
if (result) {
|
|
resolve(result);
|
|
}
|
|
});
|
|
});
|
|
|
|
promise.then((result) => {
|
|
e.emit('createUserSetting', null, result);
|
|
})
|
|
.catch((err) => {
|
|
e.emit('createUserSetting', err, null);
|
|
});
|
|
},
|
|
|
|
deleteUser: (e, id) => {
|
|
const promise = new Promise((resolve, reject) => {
|
|
UserModel.remove({ _id: id }, (err, result) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
if (result) {
|
|
resolve(result);
|
|
}
|
|
});
|
|
});
|
|
|
|
promise.then((result) => {
|
|
e.emit('deleteUser', null, result);
|
|
})
|
|
.catch((err) => {
|
|
e.emit('deleteUser', err, null);
|
|
});
|
|
},
|
|
|
|
findUser: (query, callback) => {
|
|
UserModel.findOne(query, (err, result) => {
|
|
if (err) {
|
|
callback(err, null);
|
|
}
|
|
|
|
if (result) {
|
|
callback(null, result);
|
|
}
|
|
});
|
|
},
|
|
|
|
forcePasswordReset: (e, id) => {
|
|
const promise = new Promise((resolve, reject) => {
|
|
UserModel.findByIdAndUpdate(id, { $set: { forceReset: true } }, { new: true }, (err, result) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
if (result) {
|
|
let resetPromise = new Promise((resetResolve, resetReject) => {
|
|
Reset.forceReset(null, result, (result) => {
|
|
if (result.success) {
|
|
resetResolve({ success: true, message: 'Force password reset initiated on the user.', result: result });
|
|
} else {
|
|
resetResolve({ success: false, message: 'There was an error initiating the forced password reset.', result: result });
|
|
}
|
|
});
|
|
});
|
|
|
|
resetPromise.then((result) => {
|
|
resolve(result);
|
|
})
|
|
.catch((err) => {
|
|
reject(err);
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
promise.then((result) => {
|
|
e.emit('forcePasswordReset', null, result);
|
|
})
|
|
.catch((err) => {
|
|
e.emit('forcePasswordReset', err, null);
|
|
});
|
|
},
|
|
|
|
getUsers: (e, query, restricted = true) => {
|
|
query = query || { find: {}, options: { sort: { 'name.last': 1, 'name.first': 1 }, limit: 0, skip: 0 }};
|
|
|
|
const promise = new Promise((resolve, reject) => {
|
|
var projection = 'userName name' + (restricted ? '' : ' title email permission avatar settings forceReset');
|
|
var query = UserModel.find(query.find, projection, query.options);
|
|
|
|
if (!restricted) {
|
|
query.populate('permission', 'name disabled manageRoles manageUsers manageCategories manageAppPreferences deleteVendor addVendorTag addVendorSample addVendorComment editVendor addNewVendor viewPrivateDetails viewPublicDetails')
|
|
}
|
|
|
|
query.exec((err, results) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
if (results) {
|
|
resolve(results);
|
|
}
|
|
});
|
|
});
|
|
|
|
promise.then((results) => {
|
|
if (e) {
|
|
e.emit('getUsers', null, results);
|
|
} else {
|
|
return results;
|
|
}
|
|
})
|
|
.catch((err) => {
|
|
if (e) {
|
|
e.emit('getUsers', err, null);
|
|
} else {
|
|
return err;
|
|
}
|
|
});
|
|
},
|
|
|
|
getUser: (e, id, restricted = true) => {
|
|
const promise = new Promise((resolve, reject) => {
|
|
var projection = 'userName name' + (restricted ? '' : ' title email permission avatar settings forceReset');
|
|
var query = UserModel
|
|
.findById(id)
|
|
.projection(projection);
|
|
|
|
if (!restricted) {
|
|
query.populate('permission', 'name disabled manageRoles manageUsers manageCategories manageAppPreferences deleteVendor addVendorTag addVendorSample addVendorComment editVendor addNewVendor viewPrivateDetails viewPublicDetails');
|
|
}
|
|
|
|
query.exec((err, result) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
if (result) {
|
|
resolve(result);
|
|
}
|
|
});
|
|
});
|
|
|
|
promise.then((result) => {
|
|
if (e) {
|
|
e.emit('getUser', null, result);
|
|
} else {
|
|
return result;
|
|
}
|
|
})
|
|
.catch((err) => {
|
|
if (e) {
|
|
e.emit('getUser', err, null);
|
|
} else {
|
|
return err;
|
|
}
|
|
});
|
|
},
|
|
|
|
getUserSetting: (e, id, key) => {
|
|
const promise = new Promise((resolve, reject) => {
|
|
UserModel.findOne({ _id: id }, (err, result) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
if (result) {
|
|
for (let i = 0; i < result.settings.length; i ++) {
|
|
if (result.settings[i].key === key) resolve(result.settings[i]);
|
|
}
|
|
|
|
reject({ method: "getUserSetting", error: "The specified settings key does not exist" });
|
|
}
|
|
});
|
|
});
|
|
|
|
promise.then((result) => {
|
|
e.emit('getUserSetting', null, result);
|
|
})
|
|
.catch((err) => {
|
|
e.emit('getUserSetting', err, null);
|
|
});
|
|
},
|
|
|
|
getUserSettings: (e, id) => {
|
|
const promise = new Promise((resolve, reject) => {
|
|
UserModel.findOne({_id: id}, (err, result) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
if (result) {
|
|
resolve(result.settings);
|
|
}
|
|
});
|
|
});
|
|
|
|
promise.then((result) => {
|
|
e.emit('getUserSettings', null, result);
|
|
})
|
|
.catch((err) => {
|
|
e.emit('getUserSettings', err, null);
|
|
});
|
|
},
|
|
|
|
isUserNameUnique: (e, username) => {
|
|
const promise = new Promise((resolve, reject) => {
|
|
UserModel.findOne({ userName: username }, (err, result) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
if (result) {
|
|
resolve({ unique: false, length: true });
|
|
} else {
|
|
resolve({ unique: true, length: true });
|
|
}
|
|
});
|
|
});
|
|
|
|
promise.then((result) => {
|
|
e.emit('isUserNameUnique', null, result);
|
|
})
|
|
.catch((err) => {
|
|
e.emit('isUserNameUnique', err, null);
|
|
});
|
|
},
|
|
|
|
updateUser: (e, id, user) => {
|
|
const promise = new Promise((resolve, reject) => {
|
|
UserModel
|
|
.findByIdAndUpdate(id, { $set: user }, { new: true })
|
|
.populate('permission', 'name disabled manageRoles manageUsers manageCategories manageAppPreferences deleteVendor addVendorTag addVendorSample addVendorComment editVendor addNewVendor viewPrivateDetails viewPublicDetails')
|
|
.exec((err, result) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
if (result) {
|
|
resolve(result);
|
|
}
|
|
});
|
|
});
|
|
|
|
promise.then((result) => {
|
|
e.emit('updateUser', null, result);
|
|
})
|
|
.catch((err) => {
|
|
e.emit('updateUser', err, null);
|
|
});
|
|
},
|
|
|
|
updateUserByUserName: (e, username, user) => {
|
|
const promise = new Promise((resolve, reject) => {
|
|
UserModel.update({ userName: username }, { $set: user }, { new: true }, (err, result) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
if (result) {
|
|
resolve(result);
|
|
}
|
|
});
|
|
});
|
|
|
|
promise.then((result) => {
|
|
e.emit('updateUserByUserName', null, result);
|
|
})
|
|
.catch((err) => {
|
|
e.emit('updateUserByUserName', err, null);
|
|
});
|
|
},
|
|
|
|
updateUserSetting: (e, userId, settingsId, data) => {
|
|
const promise = new Promise((resolve, reject) => {
|
|
var changed = {};
|
|
|
|
for (let property in data) {
|
|
changed['settings.$.' + property] = data[property];
|
|
}
|
|
|
|
UserModel.update({ _id: userId, 'settings._id': settingsId }, { $set: changed }, { new: true }, (err, result) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
if (result) {
|
|
resolve(result);
|
|
}
|
|
});
|
|
});
|
|
|
|
promise.then((result) => {
|
|
e.emit('updateUserSetting', null, result);
|
|
})
|
|
.catch((err) => {
|
|
e.emit('updateUserSetting', err, null);
|
|
});
|
|
},
|
|
|
|
updatePassword: (e, id, token, data) => {
|
|
const promise = new Promise((resolve, reject) => {
|
|
Reset.checkReset(null, id, token, (err, validatedId) => {
|
|
if (data.userId === validatedId) {
|
|
UserModel.findByIdAndUpdate(data.userId, { $set: { password: data.password, confirmPassword: data.confirmPassword, forceReset: false } }, { new: true }, (err, updated) => {
|
|
if (err) {
|
|
if (err.success === false) {
|
|
resolve(err);
|
|
} else {
|
|
reject(err);
|
|
}
|
|
}
|
|
|
|
if (updated) {
|
|
Reset.markUsed(null, id);
|
|
resolve({ success: true, updated: updated });
|
|
}
|
|
});
|
|
} else {
|
|
resolve({ success: false, message: 'The password reset link is not valid. Please request a new link.'});
|
|
}
|
|
});
|
|
|
|
});
|
|
|
|
promise.then((result) => {
|
|
e.emit('updatePassword', null, result);
|
|
})
|
|
.catch((err) => {
|
|
e.emit('updatePassword', err, null);
|
|
});
|
|
}
|
|
};
|