From a12ff8d0d20435a9b74c6e3ad4f9c69d865984a5 Mon Sep 17 00:00:00 2001 From: Pranav Kant Date: Wed, 26 Oct 2016 18:59:27 +0530 Subject: [PATCH 1/2] security: Support WOPI's PostMessageOrigin Adds a new property PostMessageOrigin to WOPI's CheckFileInfo. The inner frame then only sends message to target with origin mentioned in this property. Also implement editor initialization WOPI specs. Inner frame sends a App_LoadingStatus message to us when ready, and we send Host_PostmessageReady when we are ready. --- appinfo/database.xml | 7 ++++++ appinfo/info.xml | 2 +- controller/documentcontroller.php | 7 +++--- js/documents.js | 38 +++++++++++++++++++++++++++---- lib/db/wopi.php | 10 ++++---- 5 files changed, 52 insertions(+), 12 deletions(-) diff --git a/appinfo/database.xml b/appinfo/database.xml index fe6c0760..b6c272b4 100644 --- a/appinfo/database.xml +++ b/appinfo/database.xml @@ -327,6 +327,13 @@ true Can make changes to this file + + server_host + text + localhost + true + Host from which token generation request originated + token text diff --git a/appinfo/info.xml b/appinfo/info.xml index bc532d10..8006b460 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -5,7 +5,7 @@ Collabora Online allows you to to work with all kinds of office documents directly in your browser. This application requires Collabora Cloudsuite to be installed on one of your servers, please read the documentation to learn more about that. Edit office documents directly in your browser. AGPL - 1.1.10 + 1.1.11 Collabora Productivity based on work of Frank Karlitschek, Victor Dubiniuk https://github.com/owncloud/richdocuments/issues https://github.com/owncloud/richdocuments.git diff --git a/controller/documentcontroller.php b/controller/documentcontroller.php index 23cd3b8f..ad85f728 100644 --- a/controller/documentcontroller.php +++ b/controller/documentcontroller.php @@ -503,7 +503,8 @@ class DocumentController extends Controller { \OC::$server->getLogger()->debug('File with {fileid} has updatable set to {updatable}', [ 'app' => $this->appName, 'fileid' => $fileId, 'updatable' => $updatable ]); $row = new Db\Wopi(); - $token = $row->generateFileToken($fileId, $version, $updatable); + $serverHost = $this->request->getServerProtocol() . '://' . $this->request->getServerHost(); + $token = $row->generateFileToken($fileId, $version, $updatable, $serverHost); // Return the token. return array( @@ -543,7 +544,6 @@ class DocumentController extends Controller { $this->loginUser($res['owner']); $view = new \OC\Files\View('/' . $res['owner'] . '/files'); $info = $view->getFileInfo($res['path']); - $this->logoutUser(); if (!$info) { @@ -558,7 +558,8 @@ class DocumentController extends Controller { 'Version' => $version, 'UserId' => $res['editor'], 'UserFriendlyName' => $editorName, - 'UserCanWrite' => $res['canwrite'] ? 'true' : 'false' + 'UserCanWrite' => $res['canwrite'] ? 'true' : 'false', + 'PostMessageOrigin' => $res['server_host'] ); } diff --git a/js/documents.js b/js/documents.js index 9806d00e..85feaeba 100644 --- a/js/documents.js +++ b/js/documents.js @@ -473,23 +473,41 @@ var documentsMain = { $('#mainContainer').append(form); $('#mainContainer').append(frame); - // handler for the 'Close' button - we have enabled it via closebutton=1 + // Listen for App_LoadingStatus as soon as possible + $('#loleafletframe').ready(function() { + var editorInitListener = function(e) { + var msg = JSON.parse(e.data); + if (msg.MessageId === 'App_LoadingStatus') { + // LOOL Iframe is ready, turn off our overlay + documentsMain.overlay.documentOverlay('hide'); + window.removeEventListener('message', editorInitListener, false); + } + }; + window.addEventListener('message', editorInitListener, false); + }); + $('#loleafletframe').load(function(){ - documentsMain.overlay.documentOverlay('hide'); + // And start listening to incoming post messages window.addEventListener('message', function(e){ if (documentsMain.isViewerMode) { return; } - if (e.data === 'close') { + + var msg = JSON.parse(e.data); + if (msg.MessageId === 'UI_Close') { documentsMain.onClose(); - } else if (e.data === 'rev-history') { + } else if (msg.MessageId === 'rev-history') { documentsMain.UI.showRevHistory($('li[data-id=' + documentsMain.fileId + ']>a').attr('original-title')); } }); + + // Tell the LOOL iframe that we are ready now + documentsMain.WOPIPostMessage($('#loleafletframe')[0], 'Host_PostmessageReady', {}); }); // submit that $('#loleafletform').submit(); + }); }, @@ -586,6 +604,18 @@ var documentsMain = { documentsMain.ready = true; }, + WOPIPostMessage: function(iframe, msgId, values) { + if (iframe) { + var msg = { + 'MessageId': msgId, + 'SendTime': Date.now(), + 'Values': values + }; + + iframe.contentWindow.postMessage(JSON.stringify(msg), '*'); + } + }, + prepareSession : function(){ documentsMain.isEditorMode = true; documentsMain.overlay.documentOverlay('show'); diff --git a/lib/db/wopi.php b/lib/db/wopi.php index c4ea3830..36cda195 100644 --- a/lib/db/wopi.php +++ b/lib/db/wopi.php @@ -29,8 +29,8 @@ class Wopi extends \OCA\Richdocuments\Db{ protected $tableName = '`*PREFIX*richdocuments_wopi`'; - protected $insertStatement = 'INSERT INTO `*PREFIX*richdocuments_wopi` (`owner_uid`, `editor_uid`, `fileid`, `version`, `path`, `canwrite`, `token`, `expiry`) - VALUES (?, ?, ?, ?, ?, ?, ?, ?)'; + protected $insertStatement = 'INSERT INTO `*PREFIX*richdocuments_wopi` (`owner_uid`, `editor_uid`, `fileid`, `version`, `path`, `canwrite`, `server_host`, `token`, `expiry`) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)'; protected $loadStatement = 'SELECT * FROM `*PREFIX*richdocuments_wopi` WHERE `token`= ?'; @@ -41,7 +41,7 @@ class Wopi extends \OCA\Richdocuments\Db{ * its the version number as stored by files_version app * Returns the token. */ - public function generateFileToken($fileId, $version, $updatable){ + public function generateFileToken($fileId, $version, $updatable, $serverHost){ // Get the FS view of the current user. $view = \OC\Files\Filesystem::getView(); @@ -80,6 +80,7 @@ class Wopi extends \OCA\Richdocuments\Db{ $version, $path, $updatable, + $serverHost, $token, time() + self::TOKEN_LIFETIME_SECONDS ]); @@ -125,7 +126,8 @@ class Wopi extends \OCA\Richdocuments\Db{ 'owner' => $row['owner_uid'], 'editor' => $row['editor_uid'], 'path' => $row['path'], - 'canwrite' => $row['canwrite'] + 'canwrite' => $row['canwrite'], + 'server_host' => $row['server_host'] ); } } From 309d5535ff8a9fc047baea1d33fd8571de51d711 Mon Sep 17 00:00:00 2001 From: Pranav Kant Date: Fri, 28 Oct 2016 17:29:05 +0530 Subject: [PATCH 2/2] Be backward compatible with older LOOLs --- js/documents.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/js/documents.js b/js/documents.js index 85feaeba..995b18e0 100644 --- a/js/documents.js +++ b/js/documents.js @@ -478,8 +478,6 @@ var documentsMain = { var editorInitListener = function(e) { var msg = JSON.parse(e.data); if (msg.MessageId === 'App_LoadingStatus') { - // LOOL Iframe is ready, turn off our overlay - documentsMain.overlay.documentOverlay('hide'); window.removeEventListener('message', editorInitListener, false); } }; @@ -493,16 +491,26 @@ var documentsMain = { return; } - var msg = JSON.parse(e.data); - if (msg.MessageId === 'UI_Close') { + try { + var msg = JSON.parse(e.data).MessageId; + } catch(exc) { + msg = e.data; + } + if (msg === 'UI_Close' || msg === 'close') { documentsMain.onClose(); - } else if (msg.MessageId === 'rev-history') { + } else if (msg === 'rev-history') { documentsMain.UI.showRevHistory($('li[data-id=' + documentsMain.fileId + ']>a').attr('original-title')); } }); // Tell the LOOL iframe that we are ready now documentsMain.WOPIPostMessage($('#loleafletframe')[0], 'Host_PostmessageReady', {}); + + // LOOL Iframe is ready, turn off our overlay + // This should ideally be taken off when we receive App_LoadingStatus, but + // for backward compatibility with older lool, lets keep it here till we decide + // to break older lools + documentsMain.overlay.documentOverlay('hide'); }); // submit that