From 9327272690aea4f3ac33554ea112035e9938a2d9 Mon Sep 17 00:00:00 2001 From: Rash419 Date: Thu, 24 Mar 2022 15:53:34 +0530 Subject: wsd: cleanup: moved all parsing related static methods and containers to HostUtil.cpp Signed-off-by: Rash419 Change-Id: I8555522c6216f893b90ba4c27747314830f7abd2 --- Makefile.am | 4 +- common/Util.cpp | 2 +- test/Makefile.am | 2 +- wsd/COOLWSD.cpp | 11 +-- wsd/HostUtil.cpp | 186 ++++++++++++++++++++++++++++++++++++++++++++++ wsd/HostUtil.hpp | 52 +++++++++++++ wsd/RequestDetails.cpp | 6 +- wsd/Storage.cpp | 196 +++---------------------------------------------- wsd/Storage.hpp | 24 ------ 9 files changed, 260 insertions(+), 223 deletions(-) create mode 100644 wsd/HostUtil.cpp create mode 100644 wsd/HostUtil.hpp diff --git a/Makefile.am b/Makefile.am index e2e8ec3533..d7e67ad048 100644 --- a/Makefile.am +++ b/Makefile.am @@ -130,6 +130,7 @@ coolwsd_sources = common/Crypto.cpp \ wsd/FileServerUtil.cpp \ wsd/RequestDetails.cpp \ wsd/Storage.cpp \ + wsd/HostUtil.cpp \ wsd/TileCache.cpp \ wsd/ProofKey.cpp \ wsd/QuarantineUtil.cpp @@ -263,7 +264,8 @@ wsd_headers = wsd/Admin.hpp \ wsd/TileDesc.hpp \ wsd/TraceFile.hpp \ wsd/UserMessages.hpp \ - wsd/QuarantineUtil.hpp + wsd/QuarantineUtil.hpp \ + wsd/HostUtil.hpp shared_headers = common/Common.hpp \ common/Clipboard.hpp \ diff --git a/common/Util.cpp b/common/Util.cpp index 6a51770067..c4140d9495 100644 --- a/common/Util.cpp +++ b/common/Util.cpp @@ -1138,7 +1138,7 @@ namespace Util } template - static bool matchRegex(const Type& set, const std::string& subject, Getter& getter) + static bool matchRegex(const Type& set, const std::string& subject, const Getter& getter) { if (set.find(subject) != set.end()) { diff --git a/test/Makefile.am b/test/Makefile.am index 47571dd68c..88adec42ce 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -102,7 +102,7 @@ wsd_sources = \ ../common/Authorization.cpp \ ../kit/Kit.cpp \ ../kit/TestStubs.cpp \ - ../wsd/Storage.cpp \ + ../wsd/HostUtil.cpp \ ../wsd/FileServerUtil.cpp \ ../wsd/RequestDetails.cpp \ ../wsd/TileCache.cpp \ diff --git a/wsd/COOLWSD.cpp b/wsd/COOLWSD.cpp index 0c1d29bec5..968638b6c9 100644 --- a/wsd/COOLWSD.cpp +++ b/wsd/COOLWSD.cpp @@ -10,6 +10,7 @@ #include "COOLWSD.hpp" #include "ProofKey.hpp" #include "CommandControl.hpp" +#include "HostUtil.hpp" /* Default host used in the start test URI */ #define COOLWSD_TEST_HOST "localhost" @@ -1149,13 +1150,13 @@ public: AutoPtr newConfig(new AppConfigMap(newAppConfig)); conf.addWriteable(newConfig, PRIO_JSON); - StorageBase::parseWopiHost(conf); - - StorageBase::parseAliases(conf); + HostUtil::parseWopiHost(conf); #ifdef ENABLE_FEATURE_LOCK CommandControl::LockManager::parseLockedHost(conf); #endif + + HostUtil::parseAliases(conf); } void fetchWopiHostPatterns(std::map& newAppConfig, @@ -2982,7 +2983,7 @@ public: { std::string addressToCheck = address; std::string hostToCheck = request.getHost(); - bool allow = allowPostFrom(addressToCheck) || StorageBase::allowedWopiHost(hostToCheck); + bool allow = allowPostFrom(addressToCheck) || HostUtil::allowedWopiHost(hostToCheck); if(!allow) { @@ -3008,7 +3009,7 @@ public: if (!allowPostFrom(addressToCheck)) { hostToCheck = Poco::Net::DNS::resolve(addressToCheck).name(); - allow &= StorageBase::allowedWopiHost(hostToCheck); + allow &= HostUtil::allowedWopiHost(hostToCheck); } } catch (const Poco::Exception& exc) diff --git a/wsd/HostUtil.cpp b/wsd/HostUtil.cpp new file mode 100644 index 0000000000..dacdc3e38d --- /dev/null +++ b/wsd/HostUtil.cpp @@ -0,0 +1,186 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "HostUtil.hpp" +#include +#include + +Util::RegexListMatcher HostUtil::WopiHosts; +std::map HostUtil::AliasHosts; +std::set HostUtil::AllHosts; +std::string HostUtil::FirstHost; +bool HostUtil::WopiEnabled; + +void HostUtil::parseWopiHost(Poco::Util::LayeredConfiguration& conf) +{ + // Parse the WOPI settings. + WopiHosts.clear(); + WopiEnabled = conf.getBool("storage.wopi[@allow]", false); + if (WopiEnabled) + { + for (size_t i = 0;; ++i) + { + const std::string path = "storage.wopi.host[" + std::to_string(i) + ']'; + if (!conf.has(path)) + { + break; + } + HostUtil::addWopiHost(conf.getString(path, ""), + conf.getBool(path + "[@allow]", false)); + } + } +} + +void HostUtil::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); + } + } +} + +bool HostUtil::allowedWopiHost(const std::string& host) +{ + return WopiEnabled && WopiHosts.match(host); +} + +bool HostUtil::allowedAlias(const Poco::URI& uri) +{ + if (Util::iequal(config::getString("storage.wopi.alias_groups[@mode]", "first"), "compat")) + { + return true; + } + + if (AllHosts.empty()) + { + if (FirstHost.empty()) + { + FirstHost = uri.getAuthority(); + } + else if (FirstHost != uri.getAuthority()) + { + LOG_ERR("Only allowed host is: " << FirstHost); + return false; + } + } + else if (!Util::matchRegex(AllHosts, uri.getAuthority())) + { + LOG_ERR("Host: " << uri.getAuthority() + << " is not allowed, It is not part of alias_groups configuration"); + return false; + } + return true; +} + +void HostUtil::parseAliases(Poco::Util::LayeredConfiguration& conf) +{ + //set alias_groups mode to compat + if (!conf.has("storage.wopi.alias_groups")) + { + conf.setString("storage.wopi.alias_groups[@mode]", "compat"); + } + else if (conf.has("storage.wopi.alias_groups.group[0]")) + { + // group defined in alias_groups + if (Util::iequal(config::getString("storage.wopi.alias_groups[@mode]", "first"), "first")) + { + LOG_ERR("Admins didnot set the alias_groups mode to 'groups'"); + AliasHosts.clear(); + AllHosts.clear(); + return; + } + } + + AliasHosts.clear(); + AllHosts.clear(); + + for (size_t i = 0;; i++) + { + const std::string path = "storage.wopi.alias_groups.group[" + std::to_string(i) + ']'; + if (!conf.has(path + ".host")) + { + break; + } + + const std::string uri = conf.getString(path + ".host", ""); + if (uri.empty()) + { + continue; + } + bool allow = conf.getBool(path + ".host[@allow]", false); + + try + { + const Poco::URI realUri(uri); + HostUtil::addWopiHost(realUri.getHost(), allow); + + AllHosts.insert(realUri.getAuthority()); + } + catch (const Poco::Exception& exc) + { + LOG_WRN("parseAliases: " << exc.displayText()); + } + + for (size_t j = 0;; j++) + { + const std::string aliasPath = path + ".alias[" + std::to_string(j) + ']'; + if (!conf.has(aliasPath)) + { + break; + } + + try + { + const Poco::URI aliasUri(conf.getString(aliasPath, "")); + if (aliasUri.empty()) + { + continue; + } + const Poco::URI realUri(uri); + AliasHosts.insert({ aliasUri.getAuthority(), realUri.getAuthority() }); + AllHosts.insert(aliasUri.getAuthority()); + HostUtil::addWopiHost(aliasUri.getHost(), allow); + } + catch (const Poco::Exception& exc) + { + LOG_WRN("parseAliases: " << exc.displayText()); + } + } + } +} + +std::string HostUtil::getNewUri(const Poco::URI& uri) +{ + if (Util::iequal(config::getString("storage.wopi.alias_groups[@mode]", "first"), "compat")) + { + return uri.getPath(); + } + Poco::URI newUri(uri); + const std::string key = newUri.getAuthority(); + if (Util::matchRegex(AliasHosts, key)) + { + newUri.setAuthority(AliasHosts[key]); + } + + if (newUri.getAuthority().empty()) + { + return newUri.getPath(); + } + return newUri.getScheme() + "://" + newUri.getHost() + ':' + std::to_string(newUri.getPort()) + + newUri.getPath(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/wsd/HostUtil.hpp b/wsd/HostUtil.hpp new file mode 100644 index 0000000000..0071a6a397 --- /dev/null +++ b/wsd/HostUtil.hpp @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include +#include +#include + +/// This class contains static methods to parse alias_groups and WOPI host and static containers to store the data from the coolwsd.xml +class HostUtil +{ +private: + /// 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 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 AllHosts; + + static bool WopiEnabled; + +public: + /// parse wopi.storage.host + static void parseWopiHost(Poco::Util::LayeredConfiguration& conf); + + /// parse wopi.storage.alias_groups.group + 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); + + /// add host to WopiHosts + static void addWopiHost(std::string host, bool allow); + + static bool allowedAlias(const Poco::URI& uri); + + static bool allowedWopiHost(const std::string& host); + + static bool isWopiEnabled() { return WopiEnabled; } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/wsd/RequestDetails.cpp b/wsd/RequestDetails.cpp index 2035b8f143..3dc1a027dd 100644 --- a/wsd/RequestDetails.cpp +++ b/wsd/RequestDetails.cpp @@ -10,7 +10,7 @@ #include "COOLWSD.hpp" #include "RequestDetails.hpp" #include "common/Log.hpp" -#include "Storage.hpp" +#include "HostUtil.hpp" #include #include "Exceptions.hpp" @@ -282,7 +282,6 @@ Poco::URI RequestDetails::sanitizeURI(const std::string& uri) return uriPublic; } -#if !defined(BUILDING_TESTS) std::string RequestDetails::getDocKey(const Poco::URI& uri) { std::string docKey; @@ -290,13 +289,12 @@ std::string RequestDetails::getDocKey(const Poco::URI& uri) // resolve aliases #if !MOBILEAPP - newUri = StorageBase::getNewUri(uri); + newUri = HostUtil::getNewUri(uri); #endif Poco::URI::encode(newUri, "", docKey); LOG_INF("DocKey from URI [" << uri.toString() << "] => [" << docKey << ']'); return docKey; } -#endif // !defined(BUILDING_TESTS) /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/wsd/Storage.cpp b/wsd/Storage.cpp index 25cf573b0b..5d484d3683 100644 --- a/wsd/Storage.cpp +++ b/wsd/Storage.cpp @@ -53,6 +53,7 @@ #include #include #include +#include "HostUtil.hpp" #ifdef IOS #include @@ -60,16 +61,9 @@ #include "androidapp.hpp" #endif -#if !defined(BUILDING_TESTS) bool StorageBase::FilesystemEnabled; -bool StorageBase::WopiEnabled; bool StorageBase::SSLAsScheme = true; bool StorageBase::SSLEnabled = false; -Util::RegexListMatcher StorageBase::WopiHosts; -std::map StorageBase::AliasHosts; -std::set StorageBase::AllHosts; -std::string StorageBase::FirstHost; -#endif // !defined(BUILDING_TESTS) #if !MOBILEAPP @@ -88,160 +82,22 @@ std::string StorageBase::getLocalRootPath() const return rootPath.toString(); } - -#if !defined(BUILDING_TESTS) -void StorageBase::parseWopiHost(Poco::Util::LayeredConfiguration& conf) -{ - // Parse the WOPI settings. - WopiHosts.clear(); - WopiEnabled = conf.getBool("storage.wopi[@allow]", false); - if (WopiEnabled) - { - for (size_t i = 0;; ++i) - { - const std::string path = "storage.wopi.host[" + std::to_string(i) + ']'; - 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) -{ - //set alias_groups mode to compat - if (!conf.has("storage.wopi.alias_groups")) - { - conf.setString("storage.wopi.alias_groups[@mode]", "compat"); - } - else if (conf.has("storage.wopi.alias_groups.group[0]")) - { - // group defined in alias_groups - if (Util::iequal(config::getString("storage.wopi.alias_groups[@mode]", "first"), "first")) - { - LOG_ERR("Admins didnot set the alias_groups mode to 'groups'"); - AliasHosts.clear(); - AllHosts.clear(); - return; - } - } - - AliasHosts.clear(); - AllHosts.clear(); - - for (size_t i = 0;; i++) - { - const std::string path = "storage.wopi.alias_groups.group[" + std::to_string(i) + ']'; - if (!conf.has(path + ".host")) - { - break; - } - - const std::string uri = conf.getString(path + ".host", ""); - if (uri.empty()) - { - continue; - } - bool allow = conf.getBool(path + ".host[@allow]", false); - - try - { - const Poco::URI realUri(uri); - StorageBase::addWopiHost(realUri.getHost(), allow); - AllHosts.insert(realUri.getAuthority()); - } - catch (const Poco::Exception& exc) - { - LOG_WRN("parseAliases: " << exc.displayText()); - } - - for (size_t j = 0;; j++) - { - const std::string aliasPath = path + ".alias[" + std::to_string(j) + ']'; - if (!conf.has(aliasPath)) - { - break; - } - - try - { - const Poco::URI aliasUri(conf.getString(aliasPath, "")); - if (aliasUri.empty()) - { - continue; - } - const Poco::URI realUri(uri); - AliasHosts.insert({ aliasUri.getAuthority(), realUri.getAuthority() }); - AllHosts.insert(aliasUri.getAuthority()); - StorageBase::addWopiHost(aliasUri.getHost(), allow); - } - catch (const Poco::Exception& exc) - { - LOG_WRN("parseAliases: " << exc.displayText()); - } - } - } -} - -std::string StorageBase::getNewUri(const Poco::URI& uri) -{ - if (Util::iequal(config::getString("storage.wopi.alias_groups[@mode]", "first"), "compat")) - { - return uri.getPath(); - } - Poco::URI newUri(uri); - const std::string key = newUri.getAuthority(); - if (Util::matchRegex(AliasHosts, key)) - { - newUri.setAuthority(AliasHosts[key]); - } - - if (newUri.getAuthority().empty()) - { - return newUri.getPath(); - } - return newUri.getScheme() + "://" + newUri.getHost() + ':' + std::to_string(newUri.getPort()) + - newUri.getPath(); -} -#endif // !defined(BUILDING_TESTS) - #endif -#if !defined(BUILDING_TESTS) - void StorageBase::initialize() { #if !MOBILEAPP const auto& app = Poco::Util::Application::instance(); FilesystemEnabled = app.config().getBool("storage.filesystem[@allow]", false); - parseWopiHost(app.config()); - - parseAliases(app.config()); + HostUtil::parseWopiHost(app.config()); #ifdef ENABLE_FEATURE_LOCK CommandControl::LockManager::parseLockedHost(app.config()); #endif + HostUtil::parseAliases(app.config()); + #if ENABLE_SSL // FIXME: should use our own SSL socket implementation here. Poco::Crypto::initializeCrypto(); @@ -307,39 +163,6 @@ void StorageBase::initialize() #endif } -bool StorageBase::allowedWopiHost(const std::string& host) -{ - return WopiEnabled && WopiHosts.match(host); -} - -bool StorageBase::allowedAlias(const Poco::URI& uri) -{ - if (Util::iequal(config::getString("storage.wopi.alias_groups[@mode]", "first"), "compat")) - { - return true; - } - - if (AllHosts.empty()) - { - if (FirstHost.empty()) - { - FirstHost = uri.getAuthority(); - } - else if (FirstHost != uri.getAuthority()) - { - LOG_ERR("Only allowed host is: " << FirstHost); - return false; - } - } - else if (!Util::matchRegex(AllHosts, uri.getAuthority())) - { - LOG_ERR("Host: " << uri.getAuthority() - << " is not allowed, It is not part of alias_groups configuration"); - return false; - } - return true; -} - #if !MOBILEAPP bool isLocalhost(const std::string& targetHost) @@ -407,12 +230,12 @@ std::unique_ptr StorageBase::create(const Poco::URI& uri, const std LOG_ERR("Local Storage is disabled by default. Enable in the config file or on the command-line to enable."); } #if !MOBILEAPP - else if (WopiEnabled) + else if (HostUtil::isWopiEnabled()) { LOG_INF("Public URI [" << COOLWSD::anonymizeUrl(uri.toString()) << "] considered WOPI."); const auto& targetHost = uri.getHost(); bool allowed(false); - if ((StorageBase::allowedWopiHost(targetHost) && StorageBase::allowedAlias(uri)) || + if ((HostUtil::allowedWopiHost(targetHost) && HostUtil::allowedAlias(uri)) || isLocalhost(targetHost)) { allowed = true; @@ -423,8 +246,8 @@ std::unique_ptr StorageBase::create(const Poco::URI& uri, const std const auto hostAddresses(Poco::Net::DNS::resolve(targetHost)); for (auto &address : hostAddresses.addresses()) { - if (StorageBase::allowedWopiHost(address.toString()) && - StorageBase::allowedAlias(uri)) + if (HostUtil::allowedWopiHost(address.toString()) && + HostUtil::allowedAlias(uri)) { allowed = true; break; @@ -1051,7 +874,7 @@ WopiStorage::WOPIFileInfo::WOPIFileInfo(const FileInfo &fileInfo, } else { - LOG_INF("Could not find matching locked host so applying fallback settings"); + LOG_INF("Could not find matching locked_host: " << host << ",applying fallback settings"); isReadOnly = config::getBool("feature_lock.locked_hosts.fallback[@read_only]", false); isUserLocked = config::getBool("feature_lock.locked_hosts.fallback[@disabled_commands]", false); @@ -1691,5 +1514,4 @@ WopiStorage::handleUploadToStorageResponse(const WopiUploadDetails& details, #endif // !MOBILEAPP -#endif // !defined(BUILDING_TESTS) /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/wsd/Storage.hpp b/wsd/Storage.hpp index 28456985fe..bfd43e2baf 100644 --- a/wsd/Storage.hpp +++ b/wsd/Storage.hpp @@ -324,24 +324,9 @@ public: static std::unique_ptr create(const Poco::URI& uri, const std::string& jailRoot, const std::string& jailPath, bool takeOwnership); - static bool allowedWopiHost(const std::string& host); static Poco::Net::HTTPClientSession* getHTTPClientSession(const Poco::URI& uri); static std::shared_ptr getHttpSession(const Poco::URI& uri); - 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 bool allowedAlias(const Poco::URI& uri); - - static void addWopiHost(std::string host, bool allow); - protected: /// Sanitize a URI by removing authorization tokens. @@ -395,19 +380,10 @@ private: std::string _extendedData; static bool FilesystemEnabled; - static bool WopiEnabled; /// If true, use only the WOPI URL for whether to use SSL to talk to storage server static bool SSLAsScheme; /// If true, force SSL communication with storage server 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 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 AllHosts; }; /// Trivial implementation of local storage that does not need do anything. -- cgit