Advertisement
Guest User

Untitled

a guest
Jul 8th, 2017
101
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. 'use strict';
  2.  
  3. /**
  4.  * Module dependencies
  5.  */
  6. var mongoose = require('mongoose'),
  7.   path = require('path'),
  8.   config = require(path.resolve('./config/config')),
  9.   Schema = mongoose.Schema,
  10.   crypto = require('crypto'),
  11.   validator = require('validator'),
  12.   generatePassword = require('generate-password'),
  13.   owasp = require('owasp-password-strength-test');
  14.  
  15. owasp.config(config.shared.owasp);
  16.  
  17.  
  18. /**
  19.  * A Validation function for local strategy properties
  20.  */
  21. var validateLocalStrategyProperty = function (property) {
  22.   return ((this.provider !== 'local' && !this.updated) || property.length);
  23. };
  24.  
  25. /**
  26.  * A Validation function for local strategy email
  27.  */
  28. var validateLocalStrategyEmail = function (email) {
  29.   return ((this.provider !== 'local' && !this.updated) || validator.isEmail(email, { require_tld: false }));
  30. };
  31.  
  32. /**
  33.  * A Validation function for username
  34.  * - at least 3 characters
  35.  * - only a-z0-9_-.
  36.  * - contain at least one alphanumeric character
  37.  * - not in list of illegal usernames
  38.  * - no consecutive dots: "." ok, ".." nope
  39.  * - not begin or end with "."
  40.  */
  41.  
  42. var validateUsername = function(username) {
  43.   var usernameRegex = /^(?=[\w.-]+$)(?!.*[._-]{2})(?!\.)(?!.*\.$).{3,34}$/;
  44.   return (
  45.     this.provider !== 'local' ||
  46.     (username && usernameRegex.test(username) && config.illegalUsernames.indexOf(username) < 0)
  47.   );
  48. };
  49.  
  50. /**
  51.  * User Schema
  52.  */
  53. var UserSchema = new Schema({
  54.   firstName: {
  55.     type: String,
  56.     trim: true,
  57.     default: '',
  58.     validate: [validateLocalStrategyProperty, 'Please fill in your first name']
  59.   },
  60.   lastName: {
  61.     type: String,
  62.     trim: true,
  63.     default: '',
  64.     validate: [validateLocalStrategyProperty, 'Please fill in your last name']
  65.   },
  66.   displayName: {
  67.     type: String,
  68.     trim: true
  69.   },
  70.   email: {
  71.     type: String,
  72.     index: {
  73.       unique: true,
  74.       sparse: true // For this to work on a previously indexed field, the index must be dropped & the application restarted.
  75.     },
  76.     lowercase: true,
  77.     trim: true,
  78.     default: '',
  79.     validate: [validateLocalStrategyEmail, 'Please fill a valid email address']
  80.   },
  81.   username: {
  82.     type: String,
  83.     unique: 'Username already exists',
  84.     required: 'Please fill in a username',
  85.     validate: [validateUsername, 'Please enter a valid username: 3+ characters long, non restricted word, characters "_-.", no consecutive dots, does not begin or end with dots, letters a-z and numbers 0-9.'],
  86.     lowercase: true,
  87.     trim: true
  88.   },
  89.   password: {
  90.     type: String,
  91.     default: ''
  92.   },
  93.   salt: {
  94.     type: String
  95.   },
  96.   profileImageURL: {
  97.     type: String,
  98.     default: 'modules/users/client/img/profile/default.png'
  99.   },
  100.   provider: {
  101.     type: String,
  102.     required: 'Provider is required'
  103.   },
  104.   providerData: {},
  105.   additionalProvidersData: {},
  106.   roles: {
  107.     type: [{
  108.       type: String,
  109.       enum: ['user', 'admin']
  110.     }],
  111.     default: ['user'],
  112.     required: 'Please provide at least one role'
  113.   },
  114.   updated: {
  115.     type: Date
  116.   },
  117.   created: {
  118.     type: Date,
  119.     default: Date.now
  120.   },
  121.   /* For reset password */
  122.   resetPasswordToken: {
  123.     type: String
  124.   },
  125.   resetPasswordExpires: {
  126.     type: Date
  127.   }
  128. });
  129.  
  130. /**
  131.  * Hook a pre save method to hash the password
  132.  */
  133. UserSchema.pre('save', function (next) {
  134.   if (this.password && this.isModified('password')) {
  135.     this.salt = crypto.randomBytes(16).toString('base64');
  136.     this.password = this.hashPassword(this.password);
  137.   }
  138.  
  139.   next();
  140. });
  141.  
  142. /**
  143.  * Hook a pre validate method to test the local password
  144.  */
  145. UserSchema.pre('validate', function (next) {
  146.   if (this.provider === 'local' && this.password && this.isModified('password')) {
  147.     var result = owasp.test(this.password);
  148.     if (result.errors.length) {
  149.       var error = result.errors.join(' ');
  150.       this.invalidate('password', error);
  151.     }
  152.   }
  153.  
  154.   next();
  155. });
  156.  
  157. /**
  158.  * Create instance method for hashing a password
  159.  */
  160. UserSchema.methods.hashPassword = function (password) {
  161.   if (this.salt && password) {
  162.     return crypto.pbkdf2Sync(password, new Buffer(this.salt, 'base64'), 10000, 64, 'SHA1').toString('base64');
  163.   } else {
  164.     return password;
  165.   }
  166. };
  167.  
  168. /**
  169.  * Create instance method for authenticating user
  170.  */
  171. UserSchema.methods.authenticate = function (password) {
  172.   return this.password === this.hashPassword(password);
  173. };
  174.  
  175. /**
  176.  * Find possible not used username
  177.  */
  178. UserSchema.statics.findUniqueUsername = function (username, suffix, callback) {
  179.   var _this = this;
  180.   var possibleUsername = username.toLowerCase() + (suffix || '');
  181.  
  182.   _this.findOne({
  183.     username: possibleUsername
  184.   }, function (err, user) {
  185.     if (!err) {
  186.       if (!user) {
  187.         callback(possibleUsername);
  188.       } else {
  189.         return _this.findUniqueUsername(username, (suffix || 0) + 1, callback);
  190.       }
  191.     } else {
  192.       callback(null);
  193.     }
  194.   });
  195. };
  196.  
  197. /**
  198. * Generates a random passphrase that passes the owasp test
  199. * Returns a promise that resolves with the generated passphrase, or rejects with an error if something goes wrong.
  200. * NOTE: Passphrases are only tested against the required owasp strength tests, and not the optional tests.
  201. */
  202. UserSchema.statics.generateRandomPassphrase = function () {
  203.   return new Promise(function (resolve, reject) {
  204.     var password = '';
  205.     var repeatingCharacters = new RegExp('(.)\\1{2,}', 'g');
  206.  
  207.     // iterate until the we have a valid passphrase
  208.     // NOTE: Should rarely iterate more than once, but we need this to ensure no repeating characters are present
  209.     while (password.length < 20 || repeatingCharacters.test(password)) {
  210.       // build the random password
  211.       password = generatePassword.generate({
  212.         length: Math.floor(Math.random() * (20)) + 20, // randomize length between 20 and 40 characters
  213.         numbers: true,
  214.         symbols: false,
  215.         uppercase: true,
  216.         excludeSimilarCharacters: true
  217.       });
  218.  
  219.       // check if we need to remove any repeating characters
  220.       password = password.replace(repeatingCharacters, '');
  221.     }
  222.  
  223.     // Send the rejection back if the passphrase fails to pass the strength test
  224.     if (owasp.test(password).errors.length) {
  225.       reject(new Error('An unexpected problem occured while generating the random passphrase'));
  226.     } else {
  227.       // resolve with the validated passphrase
  228.       resolve(password);
  229.     }
  230.   });
  231. };
  232.  
  233. mongoose.model('User', UserSchema);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement