From 6d64f64da078e19f0030d16ef648cad71ec31ee0 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Thu, 13 Aug 2015 10:58:42 -0300 Subject: [PATCH 1/2] rm parts of the unused emailstore --- config/config.js | 1 - package.json | 4 +- plugins/config-emailstore.js | 14 -- plugins/emailstore.js | 270 +---------------------------------- plugins/ratelimiter.js | 12 +- 5 files changed, 12 insertions(+), 289 deletions(-) delete mode 100644 plugins/config-emailstore.js diff --git a/config/config.js b/config/config.js index b15e04046..96402b409 100644 --- a/config/config.js +++ b/config/config.js @@ -94,7 +94,6 @@ module.exports = { enableRatelimiter: enableRatelimiter, ratelimiter: require('../plugins/config-ratelimiter.js'), enableEmailstore: enableEmailstore, - emailstore: require('../plugins/config-emailstore'), enableCurrencyRates: enableCurrencyRates, currencyrates: require('../plugins/config-currencyrates'), loggerLevel: loggerLevel, diff --git a/package.json b/package.json index b51a0e2e7..0b06e9880 100644 --- a/package.json +++ b/package.json @@ -62,15 +62,13 @@ "microtime": "^0.6.0", "mkdirp": "^0.5.0", "moment": "~2.5.0", - "nodemailer": "^1.3.0", "preconditions": "^1.0.7", "request": "^2.48.0", "socket.io": "1.0.6", "socket.io-client": "1.0.6", "soop": "=0.1.5", "winston": "*", - "xmlhttprequest": "~1.6.0", - "nodemailer-smtp-transport": "*" + "xmlhttprequest": "~1.6.0" }, "devDependencies": { "chai": "*", diff --git a/plugins/config-emailstore.js b/plugins/config-emailstore.js deleted file mode 100644 index 3cee82260..000000000 --- a/plugins/config-emailstore.js +++ /dev/null @@ -1,14 +0,0 @@ -var smtpTransport = require('nodemailer-smtp-transport'); - -// you can use Gmail or any other nodemailer transport -var auth = { - host: 'localhost', - port: 25, - ignoreTLS: true, -}; -// -// -module.exports = { - confirmEmailHost: 'https://insight.bitpay.com', - email: smtpTransport(auth), -}; diff --git a/plugins/emailstore.js b/plugins/emailstore.js index 70a8e9493..10481c41e 100644 --- a/plugins/emailstore.js +++ b/plugins/emailstore.js @@ -11,7 +11,6 @@ var crypto = require('crypto'); var fs = require('fs'); var levelup = require('levelup'); - var nodemailer = require('nodemailer'); var querystring = require('querystring'); var moment = require('moment'); @@ -102,24 +101,11 @@ */ emailPlugin.init = function(config) { logger.info('Using emailstore plugin'); + config = config || {}; var path = globalConfig.leveldb + '/emailstore' + (globalConfig.name ? ('-' + globalConfig.name) : ''); emailPlugin.db = config.db || globalConfig.db || levelup(path); - - emailPlugin.email = config.emailTransport || nodemailer.createTransport(config.email); - - emailPlugin.textTemplate = config.textTemplate || 'copay.plain'; - emailPlugin.htmlTemplate = config.htmlTemplate || 'copay.html'; - emailPlugin.crypto = config.crypto || crypto; - - emailPlugin.confirmUrl = ( - process.env.INSIGHT_EMAIL_CONFIRM_HOST || config.confirmUrl || 'https://insight.bitpay.com' - ) + globalConfig.apiPrefix + '/email/validate'; - - emailPlugin.redirectUrl = ( - config.redirectUrl || 'https://copay.io/in/app#!/confirmed' - ); }; /** @@ -139,78 +125,6 @@ }).end(); }; - /** - * Helper that sends a verification email. - * - * @param {string} email - the user's email - * @param {string} secret - the verification secret - */ - emailPlugin.sendVerificationEmail = function(email, secret, callback) { - var confirmUrl = emailPlugin.makeConfirmUrl(email, secret); - - logger.debug('ConfirmUrl:',confirmUrl); - - async.series([ - - function(callback) { - emailPlugin.makeEmailBody({ - email: email, - confirm_url: confirmUrl - }, callback); - }, - function(callback) { - emailPlugin.makeEmailHTMLBody({ - email: email, - confirm_url: confirmUrl, - title: 'Your wallet backup needs confirmation' - }, callback); - } - ], function(err, results) { - var emailBody = results[0]; - var emailBodyHTML = results[1]; - var mailOptions = { - from: 'copay@copay.io', - to: email, - subject: '[Copay] Your wallet backup needs confirmation', - text: emailBody, - html: emailBodyHTML - }; - - // send mail with defined transport object - emailPlugin.email.sendMail(mailOptions, function(err, info) { - if (err) { - logger.error('An error occurred when trying to send email to ' + email, err); - return callback(err); - } - logger.info('Message sent: ', info ? info : ''); - return callback(err, info); - }); - }); - }; - - emailPlugin.makeConfirmUrl = function(email, secret) { - return emailPlugin.confirmUrl + ( - '?email=' + encodeURIComponent(email) + '&verification_code=' + secret - ); - }; - - /** - * Returns a function that reads an underscore template and uses the `opts` param - * to build an email body - */ - var applyTemplate = function(templateFilename) { - return function(opts, callback) { - fs.readFile(__dirname + '/emailTemplates/' + emailPlugin[templateFilename], - function(err, template) { - return callback(err, _.template(template, opts)); - } - ); - }; - }; - - emailPlugin.makeEmailBody = applyTemplate('textTemplate'); - emailPlugin.makeEmailHTMLBody = applyTemplate('htmlTemplate'); - /** * @param {string} email * @param {Function(err, boolean)} callback @@ -349,22 +263,6 @@ }); }; - emailPlugin.createVerificationSecretAndSendEmail = function(email, callback) { - emailPlugin.createVerificationSecret(email, function(err, secret) { - if (err || !secret) { - logger.error('error saving verification secret', email, secret, err); - return callback(emailPlugin.errors.INTERNAL_ERROR); - } - emailPlugin.sendVerificationEmail(email, secret, function (err, res) { - if (err) { - logger.error('error sending verification email', email, secret, err); - return callback(emailPlugin.errors.ERROR_SENDING_EMAIL); - } - return callback(); - }); - }); - }; - /** * Creates and stores a verification secret in the database. * @@ -442,132 +340,6 @@ }); }; - /** - * Store a record in the database. The underlying database is merely a levelup instance (a key - * value store) that uses the email concatenated with the secret as a key to store the record. - * The request is expected to contain the parameters: - * * email - * * secret - * * record - * - * @param {Express.Request} request - * @param {Express.Response} response - */ - emailPlugin.save = function(request, response) { - - var queryData = ''; - var credentials = emailPlugin.getCredentialsFromRequest(request); - if (credentials.code) { - return emailPlugin.returnError(credentials, response); - } - var email = credentials.email; - var passphrase = credentials.passphrase; - - request.on('data', function(data) { - queryData += data; - if (queryData.length > POST_LIMIT) { - queryData = ''; - response.writeHead(413, { - 'Content-Type': 'text/plain' - }); - response.end(); - request.connection.destroy(); - } - }).on('end', function() { - var params = querystring.parse(queryData); - var key = params.key; - var record = params.record; - if (!email || !passphrase || !record || !key) { - return emailPlugin.returnError(emailPlugin.errors.MISSING_PARAMETER, response); - } - - emailPlugin.processPost(request, response, email, key, passphrase, record); - }); - }; - - emailPlugin.processPost = function(request, response, email, key, passphrase, record) { - var isNewProfile = false; - var isConfirmed = true; - var errorCreating = false; - - async.series([ - /** - * Try to fetch this user's email. If it exists, check the secret is the same. - */ - function(callback) { - emailPlugin.exists(email, function(err, exists) { - if (err) return callback(err); - - if (exists) { - emailPlugin.checkPassphrase(email, passphrase, function(err, match) { - if (err) return callback(err); - if (!match) return callback(emailPlugin.errors.EMAIL_TAKEN); - return callback(); - }); - } else { - isNewProfile = true; - emailPlugin.savePassphrase(email, passphrase, function(err) { - return callback(err); - }); - } - }); - }, - function(callback) { - emailPlugin.isConfirmed(email, function(err, inIsConfirmed) { - if (err) return callback(err); - isConfirmed = inIsConfirmed; - return callback(); - }); - }, - function(callback) { - emailPlugin.checkSizeQuota(email, record.length, isConfirmed, function(err) { - return callback(err); - }); - }, - function(callback) { - emailPlugin.checkAndUpdateItemQuota(email, key, isConfirmed, function(err) { - return callback(err); - }); - }, - /** - * Save the encrypted private key in the storage. - */ - function(callback) { - emailPlugin.saveEncryptedData(email, key, record, function(err) { - return callback(err); - }); - }, - /** - * Create and store the verification secret. If successful, send a verification email. - */ - function(callback) { - if (!isNewProfile || isConfirmed) return callback(); - - emailPlugin.createVerificationSecretAndSendEmail(email, function(err) { - if (err) { - errorCreating = true; - } - return callback(err); - }); - }, - ], - function(err) { - if (err) { - if (isNewProfile && !isConfirmed && errorCreating) { - emailPlugin.deleteWholeProfile(email, function() { - return emailPlugin.returnError(err, response); - }); - } - - emailPlugin.returnError(err, response); - } else { - response.json({ - success: true - }).end(); - } - }); - }; - emailPlugin.getCredentialsFromRequest = function(request) { var auth = request.header('authorization'); if (!auth) { @@ -729,7 +501,7 @@ }); }; - emailPlugin._parseSecret = function (value) { + emailPlugin._parseSecret = function(value) { var obj = null; try { obj = JSON.parse(value); @@ -755,16 +527,10 @@ var secret = emailPlugin._parseSecret(value); - emailPlugin.sendVerificationEmail(email, secret, function (err) { - if (err) { - logger.error('error resending verification email', email, secret, err); - return emailPlugin.returnError(emailPlugin.errors.ERROR_SENDING_EMAIL, response); - } - return response.json({ - success: true - }).end(); + return response.json({ + success: true + }).end(); - }); }); }); }; @@ -902,32 +668,6 @@ }); }; - emailPlugin.oldSave = function(request, response) { - var queryData = ''; - - request.on('data', function(data) { - queryData += data; - if (queryData.length > UNCONFIRMED_PER_ITEM_QUOTA) { - queryData = ''; - response.writeHead(413, { - 'Content-Type': 'text/plain' - }).end(); - request.connection.destroy(); - } - }).on('end', function() { - var params = querystring.parse(queryData); - var email = params.email; - var passphrase = params.secret; - var key = params.key; - var record = params.record; - if (!email || !passphrase || !record || !key) { - return emailPlugin.returnError(emailPlugin.errors.MISSING_PARAMETER, response); - } - - emailPlugin.processPost(request, response, email, key, passphrase, record); - }); - }; - module.exports = emailPlugin; })(); diff --git a/plugins/ratelimiter.js b/plugins/ratelimiter.js index 6c3b2b728..163365273 100644 --- a/plugins/ratelimiter.js +++ b/plugins/ratelimiter.js @@ -2,15 +2,15 @@ var logger = require('../lib/logger').logger; var preconditions = require('preconditions').singleton(); var limiter = require('connect-ratelimit'); -var ONE_HOUR = 60 * 60 * 1000; +var THREE_HOURS = 3* 60 * 60 * 1000; module.exports.init = function(app, config) { preconditions.checkArgument(app); logger.info('Using ratelimiter plugin'); config = config || {}; - config.whitelistRPH = config.whitelistRPH || 500000; - config.normalRPH = config.normalRPH || 10000; + config.whitelistRPH = config.whitelistRPH || 3*60*60*10; + config.normalRPH = config.normalRPH || 3*60*60; config.blacklistRPH = config.blacklistRPH || 0; app.use(limiter({ @@ -20,15 +20,15 @@ module.exports.init = function(app, config) { categories: { whitelist: { totalRequests: config.whitelistRPH, - every: ONE_HOUR + every: THREE_HOURS }, blacklist: { totalRequests: config.blacklistRPH, - every: ONE_HOUR + every: THREE_HOURS }, normal: { totalRequests: config.normalRPH, - every: ONE_HOUR + every: THREE_HOURS } } })); From bea9ec07271da5384d960a3df4dd34a6f2f9cff2 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Thu, 13 Aug 2015 11:04:32 -0300 Subject: [PATCH 2/2] rm old code --- config/routes.js | 1 - plugins/emailstore.js | 387 +----------------------------------------- 2 files changed, 1 insertion(+), 387 deletions(-) diff --git a/config/routes.js b/config/routes.js index fbb896cc8..0b774469f 100644 --- a/config/routes.js +++ b/config/routes.js @@ -65,7 +65,6 @@ module.exports = function(app) { if (config.enableEmailstore) { var emailPlugin = require('../plugins/emailstore'); app.get(apiPrefix + '/email/retrieve', emailPlugin.retrieve); - app.get(apiPrefix + '/email/retrieve/:email', emailPlugin.oldRetrieve); } // Currency rates plugin diff --git a/plugins/emailstore.js b/plugins/emailstore.js index 10481c41e..05c7f8865 100644 --- a/plugins/emailstore.js +++ b/plugins/emailstore.js @@ -125,21 +125,6 @@ }).end(); }; - /** - * @param {string} email - * @param {Function(err, boolean)} callback - */ - emailPlugin.exists = function(email, callback) { - emailPlugin.db.get(emailToPassphrase(email), function(err, value) { - if (err && err.notFound) { - return callback(null, false); - } else if (err) { - return callback(emailPlugin.errors.INTERNAL_ERROR); - } - return callback(null, true); - }); - }; - /** * @param {string} email * @param {string} passphrase @@ -175,78 +160,6 @@ }; - /** - * checkSizeQuota - * - * @param email - * @param size - * @param isConfirmed - * @param callback - */ - emailPlugin.checkSizeQuota = function(email, size, isConfirmed, callback) { - var err; - - if (size > (isConfirmed ? CONFIRMED_PER_ITEM_QUOTA : UNCONFIRMED_PER_ITEM_QUOTA)) - err = emailPlugin.errors.OVER_QUOTA; - - logger.info('Storage size:', size); - return callback(err); - }; - - - emailPlugin.checkAndUpdateItemCounter = function(email, isConfirmed, isAdd, callback) { - // this is a new item... Check User's Items quota. - emailPlugin.db.get(countKey(email), function(err, counter) { - if (err && !err.notFound) { - return callback(emailPlugin.errors.INTERNAL_ERROR); - } - counter = (parseInt(counter) || 0) - - if (isAdd) { - counter++; - logger.info('User counter quota:', counter); - if (counter > (isConfirmed ? CONFIRMED_ITEMS_LIMIT : UNCONFIRMED_ITEMS_LIMIT)) { - return callback(emailPlugin.errors.OVER_QUOTA); - } - } else { - if (counter > 0) counter--; - } - - - emailPlugin.db.put(countKey(email), counter, function(err) { - if (err) { - logger.error('error saving counter'); - return callback(emailPlugin.errors.INTERNAL_ERROR); - } - return callback(); - }); - }); - }; - - - /** - * @param {string} email - * @param {string} key - * @param {Function(err)} callback - */ - emailPlugin.checkAndUpdateItemQuota = function(email, key, isConfirmed, callback) { - - emailPlugin.db.get(valueKey(email, key), function(err) { - - //existing item? - if (!err) - return callback(); - - if (err.notFound) { - //new item - return emailPlugin.checkAndUpdateItemCounter(email, isConfirmed, 1, callback); - } else { - return callback(emailPlugin.errors.INTERNAL_ERROR); - } - }); - }; - - /** * @param {string} email * @param {string} key @@ -263,33 +176,6 @@ }); }; - /** - * Creates and stores a verification secret in the database. - * - * @param {string} email - the user's email - * @param {Function} callback - will be called with params (err, secret) - */ - emailPlugin.createVerificationSecret = function(email, callback) { - emailPlugin.db.get(pendingKey(email), function(err, value) { - if (err && err.notFound) { - var secret = emailPlugin.crypto.randomBytes(16).toString('hex'); - var value = { - secret: secret, - created: moment().unix(), - }; - emailPlugin.db.put(pendingKey(email), JSON.stringify(value), function(err) { - if (err) { - logger.error('error saving pending data:', email, value); - return callback(emailPlugin.errors.INTERNAL_ERROR); - } - return callback(null, secret); - }); - } else { - return callback(err); - } - }); - }; - /** * @param {string} email * @param {Function(err)} callback @@ -306,40 +192,6 @@ }); }; - emailPlugin.deleteByEmailAndKey = function deleteByEmailAndKey(email, key, callback) { - emailPlugin.db.del(valueKey(email, key), function(error) { - if (error) { - logger.error(error); - if (error.notFound) { - return callback(emailPlugin.errors.NOT_FOUND); - } - return callback(emailPlugin.errors.INTERNAL_ERROR); - } - return emailPlugin.checkAndUpdateItemCounter(email, null, null, callback); - }); - }; - - emailPlugin.deleteWholeProfile = function deleteWholeProfile(email, callback) { - async.parallel([ - - function(cb) { - emailPlugin.db.del(emailToPassphrase(email), cb); - }, - function(cb) { - emailPlugin.db.del(pendingKey(email), cb); - }, - function(cb) { - emailPlugin.db.del(validatedKey(email), cb); - } - ], function(err) { - if (err && !err.notFound) { - logger.error(err); - return callback(emailPlugin.errors.INTERNAL_ERROR); - } - return callback(); - }); - }; - emailPlugin.getCredentialsFromRequest = function(request) { var auth = request.header('authorization'); if (!auth) { @@ -376,27 +228,6 @@ }; - /** - * addValidationAndQuotaHeader - * - * @param response - * @param email - * @param {Function(err, boolean)} callback - */ - emailPlugin.addValidationAndQuotaHeader = function(response, email, callback) { - emailPlugin.isConfirmed(email, function(err, isConfirmed) { - if (err) return callback(err); - - if (!isConfirmed) { - response.set('X-Email-Needs-Validation', 'true'); - } - - response.set('X-Quota-Per-Item', isConfirmed ? CONFIRMED_PER_ITEM_QUOTA : UNCONFIRMED_PER_ITEM_QUOTA); - response.set('X-Quota-Items-Limit', isConfirmed ? CONFIRMED_ITEMS_LIMIT : UNCONFIRMED_ITEMS_LIMIT); - return callback(); - }); - }; - emailPlugin.authorizeRequest = function(request, withKey, callback) { var credentialsResult = emailPlugin.getCredentialsFromRequest(request); if (_.contains(emailPlugin.errors, credentialsResult)) { @@ -447,227 +278,11 @@ if (err) return emailPlugin.returnError(err, response); - - emailPlugin.addValidationAndQuotaHeader(response, email, function(err) { - if (err) - return emailPlugin.returnError(err, response); - - response.send(value).end(); - }); + response.send(value).end(); }); }); }; - /** - * Remove a record from the database - */ - emailPlugin.erase = function(request, response) { - emailPlugin.authorizeRequestWithKey(request, function(err, email, key) { - if (err) { - return emailPlugin.returnError(err, response); - } - emailPlugin.deleteByEmailAndKey(email, key, function(err, value) { - if (err) { - return emailPlugin.returnError(err, response); - } else { - return response.json({ - success: true - }).end(); - }; - }); - }); - }; - - /** - * Remove a whole profile from the database - * - * @TODO: This looks very similar to the method above - */ - emailPlugin.eraseProfile = function(request, response) { - emailPlugin.authorizeRequestWithoutKey(request, function(err, email) { - if (err) { - return emailPlugin.returnError(err, response); - } - - emailPlugin.deleteWholeProfile(email, function(err, value) { - if (err) { - return emailPlugin.returnError(err, response); - } else { - return response.json({ - success: true - }).end(); - }; - }); - }); - }; - - emailPlugin._parseSecret = function(value) { - var obj = null; - try { - obj = JSON.parse(value); - } catch (e) {} - - if (obj && _.isObject(obj)) { - return obj.secret; - } - - return value; - }; - - emailPlugin.resendEmail = function(request, response) { - emailPlugin.authorizeRequestWithoutKey(request, function(err, email) { - if (err) { - return emailPlugin.returnError(err, response); - } - emailPlugin.db.get(pendingKey(email), function(err, value) { - if (err) { - logger.error('error retrieving secret for email', email, err); - return emailPlugin.returnError(err, response); - } - - var secret = emailPlugin._parseSecret(value); - - return response.json({ - success: true - }).end(); - - }); - }); - }; - - /** - * Marks an email as validated - * - * The two expected params are: - * * email - * * verification_code - * - * @param {Express.Request} request - * @param {Express.Response} response - */ - emailPlugin.validate = function(request, response) { - var email = request.param('email'); - var secret = request.param('verification_code'); - if (!email || !secret) { - return emailPlugin.returnError(emailPlugin.errors.MISSING_PARAMETER, response); - } - - emailPlugin.db.get(pendingKey(email), function(err, value) { - if (err) { - if (err.notFound) { - return emailPlugin.returnError(emailPlugin.errors.NOT_FOUND, response); - } - return emailPlugin.returnError({ - code: 500, - message: err - }, response); - } - - value = emailPlugin._parseSecret(value); - - if (value !== secret) { - return emailPlugin.returnError(emailPlugin.errors.INVALID_CODE, response); - } - - emailPlugin.db.put(validatedKey(email), true, function(err, value) { - if (err) { - return emailPlugin.returnError({ - code: 500, - message: err - }, response); - } else { - emailPlugin.db.del(pendingKey(email), function(err, value) { - if (err) { - return emailPlugin.returnError({ - code: 500, - message: err - }, response); - } else { - response.redirect(emailPlugin.redirectUrl); - } - }); - } - }); - }); - }; - - /** - * Changes an user's passphrase - * - * @param {Express.Request} request - * @param {Express.Response} response - */ - emailPlugin.changePassphrase = function(request, response) { - - emailPlugin.authorizeRequestWithoutKey(request, function(err, email) { - - if (err) { - return emailPlugin.returnError(err, response); - } - - var queryData = ''; - request.on('data', function(data) { - queryData += data; - if (queryData.length > POST_LIMIT) { - queryData = ''; - response.writeHead(413, { - 'Content-Type': 'text/plain' - }).end(); - request.connection.destroy(); - } - }).on('end', function() { - var params = querystring.parse(queryData); - var newPassphrase = params.newPassphrase; - if (!newPassphrase) { - return emailPlugin.returnError(emailPlugin.errors.INVALID_REQUEST, response); - } - emailPlugin.savePassphrase(email, newPassphrase, function(error) { - if (error) { - return emailPlugin.returnError(error, response); - } - return response.json({ - success: true - }).end(); - }); - }); - }); - }; - - - // - // Backwards compatibility - // - - emailPlugin.oldRetrieveDataByEmailAndPassphrase = function(email, key, passphrase, callback) { - emailPlugin.checkPassphrase(email, passphrase, function(err, matches) { - if (err) { - return callback(err); - } - if (matches) { - return emailPlugin.retrieveByEmailAndKey(email, key, callback); - } else { - return callback(emailPlugin.errors.INVALID_CODE); - } - }); - }; - - - emailPlugin.oldRetrieve = function(request, response) { - var email = request.param('email'); - var key = request.param('key'); - var secret = request.param('secret'); - if (!secret) { - return emailPlugin.returnError(emailPlugin.errors.MISSING_PARAMETER, response); - } - - emailPlugin.oldRetrieveDataByEmailAndPassphrase(email, key, secret, function(err, value) { - if (err) { - return emailPlugin.returnError(err, response); - } - response.send(value).end(); - }); - }; - module.exports = emailPlugin; })();