Skip to content

Commit

Permalink
Add Wopi Host check support
Browse files Browse the repository at this point in the history
Signed-off-by: Méven Car <[email protected]>
Change-Id: I8f61cc0776f437591cd37d1e85c87a8e89718c85
  • Loading branch information
meven committed Dec 16, 2024
1 parent 8a04dbb commit e616a8b
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 68 deletions.
9 changes: 1 addition & 8 deletions net/HttpRequest.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1665,14 +1665,10 @@ class Session final : public ProtocolHandlerInterface

void onDisconnect() override
{

bool completed = false;
// Make sure the socket is disconnected and released.
std::shared_ptr<StreamSocket> socket = _socket.lock();
if (socket)
{
completed = socket->getOutBuffer().empty() && socket->getInBuffer().empty();

LOG_TRC("onDisconnect");
socket->shutdown(); // Flag for shutdown for housekeeping in SocketPoll.
socket->closeConnection(); // Immediately disconnect.
Expand All @@ -1681,10 +1677,7 @@ class Session final : public ProtocolHandlerInterface

_connected = false;
if (_response) {
if (completed)
_response->complete();
else
_response->finish();
_response->finish();
}

_fd = -1; // No longer our socket fd.
Expand Down
4 changes: 2 additions & 2 deletions net/NetUtil.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ bool parseUri(std::string uri, std::string& scheme, std::string& host, std::stri
/// Returns true if parsing was successful.
inline bool parseUri(std::string uri, std::string& scheme, std::string& host, std::string& port)
{
std::string path;
return parseUri(std::move(uri), scheme, host, port, path);
std::string pathAndQuery;
return parseUri(std::move(uri), scheme, host, port, pathAndQuery);
}

/// Return the locator given a URI.
Expand Down
2 changes: 1 addition & 1 deletion net/Socket.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1572,7 +1572,7 @@ class StreamSocket : public Socket,

if (closed)
{
LOG_TRC("Closed. Firing onDisconnect.");
LOG_TRC("Closed. Firing onDisconnect." << "hanging up? " << (events & POLLHUP) );
_socketHandler->onDisconnect();
setClosed(disposition);
}
Expand Down
44 changes: 22 additions & 22 deletions test/WhiteBoxTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1099,72 +1099,72 @@ void WhiteBoxTests::testParseUriUrl()
std::string scheme = "***";
std::string host = "***";
std::string port = "***";
std::string path = "***";
std::string pathAndQuery = "***";

LOK_ASSERT(!net::parseUri(std::string(), scheme, host, port, path));
LOK_ASSERT(!net::parseUri(std::string(), scheme, host, port, pathAndQuery));
LOK_ASSERT(scheme.empty());
LOK_ASSERT(host.empty());
LOK_ASSERT(port.empty());
LOK_ASSERT(path.empty());
LOK_ASSERT(pathAndQuery.empty());

LOK_ASSERT(net::parseUri("localhost", scheme, host, port, path));
LOK_ASSERT(net::parseUri("localhost", scheme, host, port, pathAndQuery));
LOK_ASSERT(scheme.empty());
LOK_ASSERT_EQUAL(std::string("localhost"), host);
LOK_ASSERT(port.empty());
LOK_ASSERT(path.empty());
LOK_ASSERT(pathAndQuery.empty());

LOK_ASSERT(net::parseUri("127.0.0.1", scheme, host, port, path));
LOK_ASSERT(net::parseUri("127.0.0.1", scheme, host, port, pathAndQuery));
LOK_ASSERT(scheme.empty());
LOK_ASSERT_EQUAL(std::string("127.0.0.1"), host);
LOK_ASSERT(port.empty());
LOK_ASSERT(path.empty());
LOK_ASSERT(pathAndQuery.empty());

LOK_ASSERT(net::parseUri("domain.com", scheme, host, port, path));
LOK_ASSERT(net::parseUri("domain.com", scheme, host, port, pathAndQuery));
LOK_ASSERT(scheme.empty());
LOK_ASSERT_EQUAL(std::string("domain.com"), host);
LOK_ASSERT(port.empty());
LOK_ASSERT(path.empty());
LOK_ASSERT(pathAndQuery.empty());

LOK_ASSERT(net::parseUri("127.0.0.1:9999", scheme, host, port, path));
LOK_ASSERT(net::parseUri("127.0.0.1:9999", scheme, host, port, pathAndQuery));
LOK_ASSERT(scheme.empty());
LOK_ASSERT_EQUAL(std::string("127.0.0.1"), host);
LOK_ASSERT_EQUAL(std::string("9999"), port);
LOK_ASSERT(path.empty());
LOK_ASSERT(pathAndQuery.empty());

LOK_ASSERT(net::parseUri("domain.com:88", scheme, host, port, path));
LOK_ASSERT(net::parseUri("domain.com:88", scheme, host, port, pathAndQuery));
LOK_ASSERT(scheme.empty());
LOK_ASSERT_EQUAL(std::string("domain.com"), host);
LOK_ASSERT_EQUAL(std::string("88"), port);
LOK_ASSERT(path.empty());
LOK_ASSERT(pathAndQuery.empty());

LOK_ASSERT(net::parseUri("http://domain.com", scheme, host, port, path));
LOK_ASSERT(net::parseUri("http://domain.com", scheme, host, port, pathAndQuery));
LOK_ASSERT_EQUAL(std::string("http://"), scheme);
LOK_ASSERT_EQUAL(std::string("domain.com"), host);
LOK_ASSERT(port.empty());
LOK_ASSERT(path.empty());
LOK_ASSERT(pathAndQuery.empty());

LOK_ASSERT(net::parseUri("https://domain.com:88", scheme, host, port, path));
LOK_ASSERT(net::parseUri("https://domain.com:88", scheme, host, port, pathAndQuery));
LOK_ASSERT_EQUAL(std::string("https://"), scheme);
LOK_ASSERT_EQUAL(std::string("domain.com"), host);
LOK_ASSERT_EQUAL(std::string("88"), port);

LOK_ASSERT(net::parseUri("http://domain.com/path/to/file", scheme, host, port, path));
LOK_ASSERT(net::parseUri("http://domain.com/path/to/file", scheme, host, port, pathAndQuery));
LOK_ASSERT_EQUAL(std::string("http://"), scheme);
LOK_ASSERT_EQUAL(std::string("domain.com"), host);
LOK_ASSERT(port.empty());
LOK_ASSERT_EQUAL(std::string("/path/to/file"), path);
LOK_ASSERT_EQUAL(std::string("/path/to/file"), pathAndQuery);

LOK_ASSERT(net::parseUri("https://domain.com:88/path/to/file", scheme, host, port, path));
LOK_ASSERT(net::parseUri("https://domain.com:88/path/to/file", scheme, host, port, pathAndQuery));
LOK_ASSERT_EQUAL(std::string("https://"), scheme);
LOK_ASSERT_EQUAL(std::string("domain.com"), host);
LOK_ASSERT_EQUAL(std::string("88"), port);
LOK_ASSERT_EQUAL(std::string("/path/to/file"), path);
LOK_ASSERT_EQUAL(std::string("/path/to/file"), pathAndQuery);

LOK_ASSERT(net::parseUri("wss://127.0.0.1:9999/", scheme, host, port, path));
LOK_ASSERT(net::parseUri("wss://127.0.0.1:9999/", scheme, host, port, pathAndQuery));
LOK_ASSERT_EQUAL(std::string("wss://"), scheme);
LOK_ASSERT_EQUAL(std::string("127.0.0.1"), host);
LOK_ASSERT_EQUAL(std::string("9999"), port);
LOK_ASSERT_EQUAL(std::string("/"), path);
LOK_ASSERT_EQUAL(std::string("/"), pathAndQuery);
}

void WhiteBoxTests::testParseUrl()
Expand Down
77 changes: 42 additions & 35 deletions wsd/ClientRequestDispatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1103,11 +1103,11 @@ STATE_ENUM(CheckStatus,
Ok,
NotHttpSucess,
HostNotFound,
WopiHostNotAllowed,
HostUnReachable,
UnspecifiedError,
ConnectionAborted,
ConnectionRefused,
InvalidCertificate,
CertificateValidation,
SSLHandshakeFail,
MissingSsl,
Expand All @@ -1134,7 +1134,7 @@ bool ClientRequestDispatcher::handleWopiAccessCheckRequest(const Poco::Net::HTTP
Poco::URI uri;

std::string text(std::istreambuf_iterator<char>(message), {});
LOG_DBG("Wopi Access Check request text: " << text);
LOG_TRC("Wopi Access Check request text: " << text);

std::string callbackUrlStr;
try
Expand All @@ -1144,7 +1144,7 @@ bool ClientRequestDispatcher::handleWopiAccessCheckRequest(const Poco::Net::HTTP
}
catch (const std::exception& exception)
{
LOG_ERR_S("Wopi Access Check request error, json object expected got ["
LOG_WRN_S("Wopi Access Check request error, json object expected got ["
<< text << "] on request to URL: " << request.getURI() << exception.what());

HttpHelper::sendErrorAndShutdown(http::StatusCode::BadRequest, socket);
Expand All @@ -1157,17 +1157,20 @@ bool ClientRequestDispatcher::handleWopiAccessCheckRequest(const Poco::Net::HTTP
}
catch (const std::exception& exception)
{
LOG_ERR_S("Wopi Access Check request error, missing key callbackUrl expected got ["
LOG_WRN_S("Wopi Access Check request error, missing key callbackUrl expected got ["
<< text << "] on request to URL: " << request.getURI() << exception.what());

HttpHelper::sendErrorAndShutdown(http::StatusCode::BadRequest, socket);
return false;
}

LOG_DBG("Wopi Access Check request callbackUrlStr: " << callbackUrlStr);
LOG_TRC("Wopi Access Check request callbackUrlStr: " << callbackUrlStr);

std::string scheme, host, portStr, pathAndQuery;
if (!net::parseUri(callbackUrlStr, scheme, host, portStr, pathAndQuery)) {
LOG_WRN_S("Wopi Access Check request error, invalid url ["
<< callbackUrlStr << "] on request to URL: " << request.getURI() << scheme);

HttpHelper::sendErrorAndShutdown(http::StatusCode::BadRequest, socket);
return false;
}
Expand All @@ -1179,7 +1182,7 @@ bool ClientRequestDispatcher::handleWopiAccessCheckRequest(const Poco::Net::HTTP
protocol = http::Session::Protocol::HttpUnencrypted;
port = 80;
} else {
LOG_ERR_S("Wopi Access Check request error, bad request protocol ["
LOG_WRN_S("Wopi Access Check request error, bad request protocol ["
<< text << "] on request to URL: " << request.getURI() << scheme);

HttpHelper::sendErrorAndShutdown(http::StatusCode::BadRequest, socket);
Expand All @@ -1188,36 +1191,32 @@ bool ClientRequestDispatcher::handleWopiAccessCheckRequest(const Poco::Net::HTTP

if (!portStr.empty()) {
try {
LOG_DBG("Wopi Access Check parsing portStr:" << portStr);
port = std::stoul(portStr);

} catch(std::invalid_argument &exception) {
LOG_DBG("Wopi Access Check error parsing invalid_argument portStr:" << portStr);
LOG_WRN("Wopi Access Check error parsing invalid_argument portStr:" << portStr);
HttpHelper::sendErrorAndShutdown(http::StatusCode::BadRequest, socket);
return false;
} catch(std::exception &exception) {

LOG_DBG("Wopi Access Check error parsing portStr:" << portStr);

LOG_ERR_S("Wopi Access Check request error, bad request stoul ["
LOG_WRN_S("Wopi Access Check request error, bad request invalid porl ["
<< text << "] on request to URL: " << request.getURI());

HttpHelper::sendErrorAndShutdown(http::StatusCode::BadRequest, socket);
return false;
}
}

LOG_DBG("Wopi Access Check request scheme: " << scheme << port << portStr);
LOG_TRC("Wopi Access Check request scheme: " << scheme << " " << port);

auto sendResult = [this, socket](CheckStatus result)
{
Poco::JSON::Object::Ptr status = new Poco::JSON::Object;
status->set("status", (int)result);
status->set("details", name(result));
status->set("status", name(result));

std::ostringstream ostrJSON;
status->stringify(ostrJSON);
const auto output = ostrJSON.str();
const auto output = ostrJSON.str() + "\n";

http::Response jsonResponse(http::StatusCode::OK);
FileServerRequestHandler::hstsHeaders(jsonResponse);
Expand All @@ -1226,7 +1225,7 @@ bool ClientRequestDispatcher::handleWopiAccessCheckRequest(const Poco::Net::HTTP
jsonResponse.set("X-Content-Type-Options", "nosniff");

socket->sendAndShutdown(jsonResponse);
LOG_INF("Sent wopiAccessCheck.json successfully.");
LOG_INF("Wopi Access Check request, result" << name(result));
};

if (scheme.empty())
Expand All @@ -1240,28 +1239,39 @@ bool ClientRequestDispatcher::handleWopiAccessCheckRequest(const Poco::Net::HTTP
return true;
}

LOG_DBG("Wopi Access Check about to prepare http session");
if (HostUtil::isWopiHostsEmpty())
// make sure the wopi hosts settings are loaded
StorageBase::initialize();

bool wopiHostAllowed = false;
if (Util::iequal(ConfigUtil::getString("storage.wopi.alias_groups[@mode]", "first"), "first"))
// if first mode was selected and wopi Hosts are empty
// the domain is allowed, as it will be the "first"
wopiHostAllowed = HostUtil::isWopiHostsEmpty();

if (!wopiHostAllowed) {
// port and scheme from wopi host config are currently ignored
LOG_TRC("Wopi Access Check, matching allowed wopi host for host " << host);
wopiHostAllowed = HostUtil::allowedWopiHost(host);
}
if (!wopiHostAllowed)
{
LOG_TRC("Wopi Access Check, wopi host not allowed " << host);
sendResult(CheckStatus::WopiHostNotAllowed);
return true;
}

http::Request httpRequest(pathAndQuery.empty() ? "/" : pathAndQuery);
auto httpProbeSession = http::Session::create(host, protocol, port);
httpProbeSession->setTimeout(std::chrono::seconds(2));

LOG_DBG("Wopi Access Check preparing http session");

// first case? !WopiEnabled
// HostUtil::allowedWopiHost(host);

httpProbeSession->setConnectFailHandler(
[=, this] (const std::shared_ptr<http::Session>& probeSession){

CheckStatus status = CheckStatus::UnspecifiedError;

const auto result = probeSession->connectionResult();

LOG_TRC("ConnectFailHandler " << (int)result);

// const auto lastErrno = errno;

if (result == net::asyncConnectResult::UnknownHostError || result == net::asyncConnectResult::HostNameError)
{
status = CheckStatus::HostNotFound;
Expand Down Expand Up @@ -1303,12 +1313,6 @@ bool ClientRequestDispatcher::handleWopiAccessCheckRequest(const Poco::Net::HTTP
if (responseState == http::Response::State::Timeout)
status = CheckStatus::Timeout;

// TODO complete error coverage
// certificate errors
// self-signed
// expired
// SSL expected not here
// ssl expected but present

const auto result = probeSession->connectionResult();

Expand All @@ -1321,6 +1325,11 @@ bool ClientRequestDispatcher::handleWopiAccessCheckRequest(const Poco::Net::HTTP
if (result == net::asyncConnectResult::ConnectionError)
status = CheckStatus::ConnectionAborted;

// TODO complete error coverage
// certificate errors
// self-signed
// expired

if (!probeSession->getSslVerifyMessage().empty())
{
status = CheckStatus::CertificateValidation;
Expand All @@ -1332,10 +1341,8 @@ bool ClientRequestDispatcher::handleWopiAccessCheckRequest(const Poco::Net::HTTP
};

httpProbeSession->setFinishedHandler(std::move(finishHandler));

LOG_DBG("Wopi Access Check requesting: " << httpRequest.getUrl());

httpProbeSession->asyncRequest(httpRequest, *COOLWSD::getWebServerPoll());

return true;
}

Expand Down
5 changes: 5 additions & 0 deletions wsd/HostUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,11 @@ void HostUtil::setFirstHost(const Poco::URI& uri)
}
}

bool HostUtil::isWopiHostsEmpty()
{
return WopiHosts.empty();
}

#endif // !MOBILEAPP

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2 changes: 2 additions & 0 deletions wsd/HostUtil.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ class HostUtil

static void setFirstHost(const Poco::URI& uri);

static bool isWopiHostsEmpty();

private:
/// add host to WopiHosts
static void addWopiHost(const std::string& host, bool allow);
Expand Down

0 comments on commit e616a8b

Please sign in to comment.