wsd: use WOPI host instance ID to support hostname aliases
The docKey creation moved to Storage where we first invoke WOPI (if/when it's a WOPI-hosted doc and WOPI enabled) and see if the user has access to the document at all. If they do, we expect the server to give us a unique ID to use for identifying the host regardless of hostname aliases. If a unique ID is not returned (i.e. empty or missing) we use the hostname and port in its place as fallback. This will break hostname aliases, but it will still work. Change-Id: I407b0087395f9df6ad9cc6e037570487999be4a4 Reviewed-on: https://gerrit.libreoffice.org/37697 Reviewed-by: Ashod Nakashian <ashnakash@gmail.com> Tested-by: Ashod Nakashian <ashnakash@gmail.com>
This commit is contained in:
parent
f94b42d9a8
commit
ec2fd0844f
5 changed files with 98 additions and 21 deletions
|
@ -736,7 +736,6 @@ bool ClientSession::forwardToClient(const std::shared_ptr<Message>& payload)
|
|||
|
||||
std::string ClientSession::getAccessToken() const
|
||||
{
|
||||
std::string accessToken;
|
||||
Poco::URI::QueryParameters queryParams = _uriPublic.getQueryParameters();
|
||||
for (auto& param: queryParams)
|
||||
{
|
||||
|
|
|
@ -102,15 +102,7 @@ Poco::URI DocumentBroker::sanitizeURI(const std::string& uri)
|
|||
|
||||
std::string DocumentBroker::getDocKey(const Poco::URI& uri)
|
||||
{
|
||||
// If multiple host-names are used to access us, then
|
||||
// we force same document (when opened from
|
||||
// alias hosts) to load as separate documents and sharing doesn't
|
||||
// work. Worse, saving overwrites one another.
|
||||
// But we also do not want different WOPI hosts using the same path
|
||||
// for some file getting shared across WOPI hosts
|
||||
std::string docKey;
|
||||
Poco::URI::encode(uri.getHost() + ":" + std::to_string(uri.getPort()) + uri.getPath(), "", docKey);
|
||||
return docKey;
|
||||
return StorageBase::getUniqueDocId(uri);
|
||||
}
|
||||
|
||||
/// The Document Broker Poll - one of these in a thread per document
|
||||
|
@ -409,6 +401,7 @@ bool DocumentBroker::load(const std::shared_ptr<ClientSession>& session, const s
|
|||
LOG_ERR("Failed to create Storage instance for [" << _docKey << "] in " << jailPath.toString());
|
||||
return false;
|
||||
}
|
||||
|
||||
firstInstance = true;
|
||||
}
|
||||
|
||||
|
@ -423,6 +416,14 @@ bool DocumentBroker::load(const std::shared_ptr<ClientSession>& session, const s
|
|||
std::unique_ptr<WopiStorage::WOPIFileInfo> wopifileinfo = wopiStorage->getWOPIFileInfo(session->getAccessToken());
|
||||
userid = wopifileinfo->_userid;
|
||||
username = wopifileinfo->_username;
|
||||
if (firstInstance)
|
||||
{
|
||||
_hostInstanceId = wopifileinfo->_hostInstanceId;
|
||||
}
|
||||
else if (!_hostInstanceId.empty() && _hostInstanceId != wopifileinfo->_hostInstanceId)
|
||||
{
|
||||
throw UnauthorizedRequestException("Unauthorized WOPI host.");
|
||||
}
|
||||
|
||||
if (!wopifileinfo->_userCanWrite)
|
||||
{
|
||||
|
|
|
@ -369,6 +369,7 @@ private:
|
|||
Poco::URI _uriJailed;
|
||||
std::string _jailId;
|
||||
std::string _filename;
|
||||
std::string _hostInstanceId;
|
||||
|
||||
/// The last time we tried saving, regardless of whether the
|
||||
/// document was modified and saved or not.
|
||||
|
|
|
@ -124,7 +124,7 @@ void StorageBase::initialize()
|
|||
#endif
|
||||
}
|
||||
|
||||
bool isLocalhost(const std::string& targetHost)
|
||||
bool StorageBase::isLocalhost(const std::string& targetHost)
|
||||
{
|
||||
std::string targetAddress;
|
||||
try
|
||||
|
@ -201,7 +201,7 @@ std::unique_ptr<StorageBase> StorageBase::create(const Poco::URI& uri, const std
|
|||
{
|
||||
LOG_INF("Public URI [" << uri.toString() << "] considered WOPI.");
|
||||
const auto& targetHost = uri.getHost();
|
||||
if (WopiHosts.match(targetHost) || isLocalhost(targetHost))
|
||||
if (isWopiHostAuthorized(targetHost))
|
||||
{
|
||||
return std::unique_ptr<StorageBase>(new WopiStorage(uri, jailRoot, jailPath));
|
||||
}
|
||||
|
@ -212,6 +212,39 @@ std::unique_ptr<StorageBase> StorageBase::create(const Poco::URI& uri, const std
|
|||
throw BadRequestException("No Storage configured or invalid URI.");
|
||||
}
|
||||
|
||||
std::string StorageBase::getUniqueDocId(const Poco::URI& uri)
|
||||
{
|
||||
std::string docId;
|
||||
if (uri.isRelative() || uri.getScheme() == "file")
|
||||
{
|
||||
Poco::URI::encode(uri.getPath(), "", docId);
|
||||
}
|
||||
else if (WopiEnabled)
|
||||
{
|
||||
const auto& targetHost = uri.getHost();
|
||||
if (isWopiHostAuthorized(targetHost))
|
||||
{
|
||||
std::string accessToken;
|
||||
Poco::URI::QueryParameters queryParams = uri.getQueryParameters();
|
||||
for (auto& param: queryParams)
|
||||
{
|
||||
if (param.first == "access_token")
|
||||
accessToken = param.second;
|
||||
}
|
||||
|
||||
const std::unique_ptr<WopiStorage::WOPIFileInfo> info = WopiStorage::getWOPIFileInfo(uri, accessToken);
|
||||
const std::string prefix = !info->_hostInstanceId.empty()
|
||||
? info->_hostInstanceId
|
||||
: (uri.getHost() + ':' + std::to_string(uri.getPort()));
|
||||
Poco::URI::encode(prefix + uri.getPath(), "", docId);
|
||||
}
|
||||
else
|
||||
throw UnauthorizedRequestException("No acceptable WOPI hosts found matching the target host [" + targetHost + "] in config.");
|
||||
}
|
||||
|
||||
return docId;
|
||||
}
|
||||
|
||||
std::atomic<unsigned> LocalStorage::LastLocalStorageId;
|
||||
|
||||
std::unique_ptr<LocalStorage::LocalFileInfo> LocalStorage::getLocalFileInfo()
|
||||
|
@ -406,10 +439,9 @@ void setQueryParameter(Poco::URI& uriObject, const std::string& key, const std::
|
|||
|
||||
} // anonymous namespace
|
||||
|
||||
std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const std::string& accessToken)
|
||||
std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(Poco::URI uriObject, const std::string& accessToken)
|
||||
{
|
||||
// update the access_token to the one matching to the session
|
||||
Poco::URI uriObject(_uri);
|
||||
setQueryParameter(uriObject, "access_token", accessToken);
|
||||
|
||||
LOG_DBG("Getting info for wopi uri [" << uriObject.toString() << "].");
|
||||
|
@ -456,6 +488,7 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const st
|
|||
std::string ownerId;
|
||||
std::string userId;
|
||||
std::string userName;
|
||||
std::string hostInstanceId;
|
||||
bool canWrite = false;
|
||||
bool enableOwnerTermination = false;
|
||||
std::string postMessageOrigin;
|
||||
|
@ -490,6 +523,7 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const st
|
|||
getWOPIValue(object, "DisableExport", disableExport);
|
||||
getWOPIValue(object, "DisableCopy", disableCopy);
|
||||
getWOPIValue(object, "LastModifiedTime", lastModifiedTime);
|
||||
getWOPIValue(object, "HostInstanceId", hostInstanceId);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -519,9 +553,7 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const st
|
|||
}
|
||||
}
|
||||
|
||||
_fileInfo = FileInfo({filename, ownerId, modifiedTime, size});
|
||||
|
||||
return std::unique_ptr<WopiStorage::WOPIFileInfo>(new WOPIFileInfo({userId, userName, canWrite, postMessageOrigin, hidePrintOption, hideSaveOption, hideExportOption, enableOwnerTermination, disablePrint, disableExport, disableCopy, callDuration}));
|
||||
return std::unique_ptr<WopiStorage::WOPIFileInfo>(new WOPIFileInfo(filename, ownerId, modifiedTime, size, userId, userName, hostInstanceId, canWrite, postMessageOrigin, hidePrintOption, hideSaveOption, hideExportOption, enableOwnerTermination, disablePrint, disableExport, disableCopy, callDuration));
|
||||
}
|
||||
|
||||
/// uri format: http://server/<...>/wopi*/files/<id>/content
|
||||
|
|
|
@ -100,11 +100,29 @@ public:
|
|||
static std::unique_ptr<StorageBase> create(const Poco::URI& uri,
|
||||
const std::string& jailRoot,
|
||||
const std::string& jailPath);
|
||||
|
||||
/// Given the URI of a doc, return a unique doc ID.
|
||||
/// Wopi host aliases are resolved to unique host ID.
|
||||
static std::string getUniqueDocId(const Poco::URI& uri);
|
||||
|
||||
protected:
|
||||
|
||||
/// Returns the root path of the jail directory of docs.
|
||||
std::string getLocalRootPath() const;
|
||||
|
||||
/// Returns true iff WOPI is enabled, and the host is whitelisted (or local).
|
||||
static bool isWopiHostAuthorized(const std::string& host)
|
||||
{
|
||||
if (WopiEnabled)
|
||||
{
|
||||
return (WopiHosts.match(host) || isLocalhost(host));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isLocalhost(const std::string& host);
|
||||
|
||||
protected:
|
||||
const Poco::URI _uri;
|
||||
std::string _localStorePath;
|
||||
|
@ -176,11 +194,16 @@ public:
|
|||
"], jailPath: [" << jailPath << "], uri: [" << uri.toString() << "].");
|
||||
}
|
||||
|
||||
class WOPIFileInfo
|
||||
class WOPIFileInfo : public FileInfo
|
||||
{
|
||||
public:
|
||||
WOPIFileInfo(const std::string& userid,
|
||||
WOPIFileInfo(const std::string& filename,
|
||||
const std::string& ownerId,
|
||||
const Poco::Timestamp& modifiedTime,
|
||||
size_t size,
|
||||
const std::string& userid,
|
||||
const std::string& username,
|
||||
const std::string& hostInstanceId,
|
||||
const bool userCanWrite,
|
||||
const std::string& postMessageOrigin,
|
||||
const bool hidePrintOption,
|
||||
|
@ -191,8 +214,10 @@ public:
|
|||
const bool disableExport,
|
||||
const bool disableCopy,
|
||||
const std::chrono::duration<double> callDuration)
|
||||
: _userid(userid),
|
||||
: FileInfo(filename, ownerId, modifiedTime, size),
|
||||
_userid(userid),
|
||||
_username(username),
|
||||
_hostInstanceId(hostInstanceId),
|
||||
_userCanWrite(userCanWrite),
|
||||
_postMessageOrigin(postMessageOrigin),
|
||||
_hidePrintOption(hidePrintOption),
|
||||
|
@ -210,6 +235,8 @@ public:
|
|||
std::string _userid;
|
||||
/// Display Name of user accessing the file
|
||||
std::string _username;
|
||||
/// Host instance ID (unique to the given host).
|
||||
std::string _hostInstanceId;
|
||||
/// If user accessing the file has write permission
|
||||
bool _userCanWrite;
|
||||
/// WOPI Post message property
|
||||
|
@ -236,7 +263,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 std::string& accessToken)
|
||||
{
|
||||
std::unique_ptr<WOPIFileInfo> info = getWOPIFileInfo(_uri, accessToken);
|
||||
_fileInfo = FileInfo(info->_filename, info->_ownerId, info->_modifiedTime, info->_size);
|
||||
return info;
|
||||
}
|
||||
|
||||
/// uri format: http://server/<...>/wopi*/files/<id>/content
|
||||
std::string loadStorageFileToLocal(const std::string& accessToken) override;
|
||||
|
@ -246,6 +278,11 @@ public:
|
|||
/// Total time taken for making WOPI calls during load
|
||||
std::chrono::duration<double> getWopiLoadDuration() const { return _wopiLoadDuration; }
|
||||
|
||||
/// Given the URI of a doc, return a unique doc ID.
|
||||
static std::string getUniqueDocId(const Poco::URI& uri);
|
||||
|
||||
static std::unique_ptr<WOPIFileInfo> getWOPIFileInfo(Poco::URI uriObject, const std::string& accessToken);
|
||||
|
||||
private:
|
||||
// Time spend in loading the file from storage
|
||||
std::chrono::duration<double> _wopiLoadDuration;
|
||||
|
@ -273,6 +310,13 @@ public:
|
|||
|
||||
SaveResult saveLocalFileToStorage(const std::string& accessToken) override;
|
||||
|
||||
/// Given the URI of a doc, return a unique doc ID.
|
||||
static std::string getUniqueDocId(const std::string& uri)
|
||||
{
|
||||
// TODO: Implement.
|
||||
return uri;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<AuthBase> _authAgent;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue