summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Holesovsky <kendy@collabora.com>2017-08-16 16:38:00 +0200
committerJan Holesovsky <kendy@collabora.com>2017-08-17 13:40:57 +0200
commitafcfac4bef95cf7800effbc90818a0bfb8b434c2 (patch)
treecf8c23059d54848bdc26265d3cea372ae97fd7cb
parentBetter wording. (diff)
downloadonline-afcfac4bef95cf7800effbc90818a0bfb8b434c2.tar.gz
online-afcfac4bef95cf7800effbc90818a0bfb8b434c2.zip
access_header: Infrastructure for providing custom headers for authentication.
Change-Id: I52e61dc01dbad0d501471e663aaf364d9bc23c52 Reviewed-on: https://gerrit.libreoffice.org/41223 Reviewed-by: Miklos Vajna <vmiklos@collabora.co.uk> Reviewed-by: pranavk <pranavk@collabora.co.uk> Tested-by: pranavk <pranavk@collabora.co.uk>
-rw-r--r--test/Makefile.am1
-rw-r--r--test/WhiteBoxTests.cpp45
-rw-r--r--wsd/Auth.cpp56
-rw-r--r--wsd/Auth.hpp36
-rw-r--r--wsd/ClientSession.cpp14
-rw-r--r--wsd/ClientSession.hpp2
-rw-r--r--wsd/DocumentBroker.cpp8
-rw-r--r--wsd/Storage.cpp45
-rw-r--r--wsd/Storage.hpp18
9 files changed, 177 insertions, 48 deletions
diff --git a/test/Makefile.am b/test/Makefile.am
index 9ff9bb4476..5f7b769d0c 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 7079c60af3..adab5ed0c3 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 0def0fefa1..088719d780 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 87aa54b757..de41aeb691 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 d4793e9648..77e057448a 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 0002d6152d..fd3a05b84d 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 f697c6630f..7f2943362e 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 47c81748b0..72bae46afe 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 09002bde2b..6b26c06654 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;