[IMPROVE] Add proxy for data export (#20998)

Co-authored-by: Diego Sampaio <chinello@gmail.com>
pull/21586/head
Daniel Martinez 4 years ago committed by GitHub
parent f05b07c720
commit 4b3d58fd36
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 54
      app/user-data-download/server/DataExport.js
  2. 4
      app/user-data-download/server/cronProcessDownloads.js
  3. 34
      app/user-data-download/server/exportDownload.js
  4. 1
      app/user-data-download/server/index.js
  5. 3
      packages/rocketchat-i18n/i18n/en.i18n.json
  6. 3
      packages/rocketchat-i18n/i18n/pt-BR.i18n.json
  7. 31
      private/errors/error_template.html
  8. 4
      server/lib/channelExport.ts
  9. 3
      server/methods/requestDataDownload.js

@ -0,0 +1,54 @@
import { Cookies } from 'meteor/ostrio:cookies';
import Users from '../../models/server/models/Users';
import { FileUpload } from '../../file-upload/server';
import { getURL } from '../../utils/lib/getURL';
const cookie = new Cookies();
const userDataStore = FileUpload.getStore('UserDataFiles');
export const DataExport = {
handlers: {},
getPath(path = '') {
return `/data-export/${ path }`;
},
requestCanAccessFiles({ headers = {}, query = {} }, userId) {
let { rc_uid, rc_token } = query;
if (!rc_uid && headers.cookie) {
rc_uid = cookie.get('rc_uid', headers.cookie);
rc_token = cookie.get('rc_token', headers.cookie);
}
const options = { fields: { _id: 1 } };
if (rc_uid && rc_token && rc_uid === userId) {
return !!Users.findOneByIdAndLoginToken(rc_uid, rc_token, options);
}
if (headers['x-user-id'] && headers['x-auth-token'] && headers['x-user-id'] === userId) {
return !!Users.findOneByIdAndLoginToken(headers['x-user-id'], headers['x-auth-token'], options);
}
return false;
},
get(file, req, res, next) {
if (userDataStore && userDataStore.get) {
return userDataStore.get(file, req, res, next);
}
res.writeHead(404);
res.end();
},
getErrorPage(errorType, errorDescription) {
let errorHtml = Assets.getText('errors/error_template.html');
errorHtml = errorHtml.replace('$ERROR_TYPE$', errorType);
errorHtml = errorHtml.replace('$ERROR_DESCRIPTION$', errorDescription);
errorHtml = errorHtml.replace('$SERVER_URL$', getURL('/', { full: true, cdn: false }));
return errorHtml;
},
};

@ -11,9 +11,11 @@ import moment from 'moment';
import { settings } from '../../settings/server';
import { Subscriptions, Rooms, Users, Uploads, Messages, UserDataFiles, ExportOperations, Avatars } from '../../models/server';
import { FileUpload } from '../../file-upload/server';
import { DataExport } from './DataExport';
import * as Mailer from '../../mailer';
import { readSecondaryPreferred } from '../../../server/database/readSecondaryPreferred';
import { joinPath } from '../../../server/lib/fileUtils';
import { getURL } from '../../utils/lib/getURL';
const fsStat = util.promisify(fs.stat);
const fsOpen = util.promisify(fs.open);
@ -569,7 +571,7 @@ async function processDataDownloads() {
}
const subject = TAPi18n.__('UserDataDownload_EmailSubject');
const body = TAPi18n.__('UserDataDownload_EmailBody', { download_link: file.url });
const body = TAPi18n.__('UserDataDownload_EmailBody', { download_link: getURL(DataExport.getPath(file._id), { cdn: false, full: true }) });
sendEmail(operation.userData, subject, body);
}

@ -0,0 +1,34 @@
import { WebApp } from 'meteor/webapp';
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
import { UserDataFiles } from '../../models';
import { DataExport } from './DataExport';
import { settings } from '../../settings/server';
WebApp.connectHandlers.use(DataExport.getPath(), function(req, res, next) {
const match = /^\/([^\/]+)/.exec(req.url);
if (!settings.get('UserData_EnableDownload')) {
res.writeHead(403);
res.setHeader('Content-Type', 'text/html; charset=UTF-8');
return res.end(DataExport.getErrorPage(TAPi18n.__('Feature_Disabled'), TAPi18n.__('UserDataDownload_FeatureDisabled')));
}
if (match && match[1]) {
const file = UserDataFiles.findOneById(match[1]);
if (file) {
if (!DataExport.requestCanAccessFiles(req, file.userId)) {
res.setHeader('Content-Type', 'text/html; charset=UTF-8');
res.writeHead(403);
return res.end(DataExport.getErrorPage(TAPi18n.__('403'), TAPi18n.__('UserDataDownload_LoginNeeded')));
}
res.setHeader('Content-Security-Policy', 'default-src \'none\'');
res.setHeader('Cache-Control', 'max-age=31536000');
return DataExport.get(file, req, res, next);
}
}
res.writeHead(404);
res.end();
});

@ -1,2 +1,3 @@
import './startup/settings';
import './cronProcessDownloads';
import './exportDownload';

