wsd: use hostname, port and scheme in doc key
This allows us to use multiple hosts using same coolwsd instance. added aliases configuration to coolwsd.xml to avoid possibility of opening the same file as two if the WOPI host is accessed using different aliases Signed-off-by: Rash419 <rashesh.padia@collabora.com> Change-Id: I32913015c15fd396cecc702b76e0dcaa8bcafad3pull/4422/head cp-21.11.3-0
parent
4bd6b39f71
commit
d02dd19f33
|
@ -204,6 +204,13 @@
|
|||
<locking desc="Locking settings">
|
||||
<refresh desc="How frequently we should re-acquire a lock with the storage server, in seconds (default 15 mins) or 0 for no refresh" type="int" default="900">900</refresh>
|
||||
</locking>
|
||||
|
||||
<!-- <group>
|
||||
<host desc="hostname to allow or deny." allow="true">hostname</host>
|
||||
<alias>aliasname1</alias>
|
||||
<alias>aliasname2</alias>
|
||||
</group> -->
|
||||
|
||||
</wopi>
|
||||
<ssl desc="SSL settings">
|
||||
<as_scheme type="bool" default="true" desc="When set we exclusively use the WOPI URI's scheme to enable SSL for storage">true</as_scheme>
|
||||
|
|
|
@ -102,6 +102,7 @@ wsd_sources = \
|
|||
../common/Authorization.cpp \
|
||||
../kit/Kit.cpp \
|
||||
../kit/TestStubs.cpp \
|
||||
../wsd/Storage.cpp \
|
||||
../wsd/FileServerUtil.cpp \
|
||||
../wsd/RequestDetails.cpp \
|
||||
../wsd/TileCache.cpp \
|
||||
|
|
|
@ -1138,6 +1138,8 @@ public:
|
|||
|
||||
fetchWopiHostPatterns(newAppConfig, remoteJson);
|
||||
|
||||
fetchAliasGroups(newAppConfig, remoteJson);
|
||||
|
||||
#ifdef ENABLE_FEATURE_LOCK
|
||||
fetchLockedHostPatterns(newAppConfig, remoteJson);
|
||||
#endif
|
||||
|
@ -1147,6 +1149,8 @@ public:
|
|||
|
||||
StorageBase::parseWopiHost(conf);
|
||||
|
||||
StorageBase::parseAliases(conf);
|
||||
|
||||
#ifdef ENABLE_FEATURE_LOCK
|
||||
CommandControl::LockManager::parseLockedHost(conf);
|
||||
#endif
|
||||
|
@ -1158,7 +1162,7 @@ public:
|
|||
//wopi host patterns
|
||||
if (!conf.getBool("storage.wopi[@allow]", false))
|
||||
{
|
||||
LOG_INF("WOPI host feature is disabled in coolwsd.xml");
|
||||
LOG_INF("WOPI host feature is disabled in configuration");
|
||||
return;
|
||||
}
|
||||
try
|
||||
|
@ -1211,7 +1215,7 @@ public:
|
|||
{
|
||||
if (!conf.getBool("feature_lock.locked_hosts[@allow]", false))
|
||||
{
|
||||
LOG_INF("locked_hosts feature is disabled from coolwsd.xml");
|
||||
LOG_INF("locked_hosts feature is disabled from configuration");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1267,6 +1271,74 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void fetchAliasGroups(std::map<std::string, std::string>& newAppConfig,
|
||||
Poco::JSON::Object::Ptr remoteJson)
|
||||
{
|
||||
try
|
||||
{
|
||||
Poco::JSON::Array::Ptr aliasGroups =
|
||||
remoteJson->getObject("storage")->getObject("wopi")->getArray("alias_groups");
|
||||
|
||||
if (aliasGroups->size() == 0)
|
||||
{
|
||||
LOG_WRN("Not overwriting any alias groups because alias_group array is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
std::size_t i;
|
||||
for (i = 0; i < aliasGroups->size(); i++)
|
||||
{
|
||||
Poco::JSON::Object::Ptr group = aliasGroups->getObject(i);
|
||||
std::string host;
|
||||
JsonUtil::findJSONValue(group, "host", host);
|
||||
Poco::Dynamic::Var allow = group->get("allow");
|
||||
const std::string path = "storage.wopi.group[" + std::to_string(i) + ']';
|
||||
|
||||
newAppConfig.insert(std::make_pair(path + ".host", host));
|
||||
newAppConfig.insert(std::make_pair(path + ".host[@allow]", booleanToString(allow)));
|
||||
|
||||
Poco::JSON::Array::Ptr aliases = group->getArray("aliases");
|
||||
|
||||
auto it = aliases->begin();
|
||||
|
||||
size_t j;
|
||||
for (j = 0; j < aliases->size(); j++)
|
||||
{
|
||||
const std::string aliasPath = path + ".alias[" + std::to_string(j) + ']';
|
||||
newAppConfig.insert(std::make_pair(aliasPath, it->toString()));
|
||||
it++;
|
||||
}
|
||||
for (;; j++)
|
||||
{
|
||||
const std::string aliasPath = path + ".alias[" + std::to_string(j) + ']';
|
||||
if (!conf.has(aliasPath))
|
||||
{
|
||||
break;
|
||||
}
|
||||
newAppConfig.insert(std::make_pair(aliasPath, ""));
|
||||
}
|
||||
}
|
||||
|
||||
//if number of alias_groups defined in configuration are greater than number of alias_group
|
||||
//fetched from json, overwrite the remaining alias_groups from config file to empty strings and
|
||||
for (;; i++)
|
||||
{
|
||||
const std::string path = "storage.wopi.group[" + std::to_string(i) + "].host";
|
||||
if (!conf.has(path))
|
||||
{
|
||||
break;
|
||||
}
|
||||
newAppConfig.insert(std::make_pair(path, ""));
|
||||
newAppConfig.insert(std::make_pair(path + "[@allowed]", "false"));
|
||||
}
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
LOG_ERR("Fetching of alias groups failed with error: " << exc.what()
|
||||
<< "please check JSON format");
|
||||
}
|
||||
}
|
||||
|
||||
//sets property to false if it is missing from JSON
|
||||
//and returns std::string
|
||||
std::string booleanToString(Poco::Dynamic::Var& booleanFlag)
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "COOLWSD.hpp"
|
||||
#include "RequestDetails.hpp"
|
||||
#include "common/Log.hpp"
|
||||
#include "Storage.hpp"
|
||||
|
||||
#include <Poco/URI.h>
|
||||
#include "Exceptions.hpp"
|
||||
|
@ -283,15 +284,15 @@ Poco::URI RequestDetails::sanitizeURI(const std::string& uri)
|
|||
|
||||
std::string RequestDetails::getDocKey(const Poco::URI& uri)
|
||||
{
|
||||
// If multiple host-names are used to access us, then
|
||||
// they must be aliases. Permission to access aliased hosts
|
||||
// is checked at the point of accepting incoming connections.
|
||||
// At this point storing the hostname artificially discriminates
|
||||
// between aliases and forces same document (when opened from
|
||||
// alias hosts) to load as separate documents and sharing doesn't
|
||||
// work. Worse, saving overwrites one another.
|
||||
std::string docKey;
|
||||
Poco::URI::encode(uri.getPath(), "", docKey);
|
||||
std::string newUri = uri.getPath();
|
||||
|
||||
// resolve aliases
|
||||
#if !MOBILEAPP
|
||||
newUri = StorageBase::getNewUri(uri);
|
||||
#endif
|
||||
|
||||
Poco::URI::encode(newUri, "", docKey);
|
||||
LOG_INF("DocKey from URI [" << uri.toString() << "] => [" << docKey << ']');
|
||||
return docKey;
|
||||
}
|
||||
|
|
150
wsd/Storage.cpp
150
wsd/Storage.cpp
|
@ -65,6 +65,9 @@ bool StorageBase::WopiEnabled;
|
|||
bool StorageBase::SSLAsScheme = true;
|
||||
bool StorageBase::SSLEnabled = false;
|
||||
Util::RegexListMatcher StorageBase::WopiHosts;
|
||||
std::map<std::string, std::string> StorageBase::AliasHosts;
|
||||
std::set<std::string> StorageBase::AllHosts;
|
||||
std::string StorageBase::FirstHost;
|
||||
|
||||
#if !MOBILEAPP
|
||||
|
||||
|
@ -94,30 +97,115 @@ void StorageBase::parseWopiHost(Poco::Util::LayeredConfiguration& conf)
|
|||
for (size_t i = 0;; ++i)
|
||||
{
|
||||
const std::string path = "storage.wopi.host[" + std::to_string(i) + ']';
|
||||
const std::string host = conf.getString(path, "");
|
||||
if (!host.empty())
|
||||
{
|
||||
if (conf.getBool(path + "[@allow]", false))
|
||||
{
|
||||
LOG_INF("Adding trusted WOPI host: [" << host << "].");
|
||||
WopiHosts.allow(host);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_INF("Adding blocked WOPI host: [" << host << "].");
|
||||
WopiHosts.deny(host);
|
||||
}
|
||||
}
|
||||
else if (!conf.has(path))
|
||||
if (!conf.has(path))
|
||||
{
|
||||
break;
|
||||
}
|
||||
StorageBase::addWopiHost(conf.getString(path, ""), conf.getBool(path + "[@allow]", false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StorageBase::addWopiHost(std::string host, bool allow)
|
||||
{
|
||||
if (!host.empty())
|
||||
{
|
||||
if (allow)
|
||||
{
|
||||
LOG_INF("Adding trusted WOPI host: [" << host << "].");
|
||||
WopiHosts.allow(host);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_INF("Adding blocked WOPI host: [" << host << "].");
|
||||
WopiHosts.deny(host);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StorageBase::parseAliases(Poco::Util::LayeredConfiguration& conf)
|
||||
{
|
||||
AliasHosts.clear();
|
||||
|
||||
for (size_t i = 0;; i++)
|
||||
{
|
||||
const std::string path = "storage.wopi.group[" + std::to_string(i) + ']';
|
||||
if (!conf.has(path + ".host"))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
const std::string hostAndPort = conf.getString(path + ".host", "");
|
||||
if (hostAndPort.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
StringVector tokens = Util::tokenize(hostAndPort, ":");
|
||||
bool allow = conf.getBool(path + ".host[@allow]", false);
|
||||
StorageBase::addWopiHost(tokens[0], allow);
|
||||
AllHosts.insert(tokens[0]);
|
||||
|
||||
for (size_t j = 0;; j++)
|
||||
{
|
||||
const std::string aliasPath = path + ".alias[" + std::to_string(j) + ']';
|
||||
if (!conf.has(aliasPath))
|
||||
{
|
||||
break;
|
||||
}
|
||||
const std::string aliasHostAndPort = conf.getString(aliasPath, "");
|
||||
if (!aliasHostAndPort.empty())
|
||||
{
|
||||
AliasHosts.insert({ aliasHostAndPort, hostAndPort });
|
||||
}
|
||||
|
||||
tokens = Util::tokenize(aliasHostAndPort, ":");
|
||||
AllHosts.insert(tokens[0]);
|
||||
StorageBase::addWopiHost(tokens[0], allow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string StorageBase::getNewUri(const Poco::URI& uri)
|
||||
{
|
||||
std::string uriHost = uri.getHost();
|
||||
std::string uriPort = std::to_string(uri.getPort());
|
||||
const std::string uriScheme = uri.getScheme();
|
||||
const std::string uriPath = uri.getPath();
|
||||
|
||||
const std::string key = uriHost + ":" + uriPort;
|
||||
if (AliasHosts.find(key) != AliasHosts.end())
|
||||
{
|
||||
const std::string aliasDetails = AliasHosts[key];
|
||||
StringVector tokens = Util::tokenize(aliasDetails, ':');
|
||||
|
||||
if (!tokens[0].empty())
|
||||
{
|
||||
uriHost = tokens[0];
|
||||
}
|
||||
|
||||
if (!tokens[1].empty())
|
||||
{
|
||||
uriPort = tokens[1];
|
||||
}
|
||||
}
|
||||
|
||||
std::string newUri;
|
||||
if (!uriScheme.empty() && !uriHost.empty() && !Util::iequal(uriPort, "0"))
|
||||
{
|
||||
newUri = uriScheme + "://" + uriHost + ":" + uriPort + uriPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
newUri = uri.getPath();
|
||||
}
|
||||
return newUri;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if !defined(BUILDING_TESTS)
|
||||
|
||||
void StorageBase::initialize()
|
||||
{
|
||||
#if !MOBILEAPP
|
||||
|
@ -126,6 +214,8 @@ void StorageBase::initialize()
|
|||
|
||||
parseWopiHost(app.config());
|
||||
|
||||
parseAliases(app.config());
|
||||
|
||||
#ifdef ENABLE_FEATURE_LOCK
|
||||
CommandControl::LockManager::parseLockedHost(app.config());
|
||||
#endif
|
||||
|
@ -197,7 +287,30 @@ void StorageBase::initialize()
|
|||
|
||||
bool StorageBase::allowedWopiHost(const std::string& host)
|
||||
{
|
||||
return WopiEnabled && WopiHosts.match(host);
|
||||
bool allow = WopiEnabled && WopiHosts.match(host);
|
||||
if (!allow)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (AliasHosts.empty())
|
||||
{
|
||||
if (FirstHost.empty())
|
||||
{
|
||||
FirstHost = host;
|
||||
}
|
||||
else if (FirstHost != host)
|
||||
{
|
||||
LOG_ERR("Only allowed host is: " << FirstHost
|
||||
<< ", no aliases groups are defined in configuration");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (AllHosts.find(host) == AllHosts.end())
|
||||
{
|
||||
LOG_ERR("Host: " << host << " is not allowed, It is not part of aliases group");
|
||||
return false;
|
||||
}
|
||||
return allow;
|
||||
}
|
||||
|
||||
#if !MOBILEAPP
|
||||
|
@ -272,7 +385,7 @@ std::unique_ptr<StorageBase> StorageBase::create(const Poco::URI& uri, const std
|
|||
LOG_INF("Public URI [" << COOLWSD::anonymizeUrl(uri.toString()) << "] considered WOPI.");
|
||||
const auto& targetHost = uri.getHost();
|
||||
bool allowed(false);
|
||||
if (WopiHosts.match(targetHost) || isLocalhost(targetHost))
|
||||
if (StorageBase::allowedWopiHost(targetHost) || isLocalhost(targetHost))
|
||||
{
|
||||
allowed = true;
|
||||
}
|
||||
|
@ -282,7 +395,7 @@ std::unique_ptr<StorageBase> StorageBase::create(const Poco::URI& uri, const std
|
|||
const auto hostAddresses(Poco::Net::DNS::resolve(targetHost));
|
||||
for (auto &address : hostAddresses.addresses())
|
||||
{
|
||||
if (WopiHosts.match(address.toString()))
|
||||
if (StorageBase::allowedWopiHost(address.toString()))
|
||||
{
|
||||
allowed = true;
|
||||
break;
|
||||
|
@ -1545,4 +1658,5 @@ WopiStorage::handleUploadToStorageResponse(const WopiUploadDetails& details,
|
|||
|
||||
#endif // !MOBILEAPP
|
||||
|
||||
#endif // !defined(BUILDING_TESTS)
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
||||
|
|
|
@ -330,6 +330,16 @@ public:
|
|||
|
||||
static void parseWopiHost(Poco::Util::LayeredConfiguration& conf);
|
||||
|
||||
static void parseAliases(Poco::Util::LayeredConfiguration& conf);
|
||||
|
||||
/// if request uri is an alias, replace request uri host and port with
|
||||
/// original hostname and port defined by group tag from coolwsd.xml
|
||||
/// to avoid possibility of opening the same file as two if the WOPI host
|
||||
/// is accessed using different aliases
|
||||
static std::string getNewUri(const Poco::URI& uri);
|
||||
|
||||
static void addWopiHost(std::string host, bool allow);
|
||||
|
||||
protected:
|
||||
|
||||
/// Sanitize a URI by removing authorization tokens.
|
||||
|
@ -390,6 +400,12 @@ private:
|
|||
static bool SSLEnabled;
|
||||
/// Allowed/denied WOPI hosts, if any and if WOPI is enabled.
|
||||
static Util::RegexListMatcher WopiHosts;
|
||||
/// mapping of alias host and port to real host and port
|
||||
static std::map<std::string, std::string> AliasHosts;
|
||||
/// When group configuration is not defined only the firstHost gets access
|
||||
static std::string FirstHost;
|
||||
/// This contains all real and aliases host from group configuration
|
||||
static std::set<std::string> AllHosts;
|
||||
};
|
||||
|
||||
/// Trivial implementation of local storage that does not need do anything.
|
||||
|
|
Loading…
Reference in New Issue