[Libreoffice-commits] online.git: 4 commits - loleaflet/debug loleaflet/dist loleaflet/main.js test/Makefile.am test/test.cpp test/UnitOAuth.cpp test/WhiteBoxTests.cpp wsd/Auth.cpp wsd/Auth.hpp wsd/ClientSession.cpp wsd/ClientSession.hpp wsd/DocumentBroker.cpp wsd/FileServer.cpp wsd/Storage.cpp wsd/Storage.hpp

Jan Holesovsky kendy at collabora.com
Thu Aug 17 12:06:43 UTC 2017


 loleaflet/debug/document/loleaflet.html |    5 +
 loleaflet/dist/loleaflet.html           |    1 
 loleaflet/main.js                       |    3 +
 test/Makefile.am                        |    3 -
 test/UnitOAuth.cpp                      |   85 +++++++++++++++++++++-----------
 test/WhiteBoxTests.cpp                  |   45 ++++++++++++++++
 test/test.cpp                           |    4 -
 wsd/Auth.cpp                            |   56 +++++++++++++++++++++
 wsd/Auth.hpp                            |   40 ++++++++++++++-
 wsd/ClientSession.cpp                   |   14 ++++-
 wsd/ClientSession.hpp                   |    2 
 wsd/DocumentBroker.cpp                  |    8 +--
 wsd/FileServer.cpp                      |    6 +-
 wsd/Storage.cpp                         |   45 +++++-----------
 wsd/Storage.hpp                         |   18 +++---
 15 files changed, 253 insertions(+), 82 deletions(-)

New commits:
commit a3a3d0ad6c3358a47c178185da6c2348e3374f21
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Thu Aug 17 14:05:22 2017 +0200

    Fix various nitpicks.
    
    Change-Id: I41fe795bc1ea7c73527c7e1183de7098517bad7a

