[wopi] Fix downloadfile handler for wopi; fix bug 66612

# Conflicts:
#	DocService/sources/canvasservice.js
#	DocService/sources/wopiClient.js
This commit is contained in:
Sergey Konovalov
2024-02-26 14:09:00 +03:00
parent 69ff2f3e92
commit fb7b1c4257
4 changed files with 33 additions and 20 deletions

View File

@ -2242,7 +2242,7 @@ exports.install = function(server, callbackFunction) {
let queryParams = decoded.queryParams; let queryParams = decoded.queryParams;
data.lang = queryParams.lang || queryParams.ui || "en-US"; data.lang = queryParams.lang || queryParams.ui || "en-US";
} }
if (decoded.fileInfo) { if (wopiClient.isWopiJwtToken(decoded)) {
let fileInfo = decoded.fileInfo; let fileInfo = decoded.fileInfo;
if (openCmd) { if (openCmd) {
openCmd.format = wopiClient.getFileTypeByInfo(fileInfo); openCmd.format = wopiClient.getFileTypeByInfo(fileInfo);
@ -2397,7 +2397,7 @@ exports.install = function(server, callbackFunction) {
} }
//todo make required fields //todo make required fields
if (decoded.url || decoded.payload|| (decoded.key && !decoded.fileInfo)) { if (decoded.url || decoded.payload|| (decoded.key && !wopiClient.isWopiJwtToken(decoded))) {
ctx.logger.warn('fillDataFromJwt token has invalid format'); ctx.logger.warn('fillDataFromJwt token has invalid format');
res = false; res = false;
} }
@ -2438,7 +2438,7 @@ exports.install = function(server, callbackFunction) {
isDecoded = true; isDecoded = true;
let decoded = checkJwtRes.decoded; let decoded = checkJwtRes.decoded;
let fillDataFromJwtRes = false; let fillDataFromJwtRes = false;
if (decoded.fileInfo) { if (wopiClient.isWopiJwtToken(decoded)) {
//wopi //wopi
fillDataFromJwtRes = fillDataFromWopiJwt(decoded, data); fillDataFromJwtRes = fillDataFromWopiJwt(decoded, data);
} else if (decoded.editorConfig && undefined !== decoded.editorConfig.ds_view) { } else if (decoded.editorConfig && undefined !== decoded.editorConfig.ds_view) {

View File

@ -1598,6 +1598,7 @@ exports.downloadFile = function(req, res) {
let authorization; let authorization;
let isInJwtToken = false; let isInJwtToken = false;
let errorDescription; let errorDescription;
let headers;
let authRes = yield docsCoServer.getRequestParams(ctx, req); let authRes = yield docsCoServer.getRequestParams(ctx, req);
if (authRes.code === constants.NO_ERROR) { if (authRes.code === constants.NO_ERROR) {
let decoded = authRes.params; let decoded = authRes.params;
@ -1610,6 +1611,8 @@ exports.downloadFile = function(req, res) {
} else if (decoded.url && -1 !== tenDownloadFileAllowExt.indexOf(decoded.fileType)) { } else if (decoded.url && -1 !== tenDownloadFileAllowExt.indexOf(decoded.fileType)) {
url = decoded.url; url = decoded.url;
isInJwtToken = true; isInJwtToken = true;
} else if (wopiClient.isWopiJwtToken(decoded)) {
({url, headers} = wopiClient.getWopiFileUrl(ctx, decoded.fileInfo, decoded.userAuth));
} else if (!tenTokenEnableBrowser) { } else if (!tenTokenEnableBrowser) {
//todo token required //todo token required
if (decoded.url) { if (decoded.url) {
@ -1638,11 +1641,12 @@ exports.downloadFile = function(req, res) {
res.sendStatus(filterStatus); res.sendStatus(filterStatus);
return; return;
} }
let headers;
if (req.get('Range')) { if (req.get('Range')) {
headers = { if (!headers) {
'Range': req.get('Range') headers = {};
} }
headers['Range'] = req.get('Range');
} }
yield utils.downloadUrlPromise(ctx, url, tenDownloadTimeout, tenDownloadMaxBytes, authorization, isInJwtToken, headers, res); yield utils.downloadUrlPromise(ctx, url, tenDownloadTimeout, tenDownloadMaxBytes, authorization, isInJwtToken, headers, res);

View File

@ -63,6 +63,7 @@ const cfgTokenEnableBrowser = config.get('services.CoAuthoring.token.enable.brow
const cfgCallbackRequestTimeout = config.get('services.CoAuthoring.server.callbackRequestTimeout'); const cfgCallbackRequestTimeout = config.get('services.CoAuthoring.server.callbackRequestTimeout');
const cfgNewFileTemplate = config.get('services.CoAuthoring.server.newFileTemplate'); const cfgNewFileTemplate = config.get('services.CoAuthoring.server.newFileTemplate');
const cfgDownloadTimeout = config.get('FileConverter.converter.downloadTimeout'); const cfgDownloadTimeout = config.get('FileConverter.converter.downloadTimeout');
const cfgMaxDownloadBytes = config.get('FileConverter.converter.maxDownloadBytes');
const cfgWopiFileInfoBlockList = config.get('wopi.fileInfoBlockList'); const cfgWopiFileInfoBlockList = config.get('wopi.fileInfoBlockList');
const cfgWopiWopiZone = config.get('wopi.wopiZone'); const cfgWopiWopiZone = config.get('wopi.wopiZone');
const cfgWopiPdfView = config.get('wopi.pdfView'); const cfgWopiPdfView = config.get('wopi.pdfView');
@ -289,6 +290,25 @@ function getFileTypeByInfo(fileInfo) {
fileType = fileInfo.FileExtension ? fileInfo.FileExtension.substr(1) : fileType; fileType = fileInfo.FileExtension ? fileInfo.FileExtension.substr(1) : fileType;
return fileType.toLowerCase(); return fileType.toLowerCase();
} }
function getWopiFileUrl(ctx, fileInfo, userAuth) {
const tenMaxDownloadBytes = ctx.getCfg('FileConverter.converter.maxDownloadBytes', cfgMaxDownloadBytes);
let url;
let headers = {'X-WOPI-MaxExpectedSize': tenMaxDownloadBytes};
if (fileInfo?.FileUrl) {
//Requests to the FileUrl can not be signed using proof keys. The FileUrl is used exactly as provided by the host, so it does not necessarily include the access token, which is required to construct the expected proof.
url = fileInfo.FileUrl;
} else if (fileInfo?.TemplateSource) {
url = fileInfo.TemplateSource;
} else if (userAuth) {
url = `${userAuth.wopiSrc}/contents?access_token=${userAuth.access_token}`;
fillStandardHeaders(ctx, headers, url, userAuth.access_token);
}
ctx.logger.debug('getWopiFileUrl url=%s; headers=%j', url, headers);
return {url, headers};
}
function isWopiJwtToken(decoded) {
return !!decoded.fileInfo;
}
function getLastModifiedTimeFromCallbacks(callbacks) { function getLastModifiedTimeFromCallbacks(callbacks) {
for (let i = callbacks.length; i >= 0; --i) { for (let i = callbacks.length; i >= 0; --i) {
let callback = callbacks[i]; let callback = callbacks[i];
@ -952,6 +972,8 @@ exports.fillStandardHeaders = fillStandardHeaders;
exports.getWopiUnlockMarker = getWopiUnlockMarker; exports.getWopiUnlockMarker = getWopiUnlockMarker;
exports.getWopiModifiedMarker = getWopiModifiedMarker; exports.getWopiModifiedMarker = getWopiModifiedMarker;
exports.getFileTypeByInfo = getFileTypeByInfo; exports.getFileTypeByInfo = getFileTypeByInfo;
exports.getWopiFileUrl = getWopiFileUrl;
exports.isWopiJwtToken = isWopiJwtToken;
exports.dummyCheckFileInfo = dummyCheckFileInfo; exports.dummyCheckFileInfo = dummyCheckFileInfo;
exports.dummyGetFile = dummyGetFile; exports.dummyGetFile = dummyGetFile;
exports.dummyOk = dummyOk; exports.dummyOk = dummyOk;

View File

@ -429,7 +429,6 @@ function* downloadFile(ctx, uri, fileFrom, withAuthorization, isInJwtToken, opt_
return res; return res;
} }
function* downloadFileFromStorage(ctx, strPath, dir, opt_specialDir) { function* downloadFileFromStorage(ctx, strPath, dir, opt_specialDir) {
const tenMaxDownloadBytes = ctx.getCfg('FileConverter.converter.maxDownloadBytes', cfgMaxDownloadBytes);
var list = yield storage.listObjects(ctx, strPath, opt_specialDir); var list = yield storage.listObjects(ctx, strPath, opt_specialDir);
ctx.logger.debug('downloadFileFromStorage list %s', list.toString()); ctx.logger.debug('downloadFileFromStorage list %s', list.toString());
//create dirs //create dirs
@ -1002,7 +1001,6 @@ function* spawnProcess(ctx, builderParams, tempDirs, dataConvert, authorProps, g
} }
function* ExecuteTask(ctx, task) { function* ExecuteTask(ctx, task) {
const tenMaxDownloadBytes = ctx.getCfg('FileConverter.converter.maxDownloadBytes', cfgMaxDownloadBytes);
const tenForgottenFiles = ctx.getCfg('services.CoAuthoring.server.forgottenfiles', cfgForgottenFiles); const tenForgottenFiles = ctx.getCfg('services.CoAuthoring.server.forgottenfiles', cfgForgottenFiles);
const tenForgottenFilesName = ctx.getCfg('services.CoAuthoring.server.forgottenfilesname', cfgForgottenFilesName); const tenForgottenFilesName = ctx.getCfg('services.CoAuthoring.server.forgottenfilesname', cfgForgottenFilesName);
var startDate = null; var startDate = null;
@ -1039,19 +1037,8 @@ function* ExecuteTask(ctx, task) {
withAuthorization = false; withAuthorization = false;
isInJwtToken = true; isInJwtToken = true;
let fileInfo = wopiParams.commonInfo?.fileInfo; let fileInfo = wopiParams.commonInfo?.fileInfo;
let userAuth = wopiParams.userAuth;
fileSize = fileInfo?.Size; fileSize = fileInfo?.Size;
if (fileInfo?.FileUrl) { ({url, headers} = wopiClient.getWopiFileUrl(ctx, fileInfo, wopiParams.userAuth));
//Requests to the FileUrl can not be signed using proof keys. The FileUrl is used exactly as provided by the host, so it does not necessarily include the access token, which is required to construct the expected proof.
url = fileInfo.FileUrl;
} else if (fileInfo?.TemplateSource) {
url = fileInfo.TemplateSource;
} else if (userAuth) {
url = `${userAuth.wopiSrc}/contents?access_token=${userAuth.access_token}`;
headers = {'X-WOPI-MaxExpectedSize': tenMaxDownloadBytes};
wopiClient.fillStandardHeaders(ctx, headers, url, userAuth.access_token);
}
ctx.logger.debug('wopi url=%s; headers=%j', url, headers);
} }
if (undefined === fileSize || fileSize > 0) { if (undefined === fileSize || fileSize > 0) {
error = yield* downloadFile(ctx, url, dataConvert.fileFrom, withAuthorization, isInJwtToken, headers); error = yield* downloadFile(ctx, url, dataConvert.fileFrom, withAuthorization, isInJwtToken, headers);