protect file uploads

pull/2435/head
Diego Sampaio 10 years ago
parent c589027a96
commit b64e3fefa3
  1. 2
      packages/rocketchat-file-upload/package.js
  2. 8
      packages/rocketchat-file-upload/server/configS3.js
  3. 15
      packages/rocketchat-file-upload/server/methods/sendFileMessage.js
  4. 135
      packages/rocketchat-file-upload/server/requests.js
  5. 3
      packages/rocketchat-lib/server/models/Uploads.coffee

@ -13,6 +13,7 @@ Package.onUse(function(api) {
api.use('rocketchat:lib');
api.use('random');
api.use('underscore');
api.use('webapp');
api.addFiles('globalFileRestrictions.js');
@ -24,6 +25,7 @@ Package.onUse(function(api) {
api.addFiles('client/lib/fileUploadHandler.js', 'client');
api.addFiles('server/configS3.js', 'server');
api.addFiles('server/requests.js', 'server');
api.addFiles('server/settings.js', 'server');
api.addFiles('server/methods/sendFileMessage.js', 'server');

@ -17,14 +17,8 @@ createS3Directive = _.debounce(() => {
AWSSecretAccessKey: secretKey,
key: function (file, metaContext) {
var serverIdentifier = '';
// @TODO what should we do when a file already exists with the given name?
var path = serverIdentifier + this.userId + '-' + metaContext.rid + "/";
if (file.name) {
return path + file.name;
} else {
return path + Random.id();
}
return path + Random.id();
}
};

@ -18,23 +18,26 @@ Meteor.methods({
fileId = file._id;
}
var fileUrl = '/file-upload/' + fileId + '/' + file.name;
var attachment = {
title: `File Uploaded: ${file.name}`,
title_link: file.url
title_link: fileUrl
};
if (/^image\/.+/.test(file.type)) {
attachment.image_url = file.url;
attachment.image_url = fileUrl;
attachment.image_type = file.type;
attachment.image_size = file.size;
// @TODO
// attachment.image_dimensions = file.identify?.size;
if (file.identify && file.identify.size) {
attachment.image_dimensions = file.identify.size;
}
} else if (/^audio\/.+/.test(file.type)) {
attachment.audio_url = file.url;
attachment.audio_url = fileUrl;
attachment.audio_type = file.type;
attachment.audio_size = file.size;
} else if (/^video\/.+/.test(file.type)) {
attachment.video_url = file.url;
attachment.video_url = fileUrl;
attachment.video_type = file.type;
attachment.video_size = file.size;
}1

@ -0,0 +1,135 @@
var crypto = Npm.require("crypto");
var stream = Npm.require('stream');
var zlib = Npm.require('zlib');
var S3bucket, S3accessKey, S3secretKey, protectedFiles;
RocketChat.settings.get('FileUpload_S3_Bucket', function(key, value) {
S3bucket = value;
});
RocketChat.settings.get('FileUpload_S3_AWSAccessKeyId', function(key, value) {
S3accessKey = value;
});
RocketChat.settings.get('FileUpload_S3_AWSSecretAccessKey', function(key, value) {
S3secretKey = value;
});
RocketChat.settings.get('FileUpload_ProtectFiles', function(key, value) {
protectedFiles = value;
});
var generateURL = function(fullUrl) {
var resourceURL = '/' + fullUrl.substr(fullUrl.indexOf(S3bucket));
var expires = parseInt(new Date().getTime() / 1000) + 60;
var StringToSign = 'GET\n\n\n' + expires +'\n'+resourceURL;
var signature = crypto.createHmac('sha1', S3secretKey).update(new Buffer(StringToSign, "utf-8")).digest('base64');
return fullUrl + '?AWSAccessKeyId='+encodeURIComponent(S3accessKey)+'&Expires='+expires+'&Signature='+encodeURIComponent(signature);
};
// code from: https://github.com/jalik/jalik-ufs/blob/master/ufs-server.js#L91
var readFromGridFS = function(storeName, fileId, file, req, res) {
var store = UploadFS.getStore(storeName);
var rs = store.getReadStream(fileId, file);
var ws = new stream.PassThrough();
rs.on('error', function (err) {
store.onReadError.call(store, err, fileId, file);
res.end();
});
ws.on('error', function (err) {
store.onReadError.call(store, err, fileId, file);
res.end();
});
ws.on('close', function () {
// Close output stream at the end
ws.emit('end');
});
var accept = req.headers['accept-encoding'] || '';
var headers = {
'Content-Type': file.type,
'Content-Length': file.size
};
// Transform stream
store.transformRead(rs, ws, fileId, file, req, headers);
// Compress data using gzip
if (accept.match(/\bgzip\b/)) {
headers['Content-Encoding'] = 'gzip';
delete headers['Content-Length'];
res.writeHead(200, headers);
ws.pipe(zlib.createGzip()).pipe(res);
}
// Compress data using deflate
else if (accept.match(/\bdeflate\b/)) {
headers['Content-Encoding'] = 'deflate';
delete headers['Content-Length'];
res.writeHead(200, headers);
ws.pipe(zlib.createDeflate()).pipe(res);
}
// Send raw data
else {
res.writeHead(200, headers);
ws.pipe(res);
}
};
WebApp.connectHandlers.use('/file-upload/', function(req, res, next) {
var file;
var match = /^\/([^\/]+)\/(.*)/.exec(req.url);
if (match[1]) {
file = RocketChat.models.Uploads.findOneById(match[1]);
if (file) {
if (protectedFiles) {
var cookie, rawCookies, ref, token, uid;
cookie = new Cookies();
if ((typeof req !== "undefined" && req !== null ? (ref = req.headers) != null ? ref.cookie : void 0 : void 0) != null) {
rawCookies = req.headers.cookie;
}
if (rawCookies != null) {
uid = cookie.get('rc_uid', rawCookies);
}
if (rawCookies != null) {
token = cookie.get('rc_token', rawCookies);
}
if (uid == null) {
uid = req.query.rc_uid;
token = req.query.rc_token;
}
if (!(uid && token && RocketChat.models.Users.findOneByIdAndLoginToken(uid, token))) {
res.writeHead(403);
res.end();
return false;
}
}
res.setHeader('Content-Disposition', "attachment; filename=\"" + encodeURIComponent(file.name) + "\"");
res.setHeader('Last-Modified', file.uploadedAt.toUTCString());
res.setHeader('Content-Type', file.type);
res.setHeader('Content-Length', file.size);
if (file.store === 's3') {
var newUrl = generateURL(file.url);
res.setHeader('Location', newUrl);
res.writeHead(302);
res.end();
return;
} else {
return readFromGridFS(file.store, file._id, file, req, res);
}
}
}
res.writeHead(404);
res.end();
return;
});

@ -5,6 +5,9 @@ RocketChat.models.Uploads = new class extends RocketChat.models._Base
@tryEnsureIndex { 'rid': 1 }
@tryEnsureIndex { 'uploadedAt': 1 }
findOneById: (fileId) ->
@findOne { _id: fileId }
findNotHiddenFilesOfRoom: (roomId, limit) ->
fileQuery =
rid: roomId

Loading…
Cancel
Save