Server: encrypt password in database
authorChocobozzz <florian.bigard@gmail.com>
Thu, 25 Aug 2016 15:57:37 +0000 (17:57 +0200)
committerChocobozzz <florian.bigard@gmail.com>
Thu, 25 Aug 2016 15:57:37 +0000 (17:57 +0200)
package.json
server/helpers/peertube-crypto.js
server/initializers/constants.js
server/initializers/installer.js
server/lib/oauth-model.js
server/models/user.js

index c83ede5c27baed011de8071b3214468ff88f0678..5a215c7b9b9aa27958084a622cfcbcd0a000eb57 100644 (file)
@@ -35,6 +35,7 @@
   },
   "dependencies": {
     "async": "^2.0.0",
+    "bcrypt": "^0.8.7",
     "bittorrent-tracker": "^8.0.0",
     "body-parser": "^1.12.4",
     "concurrently": "^2.0.0",
index ef130ea5c125008436b7d561f3940df97d0a711f..4783e996596419e173c53f894350ca939a3c571e 100644 (file)
@@ -1,5 +1,6 @@
 'use strict'
 
+const bcrypt = require('bcrypt')
 const crypto = require('crypto')
 const fs = require('fs')
 const openssl = require('openssl-wrapper')
@@ -12,7 +13,9 @@ const algorithm = 'aes-256-ctr'
 
 const peertubeCrypto = {
   checkSignature: checkSignature,
+  comparePassword: comparePassword,
   createCertsIfNotExist: createCertsIfNotExist,
+  cryptPassword: cryptPassword,
   decrypt: decrypt,
   encrypt: encrypt,
   sign: sign
@@ -24,6 +27,14 @@ function checkSignature (publicKey, rawData, hexSignature) {
   return isValid
 }
 
+function comparePassword (plainPassword, hashPassword, callback) {
+  bcrypt.compare(plainPassword, hashPassword, function (err, isPasswordMatch) {
+    if (err) return callback(err)
+
+    return callback(null, isPasswordMatch)
+  })
+}
+
 function createCertsIfNotExist (callback) {
   certsExist(function (exist) {
     if (exist === true) {
@@ -36,6 +47,16 @@ function createCertsIfNotExist (callback) {
   })
 }
 
+function cryptPassword (password, callback) {
+  bcrypt.genSalt(constants.BCRYPT_SALT_SIZE, function (err, salt) {
+    if (err) return callback(err)
+
+    bcrypt.hash(password, salt, function (err, hash) {
+      return callback(err, hash)
+    })
+  })
+}
+
 function decrypt (key, data, callback) {
   fs.readFile(constants.CONFIG.STORAGE.CERT_DIR + 'peertube.key.pem', function (err, file) {
     if (err) return callback(err)
index ce9f8ad6c20605e04c675c629e57d477008240c4..dd4eff493eff068a18382f3f437262e42191312d 100644 (file)
@@ -6,6 +6,8 @@ const path = require('path')
 // API version of our pod
 const API_VERSION = 'v1'
 
+const BCRYPT_SALT_SIZE = 10
+
 const CONFIG = {
   DATABASE: {
     DBNAME: 'peertube' + config.get('database.suffix'),
@@ -115,6 +117,7 @@ if (isTestInstance() === true) {
 
 module.exports = {
   API_VERSION: API_VERSION,
+  BCRYPT_SALT_SIZE: BCRYPT_SALT_SIZE,
   CONFIG: CONFIG,
   CONSTRAINTS_FIELDS: CONSTRAINTS_FIELDS,
   FRIEND_SCORE: FRIEND_SCORE,
index c12187871653bc124a1ae534be86c34f467b8895..974402094a168ce24d6f492147a5a25a42dffc8b 100644 (file)
@@ -114,8 +114,8 @@ function createOAuthAdminIfNotExist (callback) {
     user.save(function (err, createdUser) {
       if (err) return callback(err)
 
-      logger.info('Username: ' + createdUser.username)
-      logger.info('User password: ' + createdUser.password)
+      logger.info('Username: ' + username)
+      logger.info('User password: ' + password)
 
       return callback(null)
     })
index d9f8b175a6df62698fd810ec4c5ec85e3dda618b..6dab02fca80e1d882d6710d899bffe9c42f06d65 100644 (file)
@@ -41,7 +41,22 @@ function getRefreshToken (refreshToken, callback) {
 function getUser (username, password) {
   logger.debug('Getting User (username: ' + username + ', password: ' + password + ').')
 
-  return User.getByUsernameAndPassword(username, password)
+  return User.getByUsername(username).then(function (user) {
+    if (!user) return null
+
+    // We need to return a promise
+    return new Promise(function (resolve, reject) {
+      return user.isPasswordMatch(password, function (err, isPasswordMatch) {
+        if (err) return reject(err)
+
+        if (isPasswordMatch === true) {
+          return resolve(user)
+        }
+
+        return resolve(null)
+      })
+    })
+  })
 }
 
 function revokeToken (token) {
index c9c35b3e2dffc0c06cbe6d35f379135998aae583..e76aab2ce5ad25d1459b90c9a7423cce8acef7d7 100644 (file)
@@ -2,6 +2,7 @@ const mongoose = require('mongoose')
 
 const customUsersValidators = require('../helpers/custom-validators').users
 const modelUtils = require('./utils')
+const peertubeCrypto = require('../helpers/peertube-crypto')
 
 // ---------------------------------------------------------------------------
 
@@ -20,27 +21,53 @@ UserSchema.path('username').required(customUsersValidators.isUserUsernameValid)
 UserSchema.path('role').validate(customUsersValidators.isUserRoleValid)
 
 UserSchema.methods = {
+  isPasswordMatch: isPasswordMatch,
   toFormatedJSON: toFormatedJSON
 }
 
 UserSchema.statics = {
   countTotal: countTotal,
-  getByUsernameAndPassword: getByUsernameAndPassword,
+  getByUsername: getByUsername,
   listForApi: listForApi,
   loadById: loadById,
   loadByUsername: loadByUsername
 }
 
+UserSchema.pre('save', function (next) {
+  const user = this
+
+  peertubeCrypto.cryptPassword(this.password, function (err, hash) {
+    if (err) return next(err)
+
+    user.password = hash
+
+    return next()
+  })
+})
+
 mongoose.model('User', UserSchema)
 
-// ---------------------------------------------------------------------------
+// ------------------------------ METHODS ------------------------------
+
+function isPasswordMatch (password, callback) {
+  return peertubeCrypto.comparePassword(password, this.password, callback)
+}
+
+function toFormatedJSON () {
+  return {
+    id: this._id,
+    username: this.username,
+    role: this.role
+  }
+}
+// ------------------------------ STATICS ------------------------------
 
 function countTotal (callback) {
   return this.count(callback)
 }
 
-function getByUsernameAndPassword (username, password) {
-  return this.findOne({ username: username, password: password })
+function getByUsername (username) {
+  return this.findOne({ username: username })
 }
 
 function listForApi (start, count, sort, callback) {
@@ -55,11 +82,3 @@ function loadById (id, callback) {
 function loadByUsername (username, callback) {
   return this.findOne({ username: username }, callback)
 }
-
-function toFormatedJSON () {
-  return {
-    id: this._id,
-    username: this.username,
-    role: this.role
-  }
-}