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 <ke...@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 <ke...@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 <ke...@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 <vmik...@collabora.co.uk> Reviewed-by: pranavk <pran...@collabora.co.uk> Tested-by: Jan Holesovsky <ke...@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 <ke...@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 <vmik...@collabora.co.uk> Reviewed-by: pranavk <pran...@collabora.co.uk> Tested-by: pranavk <pran...@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; _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits