|
@@ -1,27 +1,9 @@
|
|
|
-/* eslint-disable
|
|
|
|
|
- camelcase,
|
|
|
|
|
- handle-callback-err,
|
|
|
|
|
- max-len,
|
|
|
|
|
- no-unused-vars,
|
|
|
|
|
-*/
|
|
|
|
|
-// TODO: This file was created by bulk-decaffeinate.
|
|
|
|
|
-// Fix any style issues and re-enable lint.
|
|
|
|
|
-/*
|
|
|
|
|
- * decaffeinate suggestions:
|
|
|
|
|
- * DS102: Remove unnecessary code created because of implicit returns
|
|
|
|
|
- * DS202: Simplify dynamic range loops
|
|
|
|
|
- * DS207: Consider shorter variations of null checks
|
|
|
|
|
- * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
|
|
|
|
- */
|
|
|
|
|
let AuthenticationManager
|
|
let AuthenticationManager
|
|
|
const Settings = require('settings-sharelatex')
|
|
const Settings = require('settings-sharelatex')
|
|
|
const { User } = require('../../models/User')
|
|
const { User } = require('../../models/User')
|
|
|
const { db, ObjectId } = require('../../infrastructure/mongojs')
|
|
const { db, ObjectId } = require('../../infrastructure/mongojs')
|
|
|
-const crypto = require('crypto')
|
|
|
|
|
const bcrypt = require('bcrypt')
|
|
const bcrypt = require('bcrypt')
|
|
|
const EmailHelper = require('../Helpers/EmailHelper')
|
|
const EmailHelper = require('../Helpers/EmailHelper')
|
|
|
-const Errors = require('../Errors/Errors')
|
|
|
|
|
-const UserGetter = require('../User/UserGetter')
|
|
|
|
|
const V1Handler = require('../V1/V1Handler')
|
|
const V1Handler = require('../V1/V1Handler')
|
|
|
|
|
|
|
|
const BCRYPT_ROUNDS = Settings.security.bcryptRounds || 12
|
|
const BCRYPT_ROUNDS = Settings.security.bcryptRounds || 12
|
|
@@ -29,13 +11,10 @@ const BCRYPT_MINOR_VERSION = Settings.security.bcryptMinorVersion || 'a'
|
|
|
|
|
|
|
|
const _checkWriteResult = function(result, callback) {
|
|
const _checkWriteResult = function(result, callback) {
|
|
|
// for MongoDB
|
|
// for MongoDB
|
|
|
- if (callback == null) {
|
|
|
|
|
- callback = function(error, updated) {}
|
|
|
|
|
- }
|
|
|
|
|
if (result && result.nModified === 1) {
|
|
if (result && result.nModified === 1) {
|
|
|
- return callback(null, true)
|
|
|
|
|
|
|
+ callback(null, true)
|
|
|
} else {
|
|
} else {
|
|
|
- return callback(null, false)
|
|
|
|
|
|
|
+ callback(null, false)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -44,50 +23,38 @@ module.exports = AuthenticationManager = {
|
|
|
// Using Mongoose for legacy reasons here. The returned User instance
|
|
// Using Mongoose for legacy reasons here. The returned User instance
|
|
|
// gets serialized into the session and there may be subtle differences
|
|
// gets serialized into the session and there may be subtle differences
|
|
|
// between the user returned by Mongoose vs mongojs (such as default values)
|
|
// between the user returned by Mongoose vs mongojs (such as default values)
|
|
|
- if (callback == null) {
|
|
|
|
|
- callback = function(error, user) {}
|
|
|
|
|
- }
|
|
|
|
|
- return User.findOne(query, (error, user) => {
|
|
|
|
|
- if (error != null) {
|
|
|
|
|
|
|
+ User.findOne(query, (error, user) => {
|
|
|
|
|
+ if (error) {
|
|
|
return callback(error)
|
|
return callback(error)
|
|
|
}
|
|
}
|
|
|
- if (user != null) {
|
|
|
|
|
- if (user.hashedPassword != null) {
|
|
|
|
|
- return bcrypt.compare(password, user.hashedPassword, function(
|
|
|
|
|
- error,
|
|
|
|
|
- match
|
|
|
|
|
- ) {
|
|
|
|
|
- if (error != null) {
|
|
|
|
|
- return callback(error)
|
|
|
|
|
- }
|
|
|
|
|
- if (match) {
|
|
|
|
|
- return AuthenticationManager.checkRounds(
|
|
|
|
|
- user,
|
|
|
|
|
- user.hashedPassword,
|
|
|
|
|
- password,
|
|
|
|
|
- function(err) {
|
|
|
|
|
- if (err != null) {
|
|
|
|
|
- return callback(err)
|
|
|
|
|
- }
|
|
|
|
|
- return callback(null, user)
|
|
|
|
|
- }
|
|
|
|
|
- )
|
|
|
|
|
- } else {
|
|
|
|
|
- return callback(null, null)
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- } else {
|
|
|
|
|
- return callback(null, null)
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
|
|
+ if (!user || !user.hashedPassword) {
|
|
|
return callback(null, null)
|
|
return callback(null, null)
|
|
|
}
|
|
}
|
|
|
|
|
+ bcrypt.compare(password, user.hashedPassword, function(error, match) {
|
|
|
|
|
+ if (error) {
|
|
|
|
|
+ return callback(error)
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!match) {
|
|
|
|
|
+ return callback(null, null)
|
|
|
|
|
+ }
|
|
|
|
|
+ AuthenticationManager.checkRounds(
|
|
|
|
|
+ user,
|
|
|
|
|
+ user.hashedPassword,
|
|
|
|
|
+ password,
|
|
|
|
|
+ function(err) {
|
|
|
|
|
+ if (err) {
|
|
|
|
|
+ return callback(err)
|
|
|
|
|
+ }
|
|
|
|
|
+ callback(null, user)
|
|
|
|
|
+ }
|
|
|
|
|
+ )
|
|
|
|
|
+ })
|
|
|
})
|
|
})
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
validateEmail(email) {
|
|
validateEmail(email) {
|
|
|
const parsed = EmailHelper.parseEmail(email)
|
|
const parsed = EmailHelper.parseEmail(email)
|
|
|
- if (parsed == null) {
|
|
|
|
|
|
|
+ if (!parsed) {
|
|
|
return { message: 'email not valid' }
|
|
return { message: 'email not valid' }
|
|
|
}
|
|
}
|
|
|
return null
|
|
return null
|
|
@@ -118,10 +85,10 @@ module.exports = AuthenticationManager = {
|
|
|
max = 72
|
|
max = 72
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (!(password.length >= min)) {
|
|
|
|
|
|
|
+ if (password.length < min) {
|
|
|
return { message: 'password is too short' }
|
|
return { message: 'password is too short' }
|
|
|
}
|
|
}
|
|
|
- if (!(password.length <= max)) {
|
|
|
|
|
|
|
+ if (password.length > max) {
|
|
|
return { message: 'password is too long' }
|
|
return { message: 'password is too long' }
|
|
|
}
|
|
}
|
|
|
if (
|
|
if (
|
|
@@ -133,51 +100,45 @@ module.exports = AuthenticationManager = {
|
|
|
return null
|
|
return null
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
- setUserPassword(user_id, password, callback) {
|
|
|
|
|
- AuthenticationManager.setUserPasswordInV2(user_id, password, callback)
|
|
|
|
|
|
|
+ setUserPassword(userId, password, callback) {
|
|
|
|
|
+ AuthenticationManager.setUserPasswordInV2(userId, password, callback)
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
checkRounds(user, hashedPassword, password, callback) {
|
|
checkRounds(user, hashedPassword, password, callback) {
|
|
|
// Temporarily disable this function, TODO: re-enable this
|
|
// Temporarily disable this function, TODO: re-enable this
|
|
|
- if (callback == null) {
|
|
|
|
|
- callback = function(error) {}
|
|
|
|
|
- }
|
|
|
|
|
if (Settings.security.disableBcryptRoundsUpgrades) {
|
|
if (Settings.security.disableBcryptRoundsUpgrades) {
|
|
|
return callback()
|
|
return callback()
|
|
|
}
|
|
}
|
|
|
// check current number of rounds and rehash if necessary
|
|
// check current number of rounds and rehash if necessary
|
|
|
const currentRounds = bcrypt.getRounds(hashedPassword)
|
|
const currentRounds = bcrypt.getRounds(hashedPassword)
|
|
|
if (currentRounds < BCRYPT_ROUNDS) {
|
|
if (currentRounds < BCRYPT_ROUNDS) {
|
|
|
- return AuthenticationManager.setUserPassword(user._id, password, callback)
|
|
|
|
|
|
|
+ AuthenticationManager.setUserPassword(user._id, password, callback)
|
|
|
} else {
|
|
} else {
|
|
|
- return callback()
|
|
|
|
|
|
|
+ callback()
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
hashPassword(password, callback) {
|
|
hashPassword(password, callback) {
|
|
|
- return bcrypt.genSalt(BCRYPT_ROUNDS, BCRYPT_MINOR_VERSION, function(
|
|
|
|
|
- error,
|
|
|
|
|
- salt
|
|
|
|
|
- ) {
|
|
|
|
|
- if (error != null) {
|
|
|
|
|
|
|
+ bcrypt.genSalt(BCRYPT_ROUNDS, BCRYPT_MINOR_VERSION, function(error, salt) {
|
|
|
|
|
+ if (error) {
|
|
|
return callback(error)
|
|
return callback(error)
|
|
|
}
|
|
}
|
|
|
- return bcrypt.hash(password, salt, callback)
|
|
|
|
|
|
|
+ bcrypt.hash(password, salt, callback)
|
|
|
})
|
|
})
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
- setUserPasswordInV2(user_id, password, callback) {
|
|
|
|
|
|
|
+ setUserPasswordInV2(userId, password, callback) {
|
|
|
const validation = this.validatePassword(password)
|
|
const validation = this.validatePassword(password)
|
|
|
- if (validation != null) {
|
|
|
|
|
|
|
+ if (validation) {
|
|
|
return callback(validation.message)
|
|
return callback(validation.message)
|
|
|
}
|
|
}
|
|
|
- return this.hashPassword(password, function(error, hash) {
|
|
|
|
|
- if (error != null) {
|
|
|
|
|
|
|
+ this.hashPassword(password, function(error, hash) {
|
|
|
|
|
+ if (error) {
|
|
|
return callback(error)
|
|
return callback(error)
|
|
|
}
|
|
}
|
|
|
- return db.users.update(
|
|
|
|
|
|
|
+ db.users.update(
|
|
|
{
|
|
{
|
|
|
- _id: ObjectId(user_id.toString())
|
|
|
|
|
|
|
+ _id: ObjectId(userId.toString())
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
$set: {
|
|
$set: {
|
|
@@ -188,58 +149,51 @@ module.exports = AuthenticationManager = {
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
function(updateError, result) {
|
|
function(updateError, result) {
|
|
|
- if (updateError != null) {
|
|
|
|
|
|
|
+ if (updateError) {
|
|
|
return callback(updateError)
|
|
return callback(updateError)
|
|
|
}
|
|
}
|
|
|
- return _checkWriteResult(result, callback)
|
|
|
|
|
|
|
+ _checkWriteResult(result, callback)
|
|
|
}
|
|
}
|
|
|
)
|
|
)
|
|
|
})
|
|
})
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
- setUserPasswordInV1(v1_user_id, password, callback) {
|
|
|
|
|
|
|
+ setUserPasswordInV1(v1UserId, password, callback) {
|
|
|
const validation = this.validatePassword(password)
|
|
const validation = this.validatePassword(password)
|
|
|
- if (validation != null) {
|
|
|
|
|
|
|
+ if (validation) {
|
|
|
return callback(validation.message)
|
|
return callback(validation.message)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return V1Handler.doPasswordReset(v1_user_id, password, function(
|
|
|
|
|
- error,
|
|
|
|
|
- reset
|
|
|
|
|
- ) {
|
|
|
|
|
- if (error != null) {
|
|
|
|
|
|
|
+ V1Handler.doPasswordReset(v1UserId, password, function(error, reset) {
|
|
|
|
|
+ if (error) {
|
|
|
return callback(error)
|
|
return callback(error)
|
|
|
}
|
|
}
|
|
|
- return callback(error, reset)
|
|
|
|
|
|
|
+ callback(error, reset)
|
|
|
})
|
|
})
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
_passwordCharactersAreValid(password) {
|
|
_passwordCharactersAreValid(password) {
|
|
|
- let digits, letters, letters_up, symbols
|
|
|
|
|
|
|
+ let digits, letters, lettersUp, symbols
|
|
|
if (
|
|
if (
|
|
|
Settings.passwordStrengthOptions &&
|
|
Settings.passwordStrengthOptions &&
|
|
|
Settings.passwordStrengthOptions.chars
|
|
Settings.passwordStrengthOptions.chars
|
|
|
) {
|
|
) {
|
|
|
digits = Settings.passwordStrengthOptions.chars.digits
|
|
digits = Settings.passwordStrengthOptions.chars.digits
|
|
|
letters = Settings.passwordStrengthOptions.chars.letters
|
|
letters = Settings.passwordStrengthOptions.chars.letters
|
|
|
- letters_up = Settings.passwordStrengthOptions.chars.letters_up
|
|
|
|
|
|
|
+ lettersUp = Settings.passwordStrengthOptions.chars.letters_up
|
|
|
symbols = Settings.passwordStrengthOptions.chars.symbols
|
|
symbols = Settings.passwordStrengthOptions.chars.symbols
|
|
|
}
|
|
}
|
|
|
digits = digits || '1234567890'
|
|
digits = digits || '1234567890'
|
|
|
letters = letters || 'abcdefghijklmnopqrstuvwxyz'
|
|
letters = letters || 'abcdefghijklmnopqrstuvwxyz'
|
|
|
- letters_up = letters_up || 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
|
|
|
|
|
|
+ lettersUp = lettersUp || 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
|
|
symbols = symbols || '@#$%^&*()-_=+[]{};:<>/?!£€.,'
|
|
symbols = symbols || '@#$%^&*()-_=+[]{};:<>/?!£€.,'
|
|
|
|
|
|
|
|
- for (
|
|
|
|
|
- let charIndex = 0, end = password.length - 1, asc = end >= 0;
|
|
|
|
|
- asc ? charIndex <= end : charIndex >= end;
|
|
|
|
|
- asc ? charIndex++ : charIndex--
|
|
|
|
|
- ) {
|
|
|
|
|
|
|
+ for (let charIndex = 0; charIndex <= password.length - 1; charIndex++) {
|
|
|
if (
|
|
if (
|
|
|
- !(digits.indexOf(password[charIndex]) > -1) &&
|
|
|
|
|
- !(letters.indexOf(password[charIndex]) > -1) &&
|
|
|
|
|
- !(letters_up.indexOf(password[charIndex]) > -1) &&
|
|
|
|
|
- !(symbols.indexOf(password[charIndex]) > -1)
|
|
|
|
|
|
|
+ digits.indexOf(password[charIndex]) === -1 &&
|
|
|
|
|
+ letters.indexOf(password[charIndex]) === -1 &&
|
|
|
|
|
+ lettersUp.indexOf(password[charIndex]) === -1 &&
|
|
|
|
|
+ symbols.indexOf(password[charIndex]) === -1
|
|
|
) {
|
|
) {
|
|
|
return false
|
|
return false
|
|
|
}
|
|
}
|