[Libreoffice-commits] online.git: wsd/ClientSession.cpp wsd/DocumentBroker.cpp wsd/DocumentBroker.hpp wsd/Storage.cpp wsd/Storage.hpp

Ashod Nakashian ashod.nakashian at collabora.co.uk
Wed May 17 02:18:50 UTC 2017


 wsd/ClientSession.cpp  |    1 
 wsd/DocumentBroker.cpp |   19 +++++++++--------
 wsd/DocumentBroker.hpp |    1 
 wsd/Storage.cpp        |   46 ++++++++++++++++++++++++++++++++++++-------
 wsd/Storage.hpp        |   52 +++++++++++++++++++++++++++++++++++++++++++++----
 5 files changed, 98 insertions(+), 21 deletions(-)

New commits:
commit ec2fd0844f997f9a7347229e282daeb8dc4471bc
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Tue May 16 16:38:44 2017 -0400

    wsd: use WOPI host instance ID to support hostname aliases
    
    The docKey creation moved to Storage where we first
    invoke WOPI (if/when it's a WOPI-hosted doc and WOPI enabled)
    and see if the user has access to the document at all.
    If they do, we expect the server to give us a
    unique ID to use for identifying the host regardless
    of hostname aliases.
    
    If a unique ID is not returned (i.e. empty or missing)
    we use the hostname and port in its place as fallback.
    This will break hostname aliases, but it will still work.
    
    Change-Id: I407b0087395f9df6ad9cc6e037570487999be4a4
    Reviewed-on: https://gerrit.libreoffice.org/37697
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>

diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 5a0af025..8ce4c327 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -736,7 +736,6 @@ bool ClientSession::forwardToClient(const std::shared_ptr<Message>& payload)
 
 std::string ClientSession::getAccessToken() const
 {
-    std::string accessToken;
     Poco::URI::QueryParameters queryParams = _uriPublic.getQueryParameters();
     for (auto& param: queryParams)
     {
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index e5924f87..66a5de80 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -102,15 +102,7 @@ Poco::URI DocumentBroker::sanitizeURI(const std::string& uri)
 
 std::string DocumentBroker::getDocKey(const Poco::URI& uri)
 {
-    // If multiple host-names are used to access us, then
-    // we force same document (when opened from
-    // alias hosts) to load as separate documents and sharing doesn't
-    // work. Worse, saving overwrites one another.
-    // But we also do not want different WOPI hosts using the same path
-    // for some file getting shared across WOPI hosts
-    std::string docKey;
-    Poco::URI::encode(uri.getHost() + ":" + std::to_string(uri.getPort()) + uri.getPath(), "", docKey);
-    return docKey;
+    return StorageBase::getUniqueDocId(uri);
 }
 
 /// The Document Broker Poll - one of these in a thread per document
@@ -409,6 +401,7 @@ bool DocumentBroker::load(const std::shared_ptr<ClientSession>& session, const s
             LOG_ERR("Failed to create Storage instance for [" << _docKey << "] in " << jailPath.toString());
             return false;
         }
+
         firstInstance = true;
     }
 
@@ -423,6 +416,14 @@ bool DocumentBroker::load(const std::shared_ptr<ClientSession>& session, const s
         std::unique_ptr<WopiStorage::WOPIFileInfo> wopifileinfo = wopiStorage->getWOPIFileInfo(session->getAccessToken());
         userid = wopifileinfo->_userid;
         username = wopifileinfo->_username;
+        if (firstInstance)
+        {
+            _hostInstanceId = wopifileinfo->_hostInstanceId;
+        }
+        else if (!_hostInstanceId.empty() && _hostInstanceId != wopifileinfo->_hostInstanceId)
+        {
+            throw UnauthorizedRequestException("Unauthorized WOPI host.");
+        }
 
         if (!wopifileinfo->_userCanWrite)
         {
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index 46481cba..8ac67ee2 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -369,6 +369,7 @@ private:
     Poco::URI _uriJailed;
     std::string _jailId;
     std::string _filename;
+    std::string _hostInstanceId;
 
     /// The last time we tried saving, regardless of whether the
     /// document was modified and saved or not.
diff --git a/wsd/Storage.cpp b/wsd/Storage.cpp
index e07ffd55..bdaeab56 100644
--- a/wsd/Storage.cpp
+++ b/wsd/Storage.cpp
@@ -124,7 +124,7 @@ void StorageBase::initialize()
 #endif
 }
 
-bool isLocalhost(const std::string& targetHost)
+bool StorageBase::isLocalhost(const std::string& targetHost)
 {
     std::string targetAddress;
     try
@@ -201,7 +201,7 @@ std::unique_ptr<StorageBase> StorageBase::create(const Poco::URI& uri, const std
     {
         LOG_INF("Public URI [" << uri.toString() << "] considered WOPI.");
         const auto& targetHost = uri.getHost();
-        if (WopiHosts.match(targetHost) || isLocalhost(targetHost))
+        if (isWopiHostAuthorized(targetHost))
         {
             return std::unique_ptr<StorageBase>(new WopiStorage(uri, jailRoot, jailPath));
         }
@@ -212,6 +212,39 @@ std::unique_ptr<StorageBase> StorageBase::create(const Poco::URI& uri, const std
     throw BadRequestException("No Storage configured or invalid URI.");
 }
 
+std::string StorageBase::getUniqueDocId(const Poco::URI& uri)
+{
+    std::string docId;
+    if (uri.isRelative() || uri.getScheme() == "file")
+    {
+        Poco::URI::encode(uri.getPath(), "", docId);
+    }
+    else if (WopiEnabled)
+    {
+        const auto& targetHost = uri.getHost();
+        if (isWopiHostAuthorized(targetHost))
+        {
+            std::string accessToken;
+            Poco::URI::QueryParameters queryParams = uri.getQueryParameters();
+            for (auto& param: queryParams)
+            {
+                if (param.first == "access_token")
+                    accessToken = param.second;
+            }
+
+            const std::unique_ptr<WopiStorage::WOPIFileInfo> info = WopiStorage::getWOPIFileInfo(uri, accessToken);
+            const std::string prefix = !info->_hostInstanceId.empty()
+                                     ? info->_hostInstanceId
+                                     : (uri.getHost() + ':' + std::to_string(uri.getPort()));
+            Poco::URI::encode(prefix + uri.getPath(), "", docId);
+        }
+        else
+            throw UnauthorizedRequestException("No acceptable WOPI hosts found matching the target host [" + targetHost + "] in config.");
+    }
+
+    return docId;
+}
+
 std::atomic<unsigned> LocalStorage::LastLocalStorageId;
 
 std::unique_ptr<LocalStorage::LocalFileInfo> LocalStorage::getLocalFileInfo()
@@ -406,10 +439,9 @@ void setQueryParameter(Poco::URI& uriObject, const std::string& key, const std::
 
 } // anonymous namespace
 
-std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const std::string& accessToken)
+std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(Poco::URI uriObject, const std::string& accessToken)
 {
     // update the access_token to the one matching to the session
-    Poco::URI uriObject(_uri);
     setQueryParameter(uriObject, "access_token", accessToken);
 
     LOG_DBG("Getting info for wopi uri [" << uriObject.toString() << "].");
@@ -456,6 +488,7 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const st
     std::string ownerId;
     std::string userId;
     std::string userName;
+    std::string hostInstanceId;
     bool canWrite = false;
     bool enableOwnerTermination = false;
     std::string postMessageOrigin;
@@ -490,6 +523,7 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const st
         getWOPIValue(object, "DisableExport", disableExport);
         getWOPIValue(object, "DisableCopy", disableCopy);
         getWOPIValue(object, "LastModifiedTime", lastModifiedTime);
+        getWOPIValue(object, "HostInstanceId", hostInstanceId);
     }
     else
     {
@@ -519,9 +553,7 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const st
         }
     }
 
-    _fileInfo = FileInfo({filename, ownerId, modifiedTime, size});
-
-    return std::unique_ptr<WopiStorage::WOPIFileInfo>(new WOPIFileInfo({userId, userName, canWrite, postMessageOrigin, hidePrintOption, hideSaveOption, hideExportOption, enableOwnerTermination, disablePrint, disableExport, disableCopy, callDuration}));
+    return std::unique_ptr<WopiStorage::WOPIFileInfo>(new WOPIFileInfo(filename, ownerId, modifiedTime, size, userId, userName, hostInstanceId, canWrite, postMessageOrigin, hidePrintOption, hideSaveOption, hideExportOption, enableOwnerTermination, disablePrint, disableExport, disableCopy, callDuration));
 }
 
 /// uri format: http://server/<...>/wopi*/files/<id>/content
diff --git a/wsd/Storage.hpp b/wsd/Storage.hpp
index 6613fbbc..7cb77432 100644
--- a/wsd/Storage.hpp
+++ b/wsd/Storage.hpp
@@ -100,11 +100,29 @@ public:
     static std::unique_ptr<StorageBase> create(const Poco::URI& uri,
                                                const std::string& jailRoot,
                                                const std::string& jailPath);
+
+    /// Given the URI of a doc, return a unique doc ID.
+    /// Wopi host aliases are resolved to unique host ID.
+    static std::string getUniqueDocId(const Poco::URI& uri);
+
 protected:
 
     /// Returns the root path of the jail directory of docs.
     std::string getLocalRootPath() const;
 
+    /// Returns true iff WOPI is enabled, and the host is whitelisted (or local).
+    static bool isWopiHostAuthorized(const std::string& host)
+    {
+        if (WopiEnabled)
+        {
+            return (WopiHosts.match(host) || isLocalhost(host));
+        }
+
+        return false;
+    }
+
+    static bool isLocalhost(const std::string& host);
+
 protected:
     const Poco::URI _uri;
     std::string _localStorePath;
@@ -176,11 +194,16 @@ public:
                 "], jailPath: [" << jailPath << "], uri: [" << uri.toString() << "].");
     }
 
-    class WOPIFileInfo
+    class WOPIFileInfo : public FileInfo
     {
     public:
-        WOPIFileInfo(const std::string& userid,
+        WOPIFileInfo(const std::string& filename,
+                     const std::string& ownerId,
+                     const Poco::Timestamp& modifiedTime,
+                     size_t size,
+                     const std::string& userid,
                      const std::string& username,
+                     const std::string& hostInstanceId,
                      const bool userCanWrite,
                      const std::string& postMessageOrigin,
                      const bool hidePrintOption,
@@ -191,8 +214,10 @@ public:
                      const bool disableExport,
                      const bool disableCopy,
                      const std::chrono::duration<double> callDuration)
-            : _userid(userid),
+            : FileInfo(filename, ownerId, modifiedTime, size),
+              _userid(userid),
               _username(username),
+              _hostInstanceId(hostInstanceId),
               _userCanWrite(userCanWrite),
               _postMessageOrigin(postMessageOrigin),
               _hidePrintOption(hidePrintOption),
@@ -210,6 +235,8 @@ public:
         std::string _userid;
         /// Display Name of user accessing the file
         std::string _username;
+        /// Host instance ID (unique to the given host).
+        std::string _hostInstanceId;
         /// If user accessing the file has write permission
         bool _userCanWrite;
         /// WOPI Post message property
@@ -236,7 +263,12 @@ public:
     /// provided during the initial creation of the WOPI storage.
     /// Also extracts the basic file information from the response
     /// which can then be obtained using getFileInfo()
-    std::unique_ptr<WOPIFileInfo> getWOPIFileInfo(const std::string& accessToken);
+    std::unique_ptr<WOPIFileInfo> getWOPIFileInfo(const std::string& accessToken)
+    {
+        std::unique_ptr<WOPIFileInfo> info = getWOPIFileInfo(_uri, accessToken);
+        _fileInfo = FileInfo(info->_filename, info->_ownerId, info->_modifiedTime, info->_size);
+        return info;
+    }
 
     /// uri format: http://server/<...>/wopi*/files/<id>/content
     std::string loadStorageFileToLocal(const std::string& accessToken) override;
@@ -246,6 +278,11 @@ public:
     /// Total time taken for making WOPI calls during load
     std::chrono::duration<double> getWopiLoadDuration() const { return _wopiLoadDuration; }
 
+    /// Given the URI of a doc, return a unique doc ID.
+    static std::string getUniqueDocId(const Poco::URI& uri);
+
+    static std::unique_ptr<WOPIFileInfo> getWOPIFileInfo(Poco::URI uriObject, const std::string& accessToken);
+
 private:
     // Time spend in loading the file from storage
     std::chrono::duration<double> _wopiLoadDuration;
@@ -273,6 +310,13 @@ public:
 
     SaveResult saveLocalFileToStorage(const std::string& accessToken) override;
 
+    /// Given the URI of a doc, return a unique doc ID.
+    static std::string getUniqueDocId(const std::string& uri)
+    {
+        // TODO: Implement.
+        return uri;
+    }
+
 private:
     std::unique_ptr<AuthBase> _authAgent;
 };


More information about the Libreoffice-commits mailing list