mirror of
https://github.com/mmumshad/ansible-playable.git
synced 2025-02-15 04:42:05 +00:00
258 lines
5.2 KiB
JavaScript
258 lines
5.2 KiB
JavaScript
'use strict';
|
|
/*eslint no-invalid-this:0*/
|
|
import crypto from 'crypto';
|
|
mongoose.Promise = require('bluebird');
|
|
import mongoose, {Schema} from 'mongoose';
|
|
import {registerEvents} from './user.events';
|
|
|
|
const authTypes = ['github', 'twitter', 'facebook', 'google'];
|
|
|
|
var UserSchema = new Schema({
|
|
name: String,
|
|
email: {
|
|
type: String,
|
|
lowercase: true,
|
|
required() {
|
|
if(authTypes.indexOf(this.provider) === -1) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
},
|
|
role: {
|
|
type: String,
|
|
default: 'user'
|
|
},
|
|
password: {
|
|
type: String,
|
|
required() {
|
|
if(authTypes.indexOf(this.provider) === -1) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
},
|
|
provider: String,
|
|
salt: String,
|
|
facebook: {},
|
|
google: {},
|
|
github: {}
|
|
});
|
|
|
|
/**
|
|
* Virtuals
|
|
*/
|
|
|
|
// Public profile information
|
|
UserSchema
|
|
.virtual('profile')
|
|
.get(function() {
|
|
return {
|
|
name: this.name,
|
|
role: this.role
|
|
};
|
|
});
|
|
|
|
// Non-sensitive info we'll be putting in the token
|
|
UserSchema
|
|
.virtual('token')
|
|
.get(function() {
|
|
return {
|
|
_id: this._id,
|
|
role: this.role
|
|
};
|
|
});
|
|
|
|
/**
|
|
* Validations
|
|
*/
|
|
|
|
// Validate empty email
|
|
UserSchema
|
|
.path('email')
|
|
.validate(function(email) {
|
|
if(authTypes.indexOf(this.provider) !== -1) {
|
|
return true;
|
|
}
|
|
return email.length;
|
|
}, 'Email cannot be blank');
|
|
|
|
// Validate empty password
|
|
UserSchema
|
|
.path('password')
|
|
.validate(function(password) {
|
|
if(authTypes.indexOf(this.provider) !== -1) {
|
|
return true;
|
|
}
|
|
return password.length;
|
|
}, 'Password cannot be blank');
|
|
|
|
// Validate email is not taken
|
|
UserSchema
|
|
.path('email')
|
|
.validate(function(value) {
|
|
if(authTypes.indexOf(this.provider) !== -1) {
|
|
return true;
|
|
}
|
|
|
|
return this.constructor.findOne({ email: value }).exec()
|
|
.then(user => {
|
|
if(user) {
|
|
if(this.id === user.id) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
})
|
|
.catch(function(err) {
|
|
throw err;
|
|
});
|
|
}, 'The specified email address is already in use.');
|
|
|
|
var validatePresenceOf = function(value) {
|
|
return value && value.length;
|
|
};
|
|
|
|
/**
|
|
* Pre-save hook
|
|
*/
|
|
UserSchema
|
|
.pre('save', function(next) {
|
|
// Handle new/update passwords
|
|
if(!this.isModified('password')) {
|
|
return next();
|
|
}
|
|
|
|
if(!validatePresenceOf(this.password)) {
|
|
if(authTypes.indexOf(this.provider) === -1) {
|
|
return next(new Error('Invalid password'));
|
|
} else {
|
|
return next();
|
|
}
|
|
}
|
|
|
|
// Make salt with a callback
|
|
this.makeSalt((saltErr, salt) => {
|
|
if(saltErr) {
|
|
return next(saltErr);
|
|
}
|
|
this.salt = salt;
|
|
this.encryptPassword(this.password, (encryptErr, hashedPassword) => {
|
|
if(encryptErr) {
|
|
return next(encryptErr);
|
|
}
|
|
this.password = hashedPassword;
|
|
return next();
|
|
});
|
|
});
|
|
});
|
|
|
|
/**
|
|
* Methods
|
|
*/
|
|
UserSchema.methods = {
|
|
/**
|
|
* Authenticate - check if the passwords are the same
|
|
*
|
|
* @param {String} password
|
|
* @param {Function} callback
|
|
* @return {Boolean}
|
|
* @api public
|
|
*/
|
|
authenticate(password, callback) {
|
|
if(!callback) {
|
|
return this.password === this.encryptPassword(password);
|
|
}
|
|
|
|
this.encryptPassword(password, (err, pwdGen) => {
|
|
if(err) {
|
|
return callback(err);
|
|
}
|
|
|
|
if(this.password === pwdGen) {
|
|
return callback(null, true);
|
|
} else {
|
|
return callback(null, false);
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Make salt
|
|
*
|
|
* @param {Number} [byteSize] - Optional salt byte size, default to 16
|
|
* @param {Function} callback
|
|
* @return {String}
|
|
* @api public
|
|
*/
|
|
makeSalt(...args) {
|
|
let byteSize;
|
|
let callback;
|
|
let defaultByteSize = 16;
|
|
|
|
if(typeof args[0] === 'function') {
|
|
callback = args[0];
|
|
byteSize = defaultByteSize;
|
|
} else if(typeof args[1] === 'function') {
|
|
callback = args[1];
|
|
} else {
|
|
throw new Error('Missing Callback');
|
|
}
|
|
|
|
if(!byteSize) {
|
|
byteSize = defaultByteSize;
|
|
}
|
|
|
|
return crypto.randomBytes(byteSize, (err, salt) => {
|
|
if(err) {
|
|
return callback(err);
|
|
} else {
|
|
return callback(null, salt.toString('base64'));
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Encrypt password
|
|
*
|
|
* @param {String} password
|
|
* @param {Function} callback
|
|
* @return {String}
|
|
* @api public
|
|
*/
|
|
encryptPassword(password, callback) {
|
|
if(!password || !this.salt) {
|
|
if(!callback) {
|
|
return null;
|
|
} else {
|
|
return callback('Missing password or salt');
|
|
}
|
|
}
|
|
|
|
var defaultIterations = 10000;
|
|
var defaultKeyLength = 64;
|
|
var salt = new Buffer(this.salt, 'base64');
|
|
|
|
if(!callback) {
|
|
// eslint-disable-next-line no-sync
|
|
return crypto.pbkdf2Sync(password, salt, defaultIterations,
|
|
defaultKeyLength, 'sha1')
|
|
.toString('base64');
|
|
}
|
|
|
|
return crypto.pbkdf2(password, salt, defaultIterations, defaultKeyLength,
|
|
'sha1', (err, key) => {
|
|
if(err) {
|
|
return callback(err);
|
|
} else {
|
|
return callback(null, key.toString('base64'));
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
registerEvents(UserSchema);
|
|
export default mongoose.model('User', UserSchema);
|