@ -1760,6 +1760,7 @@
"Feature_Depends_on_Livechat_Visitor_navigation_as_a_message_to_be_enabled": "This feature depends on \"Send Visitor Navigation History as a Message\" to be enabled.",
"Features": "Features",
"Features_Enabled": "Features Enabled",
"Feature_Disabled": "Feature Disabled",
"Federation_Dashboard": "Federation Dashboard",
"FEDERATION_Discovery_Method": "Discovery Method",
"FEDERATION_Discovery_Method_Description": "You can use the hub or a SRV and a TXT entry on your DNS records.",
@ -4198,6 +4199,8 @@
"UserDataDownload_CompletedRequestExistedWithLink_Text": "Your data file was already generated. Click <a href=\"__download_link__\" target=\"_blank\">here</a> to download it.",
"UserDataDownload_EmailBody": "Your data file is now ready to download. Click <a href=\"__download_link__\">here</a> to download it.",
"UserDataDownload_EmailSubject": "Your Data File is Ready to Download",
"UserDataDownload_FeatureDisabled": "Sorry, user data exports are not enabled on this server!",
"UserDataDownload_LoginNeeded": "You need to log into your Rocket.Chat account to download this data export. Click the link below to do that, then try again.",
"UserDataDownload_Requested": "Download File Requested",
"UserDataDownload_Requested_Text": "Your data file will be generated. A link to download it will be sent to your email address when ready. There are <strong>__pending_operations__</strong> queued operations to run before yours.",
"UserDataDownload_RequestExisted_Text": "Your data file is already being generated. A link to download it will be sent to your email address when ready. There are <strong>__pending_operations__</strong> queued operations to run before yours.",

@ -1500,6 +1500,7 @@
"Favorites": "Favoritos",
"Feature_Depends_on_Livechat_Visitor_navigation_as_a_message_to_be_enabled": "Esse recurso depende de \"Enviar histórico de navegação do visitante como uma mensagem\" para ser ativado.",
"Features_Enabled": "Funcionalidades habilitadas",
"Feature_Disabled": "Funcionalidade desabilitada",
"FEDERATION_Discovery_Method": "Método de Descoberta",
"FEDERATION_Discovery_Method_Description": "Você pode usar o hub ou uma entrada SRV e TXT em seus registros DNS.",
"FEDERATION_Domain": "Domínio",
@ -3448,6 +3449,8 @@
"UserDataDownload_CompletedRequestExisted_Text": "Seu arquivo de dados já foi gerado. Verifique sua conta de e-mail para o link de download.",
"UserDataDownload_EmailBody": "Seu arquivo de dados está pronto para download. Clique em <a href=\"__download_link__\">aqui</a>para fazer o download.",
"UserDataDownload_EmailSubject": "Seu arquivo de dados está pronto para download",
"UserDataDownload_FeatureDisabled": "Desculpe, a exportação de arquivos de dados está desativada neste servidor!",
"UserDataDownload_LoginNeeded": "Você precisa fazer login na sua conta do Rocket.Chat para baixar esse arquivo de dados. Faça isso através do link abaixo e tente novamente.",
"UserDataDownload_Requested": "Download do arquivo solicitado",
"UserDataDownload_Requested_Text": "Seu arquivo de dados será gerado. Um link para baixá-lo será enviado para o seu endereço de e-mail quando estiver pronto. Há <strong>__pending_operations__</strong> operações na fila antes da sua.",
"UserDataDownload_RequestExisted_Text": "Seu arquivo de dados já está sendo gerado. Um link para baixá-lo será enviado para o seu endereço de e-mail quando estiver pronto. Há <strong>__pending_operations__</strong> operações na fila antes da sua.",

@ -0,0 +1,31 @@
<html>
<style type="text/css">
body {
margin: 40px auto;
max-width: 650px;
line-height: 1.4;
font-size: 18px;
color: #444;
padding: 0 10px;
font-family: sans-serif;
}
h1, h2, h3 {
line-height: 1.2
}
div {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
</style>
<body>
<div>
<h1>$ERROR_TYPE$</h1>
<p>$ERROR_DESCRIPTION$</p>
<p><a href='$SERVER_URL$'>Go home</a></p>
</div>
</body>
</html>

@ -19,6 +19,8 @@ import {
} from '../../app/user-data-download/server/cronProcessDownloads';
import { IUser } from '../../definition/IUser';
import { getMomentLocale } from './getMomentLocale';
import { getURL } from '../../app/utils/lib/getURL';
import { DataExport } from '../../app/user-data-download/server/DataExport';
type ExportEmail = {
rid: string;
@ -166,7 +168,7 @@ export const sendFile = async (data: ExportFile, user: IUser): Promise<void> =>
const subject = TAPi18n.__('Channel_Export');
// eslint-disable-next-line @typescript-eslint/camelcase
const body = TAPi18n.__('UserDataDownload_EmailBody', { download_link: file.url });
const body = TAPi18n.__('UserDataDownload_EmailBody', { download_link: getURL(DataExport.getPath(file._id), { cdn: false, full: true }) });
sendEmail(user, subject, body);
};

@ -6,6 +6,7 @@ import { Meteor } from 'meteor/meteor';
import { ExportOperations, UserDataFiles } from '../../app/models';
import { settings } from '../../app/settings';
import { DataExport } from '../../app/user-data-download/server/DataExport';
let tempFolder = '/tmp/userData';
if (settings.get('UserData_FileSystemPath') != null) {
@ -34,7 +35,7 @@ Meteor.methods({
return {
requested: false,
exportOperation: lastOperation,
url: file.url,
url: DataExport.getPath(file._id),
pendingOperationsBeforeMyRequest: pendingOperationsBeforeMyRequestCount,
};
}

Loading…
Cancel
Save