From 26d7d31ba3b1d26ea9a51e8626e4a4537867db94 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 25 Aug 2016 17:57:37 +0200 Subject: [PATCH] Server: encrypt password in database --- package.json | 1 + server/helpers/peertube-crypto.js | 21 +++++++++++++++ server/initializers/constants.js | 3 +++ server/initializers/installer.js | 4 +-- server/lib/oauth-model.js | 17 +++++++++++- server/models/user.js | 43 ++++++++++++++++++++++--------- 6 files changed, 74 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index c83ede5c2..5a215c7b9 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/server/helpers/peertube-crypto.js b/server/helpers/peertube-crypto.js index ef130ea5c..4783e9965 100644 --- a/server/helpers/peertube-crypto.js +++ b/server/helpers/peertube-crypto.js @@ -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) diff --git a/server/initializers/constants.js b/server/initializers/constants.js index ce9f8ad6c..dd4eff493 100644 --- a/server/initializers/constants.js +++ b/server/initializers/constants.js @@ -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, diff --git a/server/initializers/installer.js b/server/initializers/installer.js index c12187871..974402094 100644 --- a/server/initializers/installer.js +++ b/server/initializers/installer.js @@ -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) }) diff --git a/server/lib/oauth-model.js b/server/lib/oauth-model.js index d9f8b175a..6dab02fca 100644 --- a/server/lib/oauth-model.js +++ b/server/lib/oauth-model.js @@ -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) { diff --git a/server/models/user.js b/server/models/user.js index c9c35b3e2..e76aab2ce 100644 --- a/server/models/user.js +++ b/server/models/user.js @@ -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 - } -} -- 2.25.1