Files

550 lines
16 KiB
JavaScript

const Authentication = require('../modules/authentication');
const Mongoose = require('mongoose');
const Reset = require('./reset');
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 } },
email: { type: String, required: true, unique: true },
can: [{ type: String, enum: ['add','edit','delete','manage','super','update','view'], default: ['view'] }],
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);
var logger = require('../modules/logger');
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 });
logger.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) });
logger.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) });
logger.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 email password can forceReset')
.exec((err, result) => {
if (err) {
loginObject = {
status: 200,
authorized: false,
err: {
id: '005',
code: '[UMAU005]',
string: 'There was an error authenticating the user.'
}
}
logger.debug('[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) {
logger.debug('[UserModel::authenticateUser] Error validating password', { err: err, user: user });
reject(err);
}
loginObject = {
status: 200,
authorized: valid
};
if (valid) {
loginObject.user = {
_id: user._id,
username: user.username,
name: user.name,
email: user.email,
can: user.can
};
loginObject.timestamp = Date.now();
logger.debug('[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.'
};
logger.debug('[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.'
}
};
logger.error('[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.'
}
};
logger.debug('[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.'
}
};
logger.debug('[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) => {
logger.debug('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);
});
},
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;
}
});
},
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);
});
},
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);
});
}
};