diff --git a/test/UnitOAuth.cpp b/test/UnitOAuth.cpp
index ed82982e..7a52c1ee 100644
--- a/test/UnitOAuth.cpp
+++ b/test/UnitOAuth.cpp
@@ -28,20 +28,21 @@ using Poco::Net::OAuth20Credentials;
 
 class UnitOAuth : public UnitWSD
 {
-    enum class Phase {
-        Load0,  // loading the document with Bearer token
-        Load1,  // loading the document with Basic auth
-        Polling // let the loading progress, and when it succeeds, finish
+    enum class Phase
+    {
+        LoadToken,  // loading the document with Bearer token
+        LoadHeader, // loading the document with Basic auth
+        Polling     // let the loading progress, and when it succeeds, finish
     } _phase;
 
-    bool _finished0;
-    bool _finished1;
+    bool _finishedToken;
+    bool _finishedHeader;
 
 public:
     UnitOAuth() :
-        _phase(Phase::Load0),
-        _finished0(false),
-        _finished1(false)
+        _phase(Phase::LoadToken),
+        _finishedToken(false),
+        _finishedHeader(false)
     {
     }
 
@@ -123,12 +124,12 @@ public:
             if (uriReq.getPath() == "/wopi/files/0/contents")
             {
                 assertRequest(request, 0);
-                _finished0 = true;
+                _finishedToken = true;
             }
             else
             {
                 assertRequest(request, 1);
-                _finished1 = true;
+                _finishedHeader = true;
             }
 
             const std::string mimeType = "text/plain; charset=utf-8";
@@ -145,7 +146,7 @@ public:
             socket->send(oss.str());
             socket->shutdown();
 
-            if (_finished0 && _finished1)
+            if (_finishedToken && _finishedHeader)
                 exitTest(TestResult::Ok);
 
             return true;
@@ -160,11 +161,11 @@ public:
 
         switch (_phase)
         {
-            case Phase::Load0:
-            case Phase::Load1:
+            case Phase::LoadToken:
+            case Phase::LoadHeader:
             {
                 Poco::URI wopiURL(helpers::getTestServerURI() +
-                        ((_phase == Phase::Load0)? "/wopi/files/0?access_token=s3hn3ct0k3v":
+                        ((_phase == Phase::LoadToken)? "/wopi/files/0?access_token=s3hn3ct0k3v":
                                                    "/wopi/files/1?access_header=Authorization: Basic basic=="));
                 //wopiURL.setPort(_wopiSocket->address().port());
                 std::string wopiSrc;
@@ -178,8 +179,8 @@ public:
 
                 helpers::sendTextFrame(*ws->getLOOLWebSocket(), "load url=" + wopiSrc, testName);
 
-                if (_phase == Phase::Load0)
-                    _phase = Phase::Load1;
+                if (_phase == Phase::LoadToken)
+                    _phase = Phase::LoadHeader;
                 else
                     _phase = Phase::Polling;
                 break;
diff --git a/test/WhiteBoxTests.cpp b/test/WhiteBoxTests.cpp
index adab5ed0..7c9b13f2 100644
--- a/test/WhiteBoxTests.cpp
+++ b/test/WhiteBoxTests.cpp
@@ -427,15 +427,15 @@ void WhiteBoxTests::testAuthorization()
     Authorization auth1(Authorization::Type::Token, "abc");
     Poco::URI uri1("http://localhost");
     auth1.authorizeURI(uri1);
-    CPPUNIT_ASSERT_EQUAL(uri1.toString(), std::string("http://localhost/?access_token=abc"));
+    CPPUNIT_ASSERT_EQUAL(std::string("http://localhost/?access_token=abc"), uri1.toString());
     Poco::Net::HTTPRequest req1;
     auth1.authorizeRequest(req1);
-    CPPUNIT_ASSERT_EQUAL(req1.get("Authorization"), std::string("Bearer abc"));
+    CPPUNIT_ASSERT_EQUAL(std::string("Bearer abc"), req1.get("Authorization"));
 
     Authorization auth1modify(Authorization::Type::Token, "modified");
     // still the same uri1, currently "http://localhost/?access_token=abc"
     auth1modify.authorizeURI(uri1);
-    CPPUNIT_ASSERT_EQUAL(uri1.toString(), std::string("http://localhost/?access_token=modified"));
+    CPPUNIT_ASSERT_EQUAL(std::string("http://localhost/?access_token=modified"), uri1.toString());
 
     Authorization auth2(Authorization::Type::Header, "def");
     Poco::Net::HTTPRequest req2;
@@ -446,22 +446,22 @@ void WhiteBoxTests::testAuthorization()
     Poco::URI uri2("http://localhost");
     auth3.authorizeURI(uri2);
     // nothing added with the Authorization header approach
-    CPPUNIT_ASSERT_EQUAL(uri2.toString(), std::string("http://localhost"));
+    CPPUNIT_ASSERT_EQUAL(std::string("http://localhost"), uri2.toString());
     Poco::Net::HTTPRequest req3;
     auth3.authorizeRequest(req3);
-    CPPUNIT_ASSERT_EQUAL(req3.get("Authorization"), std::string("Basic huhu=="));
+    CPPUNIT_ASSERT_EQUAL(std::string("Basic huhu=="), req3.get("Authorization"));
 
     Authorization auth4(Authorization::Type::Header, "  Authorization: Basic blah== \n\r X-Something:   additional  ");
     Poco::Net::HTTPRequest req4;
     auth4.authorizeRequest(req4);
-    CPPUNIT_ASSERT_EQUAL(req4.get("Authorization"), std::string("Basic blah=="));
-    CPPUNIT_ASSERT_EQUAL(req4.get("X-Something"), std::string("additional"));
+    CPPUNIT_ASSERT_EQUAL(std::string("Basic blah=="), req4.get("Authorization"));
+    CPPUNIT_ASSERT_EQUAL(std::string("additional"), req4.get("X-Something"));
 
     Authorization auth5(Authorization::Type::Header, "  Authorization: Basic huh== \n\r X-Something-More:   else  \n\r");
     Poco::Net::HTTPRequest req5;
     auth5.authorizeRequest(req5);
-    CPPUNIT_ASSERT_EQUAL(req5.get("Authorization"), std::string("Basic huh=="));
-    CPPUNIT_ASSERT_EQUAL(req5.get("X-Something-More"), std::string("else"));
+    CPPUNIT_ASSERT_EQUAL(std::string("Basic huh=="), req5.get("Authorization"));
+    CPPUNIT_ASSERT_EQUAL(std::string("else"), req5.get("X-Something-More"));
 }
 
 CPPUNIT_TEST_SUITE_REGISTRATION(WhiteBoxTests);
diff --git a/wsd/Auth.hpp b/wsd/Auth.hpp
index de41aeb6..98c0a2ce 100644
--- a/wsd/Auth.hpp
+++ b/wsd/Auth.hpp
@@ -23,7 +23,8 @@
 class Authorization
 {
 public:
-    enum class Type {
+    enum class Type
+    {
         None,
         Token,
         Header
@@ -73,7 +74,8 @@ public:
           _aud(aud),
           _key(Poco::Crypto::RSAKey("", keyPath)),
           _digestEngine(_key, "SHA256")
-    {    }
+    {
+    }
 
     const std::string getAccessToken() override;
 
commit d78c0b164b3ae8278d51d453a16a69aefe4fec2a
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Thu Aug 17 14:04:22 2017 +0200

    unit tests: Really bail out on error; and no need for a tempfile.
    
    Change-Id: I53c1ab62bf9293217a5cada54c358292364ed60a

diff --git a/test/Makefile.am b/test/Makefile.am
index 5f7b769d..005618de 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -98,4 +98,4 @@ all-local: unittest
 	@echo "Running build-time unit tests.  For more thorough testing, please run 'make check'."
 	@echo
 	@fc-cache "@LO_PATH@"/share/fonts/truetype
-	@${top_builddir}/test/unittest 2> unittest.log || { cat unittest.log ; exit 1 ; }
+	@${top_builddir}/test/unittest
diff --git a/test/test.cpp b/test/test.cpp
index ed2f15af..e989e3fa 100644
--- a/test/test.cpp
+++ b/test/test.cpp
@@ -65,7 +65,7 @@ int main(int argc, char** argv)
 
     Log::initialize("tst", loglevel, true, false, {});
 
-    runClientTests(true, verbose);
+    return runClientTests(true, verbose)? 0: 1;
 }
 
 static bool IsStandalone = false;
@@ -134,7 +134,7 @@ bool runClientTests(bool standalone, bool verbose)
     outputter.setNoWrap();
     outputter.write();
 
-    return result.wasSuccessful() ? 0 : 1;
+    return result.wasSuccessful();
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit f8ca17278f8d1114e37e656025bbf0bac2d59cc7
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Thu Aug 17 11:47:14 2017 +0200

    access_header: Pass the access_header around + unit test.
    
    Change-Id: I5d6d93e289d8faceda59deae128e8124a0193d95
    Reviewed-on: https://gerrit.libreoffice.org/41243
    Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
    Reviewed-by: pranavk <pranavk at collabora.co.uk>
    Tested-by: Jan Holesovsky <kendy at collabora.com>

diff --git a/loleaflet/debug/document/loleaflet.html b/loleaflet/debug/document/loleaflet.html
index 33364cea..fc239f3b 100644
--- a/loleaflet/debug/document/loleaflet.html
+++ b/loleaflet/debug/document/loleaflet.html
@@ -92,9 +92,14 @@
     <script>
     var wopiSrc = getParameterByName('WOPISrc');
     var access_token = '%ACCESS_TOKEN%';
+    var access_header = '%ACCESS_HEADER%';
     if (wopiSrc !== '' && access_token !== '') {
         wopiSrc += '?access_token=' + access_token;
     }
+    else if (wopiSrc !== '' && access_header !== '') {
+        wopiSrc += '?access_header=' + access_header;
+    }
+
 
     var filePath = getParameterByName('file_path');
     var title = getParameterByName('title');
diff --git a/loleaflet/dist/loleaflet.html b/loleaflet/dist/loleaflet.html
index f4f5e541..13e60e54 100644
--- a/loleaflet/dist/loleaflet.html
+++ b/loleaflet/dist/loleaflet.html
@@ -91,6 +91,7 @@
       window.host = '%HOST%';
       window.access_token = '%ACCESS_TOKEN%';
       window.access_token_ttl = '%ACCESS_TOKEN_TTL%';
+      window.access_header = '%ACCESS_HEADER%';
       window.loleaflet_logging = '%LOLEAFLET_LOGGING%';
       window.outOfFocusTimeoutSecs = %OUT_OF_FOCUS_TIMEOUT_SECS%;
       window.idleTimeoutSecs = %IDLE_TIMEOUT_SECS%;
diff --git a/loleaflet/main.js b/loleaflet/main.js
index 48200cd9..42660a13 100644
--- a/loleaflet/main.js
+++ b/loleaflet/main.js
@@ -60,6 +60,9 @@ var wopiSrc = getParameterByName('WOPISrc');
 if (wopiSrc !== '' && access_token !== '') {
 	var wopiParams = { 'access_token': access_token, 'access_token_ttl': access_token_ttl };
 }
+else if (wopiSrc !== '' && access_header !== '') {
+	var wopiParams = { 'access_header': access_header };
+}
 
 var filePath = getParameterByName('file_path');
 var title = getParameterByName('title');
diff --git a/test/UnitOAuth.cpp b/test/UnitOAuth.cpp
index 0136d0c8..ed82982e 100644
--- a/test/UnitOAuth.cpp
+++ b/test/UnitOAuth.cpp
@@ -29,16 +29,44 @@ using Poco::Net::OAuth20Credentials;
 class UnitOAuth : public UnitWSD
 {
     enum class Phase {
-        Load,   // loading the document
+        Load0,  // loading the document with Bearer token
+        Load1,  // loading the document with Basic auth
         Polling // let the loading progress, and when it succeeds, finish
     } _phase;
 
+    bool _finished0;
+    bool _finished1;
+
 public:
     UnitOAuth() :
-        _phase(Phase::Load)
+        _phase(Phase::Load0),
+        _finished0(false),
+        _finished1(false)
     {
     }
 
+    void assertRequest(const Poco::Net::HTTPRequest& request, int fileIndex)
+    {
+        // check that the request contains the Authorization: header
+        try {
+            if (fileIndex == 0)
+            {
+                OAuth20Credentials creds(request);
+                CPPUNIT_ASSERT_EQUAL(std::string("s3hn3ct0k3v"), creds.getBearerToken());
+            }
+            else
+            {
+                OAuth20Credentials creds(request, "Basic");
+                CPPUNIT_ASSERT_EQUAL(std::string("basic=="), creds.getBearerToken());
+            }
+        }
+        catch (const std::exception&)
+        {
+            // fail as fast as possible
+            exit(1);
+        }
+    }
+
     /// Here we act as a WOPI server, so that we have a server that responds to
     /// the wopi requests without additional expensive setup.
     virtual bool handleHttpRequest(const Poco::Net::HTTPRequest& request, std::shared_ptr<StreamSocket>& socket) override
@@ -49,20 +77,11 @@ public:
         LOG_INF("Fake wopi host request: " << uriReq.toString());
 
         // CheckFileInfo
-        if (uriReq.getPath() == "/wopi/files/0")
+        if (uriReq.getPath() == "/wopi/files/0" || uriReq.getPath() == "/wopi/files/1")
         {
-            LOG_INF("Fake wopi host request, handling CheckFileInfo.");
+            LOG_INF("Fake wopi host request, handling CheckFileInfo: " << uriReq.getPath());
 
-            // check that the request contains the Authorization: header
-            try {
-                OAuth20Credentials creds(request);
-                CPPUNIT_ASSERT_EQUAL(creds.getBearerToken(), std::string("s3hn3ct0k3v"));
-            }
-            catch (const std::exception&)
-            {
-                // fail as fast as possible
-                exit(1);
-            }
+            assertRequest(request, (uriReq.getPath() == "/wopi/files/0")? 0: 1);
 
             Poco::LocalDateTime now;
             Poco::JSON::Object::Ptr fileInfo = new Poco::JSON::Object();
@@ -97,19 +116,19 @@ public:
             return true;
         }
         // GetFile
-        else if (uriReq.getPath() == "/wopi/files/0/contents")
+        else if (uriReq.getPath() == "/wopi/files/0/contents" || uriReq.getPath() == "/wopi/files/1/contents")
         {
-            LOG_INF("Fake wopi host request, handling GetFile.");
+            LOG_INF("Fake wopi host request, handling GetFile: " << uriReq.getPath());
 
-            // check that the request contains the Authorization: header
-            try {
-                OAuth20Credentials creds(request);
-                CPPUNIT_ASSERT_EQUAL(creds.getBearerToken(), std::string("s3hn3ct0k3v"));
+            if (uriReq.getPath() == "/wopi/files/0/contents")
+            {
+                assertRequest(request, 0);
+                _finished0 = true;
             }
-            catch (const std::exception&)
+            else
             {
-                // fail as fast as possible
-                exit(1);
+                assertRequest(request, 1);
+                _finished1 = true;
             }
 
             const std::string mimeType = "text/plain; charset=utf-8";
@@ -126,7 +145,8 @@ public:
             socket->send(oss.str());
             socket->shutdown();
 
-            exitTest(TestResult::Ok);
+            if (_finished0 && _finished1)
+                exitTest(TestResult::Ok);
 
             return true;
         }
@@ -140,9 +160,12 @@ public:
 
         switch (_phase)
         {
-            case Phase::Load:
+            case Phase::Load0:
+            case Phase::Load1:
             {
-                Poco::URI wopiURL(helpers::getTestServerURI() + "/wopi/files/0?access_token=s3hn3ct0k3v");
+                Poco::URI wopiURL(helpers::getTestServerURI() +
+                        ((_phase == Phase::Load0)? "/wopi/files/0?access_token=s3hn3ct0k3v":
+                                                   "/wopi/files/1?access_header=Authorization: Basic basic=="));
                 //wopiURL.setPort(_wopiSocket->address().port());
                 std::string wopiSrc;
                 Poco::URI::encode(wopiURL.toString(), ":/?", wopiSrc);
@@ -155,7 +178,10 @@ public:
 
                 helpers::sendTextFrame(*ws->getLOOLWebSocket(), "load url=" + wopiSrc, testName);
 
-                _phase = Phase::Polling;
+                if (_phase == Phase::Load0)
+                    _phase = Phase::Load1;
+                else
+                    _phase = Phase::Polling;
                 break;
             }
             case Phase::Polling:
diff --git a/wsd/FileServer.cpp b/wsd/FileServer.cpp
index f6c3c74f..f27d9272 100644
--- a/wsd/FileServer.cpp
+++ b/wsd/FileServer.cpp
@@ -462,12 +462,15 @@ void FileServerRequestHandler::preprocessFile(const HTTPRequest& request, Poco::
     const std::string& accessToken = form.get("access_token", "");
     const std::string& accessTokenTtl = form.get("access_token_ttl", "");
     LOG_TRC("access_token=" << accessToken << ", access_token_ttl=" << accessTokenTtl);
+    const std::string& accessHeader = form.get("access_header", "");
+    LOG_TRC("access_header=" << accessHeader);
 
     // Escape bad characters in access token.
     // This is placed directly in javascript in loleaflet.html, we need to make sure
     // that no one can do anything nasty with their clever inputs.
-    std::string escapedAccessToken;
+    std::string escapedAccessToken, escapedAccessHeader;
     Poco::URI::encode(accessToken, "'", escapedAccessToken);
+    Poco::URI::encode(accessHeader, "'", escapedAccessHeader);
 
     unsigned long tokenTtl = 0;
     if (accessToken != "")
@@ -491,6 +494,7 @@ void FileServerRequestHandler::preprocessFile(const HTTPRequest& request, Poco::
 
     Poco::replaceInPlace(preprocess, std::string("%ACCESS_TOKEN%"), escapedAccessToken);
     Poco::replaceInPlace(preprocess, std::string("%ACCESS_TOKEN_TTL%"), std::to_string(tokenTtl));
+    Poco::replaceInPlace(preprocess, std::string("%ACCESS_HEADER%"), escapedAccessHeader);
     Poco::replaceInPlace(preprocess, std::string("%HOST%"), host);
     Poco::replaceInPlace(preprocess, std::string("%VERSION%"), std::string(LOOLWSD_VERSION_HASH));
 
commit afcfac4bef95cf7800effbc90818a0bfb8b434c2
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Wed Aug 16 16:38:00 2017 +0200

    access_header: Infrastructure for providing custom headers for authentication.
    
    Change-Id: I52e61dc01dbad0d501471e663aaf364d9bc23c52
    Reviewed-on: https://gerrit.libreoffice.org/41223
    Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
    Reviewed-by: pranavk <pranavk at collabora.co.uk>
    Tested-by: pranavk <pranavk at collabora.co.uk>

diff --git a/test/Makefile.am b/test/Makefile.am
index 9ff9bb44..5f7b769d 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -37,6 +37,7 @@ wsd_sources = \
             ../common/Util.cpp \
             ../common/MessageQueue.cpp \
             ../kit/Kit.cpp \
+            ../wsd/Auth.cpp \
             ../wsd/TileCache.cpp \
             ../wsd/TestStubs.cpp \
             ../common/Unit.cpp \
diff --git a/test/WhiteBoxTests.cpp b/test/WhiteBoxTests.cpp
index 7079c60a..adab5ed0 100644
--- a/test/WhiteBoxTests.cpp
+++ b/test/WhiteBoxTests.cpp
@@ -11,6 +11,7 @@
 
 #include <cppunit/extensions/HelperMacros.h>
 
+#include <Auth.hpp>
 #include <ChildSession.hpp>
 #include <Common.hpp>
 #include <Kit.hpp>
@@ -32,6 +33,7 @@ class WhiteBoxTests : public CPPUNIT_NS::TestFixture
     CPPUNIT_TEST(testRegexListMatcher_Init);
     CPPUNIT_TEST(testEmptyCellCursor);
     CPPUNIT_TEST(testRectanglesIntersect);
+    CPPUNIT_TEST(testAuthorization);
 
     CPPUNIT_TEST_SUITE_END();
 
@@ -43,6 +45,7 @@ class WhiteBoxTests : public CPPUNIT_NS::TestFixture
     void testRegexListMatcher_Init();
     void testEmptyCellCursor();
     void testRectanglesIntersect();
+    void testAuthorization();
 };
 
 void WhiteBoxTests::testLOOLProtocolFunctions()
@@ -419,6 +422,48 @@ void WhiteBoxTests::testRectanglesIntersect()
                                                   1000, 1000, 2000, 1000));
 }
 
+void WhiteBoxTests::testAuthorization()
+{
+    Authorization auth1(Authorization::Type::Token, "abc");
+    Poco::URI uri1("http://localhost");
+    auth1.authorizeURI(uri1);
+    CPPUNIT_ASSERT_EQUAL(uri1.toString(), std::string("http://localhost/?access_token=abc"));
+    Poco::Net::HTTPRequest req1;
+    auth1.authorizeRequest(req1);
+    CPPUNIT_ASSERT_EQUAL(req1.get("Authorization"), std::string("Bearer abc"));
+
+    Authorization auth1modify(Authorization::Type::Token, "modified");
+    // still the same uri1, currently "http://localhost/?access_token=abc"
+    auth1modify.authorizeURI(uri1);
+    CPPUNIT_ASSERT_EQUAL(uri1.toString(), std::string("http://localhost/?access_token=modified"));
+
+    Authorization auth2(Authorization::Type::Header, "def");
+    Poco::Net::HTTPRequest req2;
+    auth2.authorizeRequest(req2);
+    CPPUNIT_ASSERT(!req2.has("Authorization"));
+
+    Authorization auth3(Authorization::Type::Header, "Authorization: Basic huhu== ");
+    Poco::URI uri2("http://localhost");
+    auth3.authorizeURI(uri2);
+    // nothing added with the Authorization header approach
+    CPPUNIT_ASSERT_EQUAL(uri2.toString(), std::string("http://localhost"));
+    Poco::Net::HTTPRequest req3;
+    auth3.authorizeRequest(req3);
+    CPPUNIT_ASSERT_EQUAL(req3.get("Authorization"), std::string("Basic huhu=="));
+
+    Authorization auth4(Authorization::Type::Header, "  Authorization: Basic blah== \n\r X-Something:   additional  ");
+    Poco::Net::HTTPRequest req4;
+    auth4.authorizeRequest(req4);
+    CPPUNIT_ASSERT_EQUAL(req4.get("Authorization"), std::string("Basic blah=="));
+    CPPUNIT_ASSERT_EQUAL(req4.get("X-Something"), std::string("additional"));
+
+    Authorization auth5(Authorization::Type::Header, "  Authorization: Basic huh== \n\r X-Something-More:   else  \n\r");
+    Poco::Net::HTTPRequest req5;
+    auth5.authorizeRequest(req5);
+    CPPUNIT_ASSERT_EQUAL(req5.get("Authorization"), std::string("Basic huh=="));
+    CPPUNIT_ASSERT_EQUAL(req5.get("X-Something-More"), std::string("else"));
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(WhiteBoxTests);
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/wsd/Auth.cpp b/wsd/Auth.cpp
index 0def0fef..088719d7 100644
--- a/wsd/Auth.cpp
+++ b/wsd/Auth.cpp
@@ -37,6 +37,62 @@ using Poco::Base64Decoder;
 using Poco::Base64Encoder;
 using Poco::OutputLineEndingConverter;
 
+void Authorization::authorizeURI(Poco::URI& uri) const
+{
+    if (_type == Authorization::Type::Token)
+    {
+        static const std::string key("access_token");
+
+        Poco::URI::QueryParameters queryParams = uri.getQueryParameters();
+        for (auto& param: queryParams)
+        {
+            if (param.first == key)
+            {
+                param.second = _data;
+                uri.setQueryParameters(queryParams);
+                return;
+            }
+        }
+
+        // it did not exist yet
+        uri.addQueryParameter(key, _data);
+    }
+}
+
+void Authorization::authorizeRequest(Poco::Net::HTTPRequest& request) const
+{
+    switch (_type)
+    {
+        case Type::Token:
+            request.set("Authorization", "Bearer " + _data);
+            break;
+        case Type::Header:
+        {
+            // there might be more headers in here; like
+            //   Authorization: Basic ....
+            //   X-Something-Custom: Huh
+            Poco::StringTokenizer tokens(_data, "\n\r", Poco::StringTokenizer::TOK_IGNORE_EMPTY | Poco::StringTokenizer::TOK_TRIM);
+            for (const auto& token : tokens)
+            {
+                size_t i = token.find_first_of(':');
+                if (i != std::string::npos)
+                {
+                    size_t separator = i;
+                    for (++i; i < token.length() && token[i] == ' ';)
+                        ++i;
+
+                    // set the header
+                    if (i < token.length())
+                        request.set(token.substr(0, separator), token.substr(i));
+                }
+            }
+            break;
+        }
+        default:
+            assert(false);
+    }
+}
+
 const std::string JWTAuth::getAccessToken()
 {
     std::string encodedHeader = createHeader();
diff --git a/wsd/Auth.hpp b/wsd/Auth.hpp
index 87aa54b7..de41aeb6 100644
--- a/wsd/Auth.hpp
+++ b/wsd/Auth.hpp
@@ -11,10 +11,46 @@
 #ifndef INCLUDED_AUTH_HPP
 #define INCLUDED_AUTH_HPP
 
+#include <cassert>
 #include <string>
 
 #include <Poco/Crypto/RSADigestEngine.h>
 #include <Poco/Crypto/RSAKey.h>
+#include <Poco/Net/HTTPRequest.h>
+#include <Poco/URI.h>
+
+/// Class to keep the authorization data.
+class Authorization
+{
+public:
+    enum class Type {
+        None,
+        Token,
+        Header
+    };
+
+private:
+    Type _type;
+    std::string _data;
+
+public:
+    Authorization()
+        : _type(Type::None)
+    {
+    }
+
+    Authorization(Type type, const std::string& data)
+        : _type(type)
+        , _data(data)
+    {
+    }
+
+    /// Set the access_token parametr to the given uri.
+    void authorizeURI(Poco::URI& uri) const;
+
+    /// Set the Authorization: header in request.
+    void authorizeRequest(Poco::Net::HTTPRequest& request) const;
+};
 
 /// Base class of all Authentication/Authorization implementations.
 class AuthBase
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index d4793e96..77e05744 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -769,17 +769,25 @@ bool ClientSession::forwardToClient(const std::shared_ptr<Message>& payload)
     return true;
 }
 
-std::string ClientSession::getAccessToken() const
+Authorization ClientSession::getAuthorization() const
 {
     std::string accessToken;
     Poco::URI::QueryParameters queryParams = _uriPublic.getQueryParameters();
+
+    // prefer the access_token
     for (auto& param: queryParams)
     {
         if (param.first == "access_token")
-            return param.second;
+            return Authorization(Authorization::Type::Token, param.second);
+    }
+
+    for (auto& param: queryParams)
+    {
+        if (param.first == "access_header")
+            return Authorization(Authorization::Type::Header, param.second);
     }
 
-    return std::string();
+    return Authorization();
 }
 
 void ClientSession::onDisconnect()
diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp
index 0002d615..fd3a05b8 100644
--- a/wsd/ClientSession.hpp
+++ b/wsd/ClientSession.hpp
@@ -98,7 +98,7 @@ public:
     const Poco::URI& getPublicUri() const { return _uriPublic; }
 
     /// The access token of this session.
-    std::string getAccessToken() const;
+    Authorization getAuthorization() const;
 
     /// Set WOPI fileinfo object
     void setWopiFileInfo(std::unique_ptr<WopiStorage::WOPIFileInfo>& wopiFileInfo) { _wopiFileInfo = std::move(wopiFileInfo); }
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index f697c663..7f294336 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -428,7 +428,7 @@ bool DocumentBroker::load(const std::shared_ptr<ClientSession>& session, const s
     WopiStorage* wopiStorage = dynamic_cast<WopiStorage*>(_storage.get());
     if (wopiStorage != nullptr)
     {
-        std::unique_ptr<WopiStorage::WOPIFileInfo> wopifileinfo = wopiStorage->getWOPIFileInfo(session->getAccessToken());
+        std::unique_ptr<WopiStorage::WOPIFileInfo> wopifileinfo = wopiStorage->getWOPIFileInfo(session->getAuthorization());
         userid = wopifileinfo->_userid;
         username = wopifileinfo->_username;
         userExtraInfo = wopifileinfo->_userExtraInfo;
@@ -552,7 +552,7 @@ bool DocumentBroker::load(const std::shared_ptr<ClientSession>& session, const s
     // Let's load the document now, if not loaded.
     if (!_storage->isLoaded())
     {
-        const auto localPath = _storage->loadStorageFileToLocal(session->getAccessToken());
+        const auto localPath = _storage->loadStorageFileToLocal(session->getAuthorization());
 
         std::ifstream istr(localPath, std::ios::binary);
         Poco::SHA1Engine sha1;
@@ -639,7 +639,7 @@ bool DocumentBroker::saveToStorageInternal(const std::string& sessionId,
         return false;
     }
 
-    const std::string accessToken = it->second->getAccessToken();
+    const Authorization auth = it->second->getAuthorization();
     const auto uri = it->second->getPublicUri().toString();
 
     // If we aren't destroying the last editable session just yet,
@@ -658,7 +658,7 @@ bool DocumentBroker::saveToStorageInternal(const std::string& sessionId,
     LOG_DBG("Persisting [" << _docKey << "] after saving to URI [" << uri << "].");
 
     assert(_storage && _tileCache);
-    StorageBase::SaveResult storageSaveResult = _storage->saveLocalFileToStorage(accessToken);
+    StorageBase::SaveResult storageSaveResult = _storage->saveLocalFileToStorage(auth);
     if (storageSaveResult == StorageBase::SaveResult::OK)
     {
         setModified(false);
diff --git a/wsd/Storage.cpp b/wsd/Storage.cpp
index 47c81748..72bae46a 100644
--- a/wsd/Storage.cpp
+++ b/wsd/Storage.cpp
@@ -246,7 +246,7 @@ std::unique_ptr<LocalStorage::LocalFileInfo> LocalStorage::getLocalFileInfo()
     return std::unique_ptr<LocalStorage::LocalFileInfo>(new LocalFileInfo({"localhost" + std::to_string(LastLocalStorageId), "Local Host #" + std::to_string(LastLocalStorageId++)}));
 }
 
-std::string LocalStorage::loadStorageFileToLocal(const std::string& /*accessToken*/)
+std::string LocalStorage::loadStorageFileToLocal(const Authorization& /*auth*/)
 {
     // /chroot/jailId/user/doc/childId/file.ext
     const auto filename = Poco::Path(_uri.getPath()).getFileName();
@@ -297,7 +297,7 @@ std::string LocalStorage::loadStorageFileToLocal(const std::string& /*accessToke
 #endif
 }
 
-StorageBase::SaveResult LocalStorage::saveLocalFileToStorage(const std::string& /*accessToken*/)
+StorageBase::SaveResult LocalStorage::saveLocalFileToStorage(const Authorization& /*auth*/)
 {
     try
     {
@@ -440,24 +440,6 @@ bool parseJSON(const std::string& json, Poco::JSON::Object::Ptr& object)
     return success;
 }
 
-void setQueryParameter(Poco::URI& uriObject, const std::string& key, const std::string& value)
-{
-    Poco::URI::QueryParameters queryParams = uriObject.getQueryParameters();
-    for (auto& param: queryParams)
-    {
-        if (param.first == key)
-        {
-            param.second = value;
-            uriObject.setQueryParameters(queryParams);
-            return;
-        }
-    }
-
-    // it did not exist yet
-    uriObject.addQueryParameter(key, value);
-}
-
-
 void addStorageDebugCookie(Poco::Net::HTTPRequest& request)
 {
     (void) request;
@@ -499,11 +481,11 @@ Poco::Timestamp iso8601ToTimestamp(const std::string& iso8601Time)
 
 } // anonymous namespace
 
-std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const std::string& accessToken)
+std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const Authorization& auth)
 {
     // update the access_token to the one matching to the session
     Poco::URI uriObject(_uri);
-    setQueryParameter(uriObject, "access_token", accessToken);
+    auth.authorizeURI(uriObject);
 
     LOG_DBG("Getting info for wopi uri [" << uriObject.toString() << "].");
 
@@ -516,7 +498,7 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const st
 
         Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, uriObject.getPathAndQuery(), Poco::Net::HTTPMessage::HTTP_1_1);
         request.set("User-Agent", WOPI_AGENT_STRING);
-        request.set("Authorization", "Bearer " + accessToken);
+        auth.authorizeRequest(request);
         addStorageDebugCookie(request);
         psession->sendRequest(request);
 
@@ -603,13 +585,14 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const st
 }
 
 /// uri format: http://server/<...>/wopi*/files/<id>/content
-std::string WopiStorage::loadStorageFileToLocal(const std::string& accessToken)
+std::string WopiStorage::loadStorageFileToLocal(const Authorization& auth)
 {
     // WOPI URI to download files ends in '/contents'.
     // Add it here to get the payload instead of file info.
     Poco::URI uriObject(_uri);
     uriObject.setPath(uriObject.getPath() + "/contents");
-    setQueryParameter(uriObject, "access_token", accessToken);
+    auth.authorizeURI(uriObject);
+
     LOG_DBG("Wopi requesting: " << uriObject.toString());
 
     const auto startTime = std::chrono::steady_clock::now();
@@ -619,7 +602,7 @@ std::string WopiStorage::loadStorageFileToLocal(const std::string& accessToken)
 
         Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, uriObject.getPathAndQuery(), Poco::Net::HTTPMessage::HTTP_1_1);
         request.set("User-Agent", WOPI_AGENT_STRING);
-        request.set("Authorization", "Bearer " + accessToken);
+        auth.authorizeRequest(request);
         addStorageDebugCookie(request);
         psession->sendRequest(request);
 
@@ -670,14 +653,14 @@ std::string WopiStorage::loadStorageFileToLocal(const std::string& accessToken)
     return "";
 }
 
-StorageBase::SaveResult WopiStorage::saveLocalFileToStorage(const std::string& accessToken)
+StorageBase::SaveResult WopiStorage::saveLocalFileToStorage(const Authorization& auth)
 {
     // TODO: Check if this URI has write permission (canWrite = true)
     const auto size = getFileSize(_jailedFilePath);
 
     Poco::URI uriObject(_uri);
     uriObject.setPath(uriObject.getPath() + "/contents");
-    setQueryParameter(uriObject, "access_token", accessToken);
+    auth.authorizeURI(uriObject);
 
     LOG_INF("Uploading URI via WOPI [" << uriObject.toString() << "] from [" << _jailedFilePath + "].");
 
@@ -689,7 +672,7 @@ StorageBase::SaveResult WopiStorage::saveLocalFileToStorage(const std::string& a
 
         Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_POST, uriObject.getPathAndQuery(), Poco::Net::HTTPMessage::HTTP_1_1);
         request.set("X-WOPI-Override", "PUT");
-        request.set("Authorization", "Bearer " + accessToken);
+        auth.authorizeRequest(request);
         if (!_forceSave)
         {
             // Request WOPI host to not overwrite if timestamps mismatch
@@ -768,14 +751,14 @@ StorageBase::SaveResult WopiStorage::saveLocalFileToStorage(const std::string& a
     return saveResult;
 }
 
-std::string WebDAVStorage::loadStorageFileToLocal(const std::string& /*accessToken*/)
+std::string WebDAVStorage::loadStorageFileToLocal(const Authorization& /*auth*/)
 {
     // TODO: implement webdav GET.
     _isLoaded = true;
     return _uri.toString();
 }
 
-StorageBase::SaveResult WebDAVStorage::saveLocalFileToStorage(const std::string& /*accessToken*/)
+StorageBase::SaveResult WebDAVStorage::saveLocalFileToStorage(const Authorization& /*auth*/)
 {
     // TODO: implement webdav PUT.
     return StorageBase::SaveResult::OK;
diff --git a/wsd/Storage.hpp b/wsd/Storage.hpp
index 09002bde..6b26c066 100644
--- a/wsd/Storage.hpp
+++ b/wsd/Storage.hpp
@@ -101,10 +101,10 @@ public:
 
     /// Returns a local file path for the given URI.
     /// If necessary copies the file locally first.
-    virtual std::string loadStorageFileToLocal(const std::string& accessToken) = 0;
+    virtual std::string loadStorageFileToLocal(const Authorization& auth) = 0;
 
     /// Writes the contents of the file back to the source.
-    virtual SaveResult saveLocalFileToStorage(const std::string& accessToken) = 0;
+    virtual SaveResult saveLocalFileToStorage(const Authorization& auth) = 0;
 
     static size_t getFileSize(const std::string& filename);
 
@@ -168,9 +168,9 @@ public:
     /// obtained using getFileInfo method
     std::unique_ptr<LocalFileInfo> getLocalFileInfo();
 
-    std::string loadStorageFileToLocal(const std::string& accessToken) override;
+    std::string loadStorageFileToLocal(const Authorization& auth) override;
 
-    SaveResult saveLocalFileToStorage(const std::string& accessToken) override;
+    SaveResult saveLocalFileToStorage(const Authorization& auth) override;
 
 private:
     /// True if the jailed file is not linked but copied.
@@ -256,12 +256,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 Authorization& auth);
 
     /// uri format: http://server/<...>/wopi*/files/<id>/content
-    std::string loadStorageFileToLocal(const std::string& accessToken) override;
+    std::string loadStorageFileToLocal(const Authorization& auth) override;
 
-    SaveResult saveLocalFileToStorage(const std::string& accessToken) override;
+    SaveResult saveLocalFileToStorage(const Authorization& auth) override;
 
     /// Total time taken for making WOPI calls during load
     std::chrono::duration<double> getWopiLoadDuration() const { return _wopiLoadDuration; }
@@ -289,9 +289,9 @@ public:
     // Implement me
     // WebDAVFileInfo getWebDAVFileInfo(const Poco::URI& uriPublic);
 
-    std::string loadStorageFileToLocal(const std::string& accessToken) override;
+    std::string loadStorageFileToLocal(const Authorization& auth) override;
 
-    SaveResult saveLocalFileToStorage(const std::string& accessToken) override;
+    SaveResult saveLocalFileToStorage(const Authorization& auth) override;
 
 private:
     std::unique_ptr<AuthBase> _authAgent;


More information about the Libreoffice-commits mailing list