From cd80c2638c49c12c7a1a7145066f0c2f99928156 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 16 Jul 2019 14:17:10 -0400 Subject: [PATCH] feat: #7743 user/password user/picture --- src/password.js | 3 + src/user/password.js | 61 +++++------- src/user/picture.js | 232 +++++++++++++++++++------------------------ test/user.js | 6 +- 4 files changed, 132 insertions(+), 170 deletions(-) diff --git a/src/password.js b/src/password.js index 1d26e16e33..c2e7e88bfb 100644 --- a/src/password.js +++ b/src/password.js @@ -69,3 +69,6 @@ function done(err, result) { process.send(err ? { err: err.message } : { result: result }); process.disconnect(); } + + +require('./promisify')(exports); diff --git a/src/user/password.js b/src/user/password.js index bfb65f9cb3..a57cb0eec9 100644 --- a/src/user/password.js +++ b/src/user/password.js @@ -1,52 +1,39 @@ 'use strict'; -var async = require('async'); -var nconf = require('nconf'); -var db = require('../database'); -var Password = require('../password'); +const nconf = require('nconf'); + +const db = require('../database'); +const Password = require('../password'); module.exports = function (User) { - User.hashPassword = function (password, callback) { + User.hashPassword = async function (password) { if (!password) { - return callback(null, password); + return password; } - Password.hash(nconf.get('bcrypt_rounds') || 12, password, callback); + return await Password.hash(nconf.get('bcrypt_rounds') || 12, password); }; - User.isPasswordCorrect = function (uid, password, ip, callback) { + User.isPasswordCorrect = async function (uid, password, ip) { password = password || ''; - var hashedPassword; - async.waterfall([ - function (next) { - db.getObjectField('user:' + uid, 'password', next); - }, - function (_hashedPassword, next) { - hashedPassword = _hashedPassword; - if (!hashedPassword) { - // Non-existant user, submit fake hash for comparison - hashedPassword = ''; - } - - User.isPasswordValid(password, 0, next); - }, - async.apply(User.auth.logAttempt, uid, ip), - function (next) { - Password.compare(password, hashedPassword, next); - }, - function (ok, next) { - if (ok) { - User.auth.clearLoginAttempts(uid); - } - next(null, ok); - }, - ], callback); + var hashedPassword = await db.getObjectField('user:' + uid, 'password'); + if (!hashedPassword) { + // Non-existant user, submit fake hash for comparison + hashedPassword = ''; + } + + await User.isPasswordValid(password, 0); + await User.auth.logAttempt(uid, ip); + const ok = await Password.compare(password, hashedPassword); + if (ok) { + User.auth.clearLoginAttempts(uid); + } + return ok; }; - User.hasPassword = function (uid, callback) { - db.getObjectField('user:' + uid, 'password', function (err, hashedPassword) { - callback(err, !!hashedPassword); - }); + User.hasPassword = async function (uid) { + const hashedPassword = await db.getObjectField('user:' + uid, 'password'); + return !!hashedPassword; }; }; diff --git a/src/user/picture.js b/src/user/picture.js index a7740ed28e..3d7ac755b1 100644 --- a/src/user/picture.js +++ b/src/user/picture.js @@ -1,6 +1,5 @@ 'use strict'; -var async = require('async'); var winston = require('winston'); var file = require('../file'); @@ -9,156 +8,129 @@ var meta = require('../meta'); var db = require('../database'); module.exports = function (User) { - User.updateCoverPosition = function (uid, position, callback) { + User.updateCoverPosition = async function (uid, position) { // Reject anything that isn't two percentages if (!/^[\d.]+%\s[\d.]+%$/.test(position)) { winston.warn('[user/updateCoverPosition] Invalid position received: ' + position); - return callback(new Error('[[error:invalid-data]]')); + throw new Error('[[error:invalid-data]]'); } - User.setUserField(uid, 'cover:position', position, callback); + await User.setUserField(uid, 'cover:position', position); }; - User.updateCoverPicture = function (data, callback) { - var url; - var picture = { + User.updateCoverPicture = async function (data) { + const picture = { name: 'profileCover', uid: data.uid, }; - if (!data.imageData && data.position) { - return User.updateCoverPosition(data.uid, data.position, callback); + try { + if (!data.imageData && data.position) { + return await User.updateCoverPosition(data.uid, data.position); + } + + if (!data.imageData && !data.file) { + throw new Error('[[error:invalid-data]]'); + } + const size = data.file ? data.file.size : image.sizeFromBase64(data.imageData); + if (size > meta.config.maximumCoverImageSize * 1024) { + throw new Error('[[error:file-too-big, ' + meta.config.maximumCoverImageSize + ']]'); + } + + if (data.file) { + picture.path = data.file.path; + } else { + picture.path = await image.writeImageDataToTempFile(data.imageData); + } + + const type = data.file ? data.file.type : image.mimeFromBase64(data.imageData); + if (!type || !type.match(/^image./)) { + throw new Error('[[error:invalid-image]]'); + } + + const extension = file.typeToExtension(type); + const filename = generateProfileImageFilename(data.uid, 'profilecover', extension); + const uploadData = await image.uploadImage(filename, 'profile', picture); + + await User.setUserField(data.uid, 'cover:url', uploadData.url); + + if (data.position) { + await User.updateCoverPosition(data.uid, data.position); + } + + return { + url: uploadData.url, + }; + } finally { + file.delete(picture.path || (data.file && data.file.path)); } - - if (!data.imageData && !data.file) { - return callback(new Error('[[error:invalid-data]]')); - } - - async.waterfall([ - function (next) { - var size = data.file ? data.file.size : image.sizeFromBase64(data.imageData); - if (size > meta.config.maximumCoverImageSize * 1024) { - return next(new Error('[[error:file-too-big, ' + meta.config.maximumCoverImageSize + ']]')); - } - - if (data.file) { - return setImmediate(next, null, data.file.path); - } - - image.writeImageDataToTempFile(data.imageData, next); - }, - function (path, next) { - picture.path = path; - - var type = data.file ? data.file.type : image.mimeFromBase64(data.imageData); - if (!type || !type.match(/^image./)) { - return next(new Error('[[error:invalid-image]]')); - } - - var extension = file.typeToExtension(type); - var filename = generateProfileImageFilename(data.uid, 'profilecover', extension); - image.uploadImage(filename, 'profile', picture, next); - }, - function (uploadData, next) { - url = uploadData.url; - User.setUserField(data.uid, 'cover:url', uploadData.url, next); - }, - function (next) { - if (data.position) { - User.updateCoverPosition(data.uid, data.position, next); - } else { - setImmediate(next); - } - }, - ], function (err) { - file.delete(picture.path); - callback(err, { - url: url, - }); - }); }; - User.uploadCroppedPicture = function (data, callback) { - if (!meta.config.allowProfileImageUploads) { - return callback(new Error('[[error:profile-image-uploads-disabled]]')); - } - - if (!data.imageData && !data.file) { - return callback(new Error('[[error:invalid-data]]')); - } - - var size = data.file ? data.file.size : image.sizeFromBase64(data.imageData); - var uploadSize = meta.config.maximumProfileImageSize; - if (size > uploadSize * 1024) { - return callback(new Error('[[error:file-too-big, ' + uploadSize + ']]')); - } - - var type = data.file ? data.file.type : image.mimeFromBase64(data.imageData); - if (!type || !type.match(/^image./)) { - return callback(new Error('[[error:invalid-image]]')); - } - var extension = file.typeToExtension(type); - if (!extension) { - return callback(new Error('[[error:invalid-image-extension]]')); - } - - var uploadedImage; - - var picture = { + User.uploadCroppedPicture = async function (data) { + const picture = { name: 'profileAvatar', uid: data.uid, }; - async.waterfall([ - function (next) { - if (data.file) { - return setImmediate(next, null, data.file.path); - } - image.writeImageDataToTempFile(data.imageData, next); - }, - function (path, next) { - convertToPNG(path, extension, next); - }, - function (path, next) { - picture.path = path; - image.resizeImage({ - path: picture.path, - width: meta.config.profileImageDimension, - height: meta.config.profileImageDimension, - }, next); - }, - function (next) { - var filename = generateProfileImageFilename(data.uid, 'profileavatar', extension); - image.uploadImage(filename, 'profile', picture, next); - }, - function (_uploadedImage, next) { - uploadedImage = _uploadedImage; - - User.setUserFields(data.uid, { - uploadedpicture: uploadedImage.url, - picture: uploadedImage.url, - }, next); - }, - ], function (err) { - file.delete(picture.path); - callback(err, uploadedImage); - }); + try { + if (!meta.config.allowProfileImageUploads) { + throw new Error('[[error:profile-image-uploads-disabled]]'); + } + + if (!data.imageData && !data.file) { + throw new Error('[[error:invalid-data]]'); + } + + const size = data.file ? data.file.size : image.sizeFromBase64(data.imageData); + const uploadSize = meta.config.maximumProfileImageSize; + if (size > uploadSize * 1024) { + throw new Error('[[error:file-too-big, ' + uploadSize + ']]'); + } + + const type = data.file ? data.file.type : image.mimeFromBase64(data.imageData); + if (!type || !type.match(/^image./)) { + throw new Error('[[error:invalid-image]]'); + } + const extension = file.typeToExtension(type); + if (!extension) { + throw new Error('[[error:invalid-image-extension]]'); + } + + if (data.file) { + picture.path = data.file.path; + } else { + picture.path = await image.writeImageDataToTempFile(data.imageData); + } + + picture.path = await convertToPNG(picture.path, extension); + + await image.resizeImage({ + path: picture.path, + width: meta.config.profileImageDimension, + height: meta.config.profileImageDimension, + }); + + const filename = generateProfileImageFilename(data.uid, 'profileavatar', extension); + const uploadedImage = await image.uploadImage(filename, 'profile', picture); + + await User.setUserFields(data.uid, { + uploadedpicture: uploadedImage.url, + picture: uploadedImage.url, + }); + return uploadedImage; + } finally { + file.delete(picture.path || (data.file && data.file.path)); + } }; - function convertToPNG(path, extension, callback) { + async function convertToPNG(path, extension) { var convertToPNG = meta.config['profile:convertProfileImageToPNG'] === 1; if (!convertToPNG) { - return setImmediate(callback, null, path); + return path; } - async.waterfall([ - function (next) { - image.normalise(path, extension, next); - }, - function (newPath, next) { - file.delete(path); - next(null, newPath); - }, - ], callback); + const newPath = await image.normalise(path, extension); + file.delete(path); + return newPath; } function generateProfileImageFilename(uid, type, extension) { @@ -167,7 +139,7 @@ module.exports = function (User) { return uid + '-' + type + (keepAllVersions ? '-' + Date.now() : '') + (convertToPNG ? '.png' : extension); } - User.removeCoverPicture = function (data, callback) { - db.deleteObjectFields('user:' + data.uid, ['cover:url', 'cover:position'], callback); + User.removeCoverPicture = async function (data) { + await db.deleteObjectFields('user:' + data.uid, ['cover:url', 'cover:position']); }; }; diff --git a/test/user.js b/test/user.js index be2af0a2d7..fb5cfd6d9e 100644 --- a/test/user.js +++ b/test/user.js @@ -899,7 +899,7 @@ describe('User', function () { it('should return error if profile image uploads disabled', function (done) { meta.config.allowProfileImageUploads = 0; var picture = { - path: path.join(nconf.get('base_dir'), 'test/files/test.png'), + path: path.join(nconf.get('base_dir'), 'test/files/test_copy.png'), size: 7189, name: 'test.png', type: 'image/png', @@ -916,7 +916,7 @@ describe('User', function () { it('should return error if profile image is too big', function (done) { meta.config.allowProfileImageUploads = 1; var picture = { - path: path.join(nconf.get('base_dir'), 'test/files/test.png'), + path: path.join(nconf.get('base_dir'), 'test/files/test_copy.png'), size: 265000, name: 'test.png', type: 'image/png', @@ -933,7 +933,7 @@ describe('User', function () { it('should return error if profile image has no mime type', function (done) { var picture = { - path: path.join(nconf.get('base_dir'), 'test/files/test.png'), + path: path.join(nconf.get('base_dir'), 'test/files/test_copy.png'), size: 7189, name: 'test', };