Initial path to fixing screenshare auth race condition

This commit is contained in:
Zach Fox 2019-12-05 11:55:24 -08:00
parent f01a39adf5
commit c96c684400
6 changed files with 86 additions and 12 deletions

View file

@ -3634,10 +3634,10 @@ void DomainServer::processAvatarZonePresencePacket(QSharedPointer<ReceivedMessag
return; return;
} }
static const int SCREENSHARE_EXPIRATION_SECONDS = 24 * 60 * 60; static const int SCREENSHARE_EXPIRATION_SECONDS = 24 * 60 * 60;
screensharePresence(zone.isNull() ? "" : zone.toString(), verifiedUsername, SCREENSHARE_EXPIRATION_SECONDS); screensharePresence(zone.isNull() ? "" : zone.toString(), verifiedUsername, avatar, SCREENSHARE_EXPIRATION_SECONDS);
} }
void DomainServer::screensharePresence(QString roomname, QString username, int expirationSeconds) { void DomainServer::screensharePresence(QString roomname, QString username, QUuid avatarID, int expirationSeconds) {
if (!DependencyManager::get<AccountManager>()->hasValidAccessToken()) { if (!DependencyManager::get<AccountManager>()->hasValidAccessToken()) {
static std::once_flag presenceAuthorityWarning; static std::once_flag presenceAuthorityWarning;
std::call_once(presenceAuthorityWarning, [] { std::call_once(presenceAuthorityWarning, [] {
@ -3649,6 +3649,10 @@ void DomainServer::screensharePresence(QString roomname, QString username, int e
callbackParams.callbackReceiver = this; callbackParams.callbackReceiver = this;
callbackParams.jsonCallbackMethod = "handleSuccessfulScreensharePresence"; callbackParams.jsonCallbackMethod = "handleSuccessfulScreensharePresence";
callbackParams.errorCallbackMethod = "handleFailedScreensharePresence"; callbackParams.errorCallbackMethod = "handleFailedScreensharePresence";
QJsonObject callbackData;
callbackData.insert("roomname", roomname);
callbackData.insert("avatarID", avatarID.toString());
callbackParams.callbackData = callbackData;
const QString PATH = "api/v1/domains/%1/screenshare"; const QString PATH = "api/v1/domains/%1/screenshare";
QString domain_id = uuidStringWithoutCurlyBraces(getID()); QString domain_id = uuidStringWithoutCurlyBraces(getID());
QJsonObject json, screenshare; QJsonObject json, screenshare;
@ -3666,11 +3670,17 @@ void DomainServer::screensharePresence(QString roomname, QString username, int e
); );
} }
void DomainServer::handleSuccessfulScreensharePresence(QNetworkReply* requestReply) { void DomainServer::handleSuccessfulScreensharePresence(QNetworkReply* requestReply, QJsonObject callbackData) {
QJsonObject jsonObject = QJsonDocument::fromJson(requestReply->readAll()).object(); QJsonObject jsonObject = QJsonDocument::fromJson(requestReply->readAll()).object();
if (jsonObject["status"].toString() != "success") { if (jsonObject["status"].toString() != "success") {
qCWarning(domain_server) << "screensharePresence api call failed:" << QJsonDocument(jsonObject).toJson(QJsonDocument::Compact); qCWarning(domain_server) << "screensharePresence api call failed:" << QJsonDocument(jsonObject).toJson(QJsonDocument::Compact);
return;
} }
auto nodeList = DependencyManager::get<LimitedNodeList>();
auto packet = NLPacket::create(PacketType::AvatarZonePresence, NUM_BYTES_RFC4122_UUID, true);
packet->write(QUuid(callbackData["roomname"].toString()).toRfc4122());
nodeList->sendPacket(std::move(packet), *(nodeList->nodeWithUUID(QUuid(callbackData["avatarID"].toString()))));
} }
void DomainServer::handleFailedScreensharePresence(QNetworkReply* requestReply) { void DomainServer::handleFailedScreensharePresence(QNetworkReply* requestReply) {

View file

@ -78,7 +78,7 @@ public:
bool isAssetServerEnabled(); bool isAssetServerEnabled();
void screensharePresence(QString roomname, QString username, int expiration_seconds = 0); void screensharePresence(QString roomname, QString username, QUuid avatarID, int expiration_seconds = 0);
public slots: public slots:
/// Called by NodeList to inform us a node has been added /// Called by NodeList to inform us a node has been added
@ -132,7 +132,7 @@ private slots:
void handleSuccessfulICEServerAddressUpdate(QNetworkReply* requestReply); void handleSuccessfulICEServerAddressUpdate(QNetworkReply* requestReply);
void handleFailedICEServerAddressUpdate(QNetworkReply* requestReply); void handleFailedICEServerAddressUpdate(QNetworkReply* requestReply);
void handleSuccessfulScreensharePresence(QNetworkReply* requestReply); void handleSuccessfulScreensharePresence(QNetworkReply* requestReply, QJsonObject callbackData);
void handleFailedScreensharePresence(QNetworkReply* requestReply); void handleFailedScreensharePresence(QNetworkReply* requestReply);
void updateReplicatedNodes(); void updateReplicatedNodes();

View file

@ -37,14 +37,41 @@ ScreenshareScriptingInterface::ScreenshareScriptingInterface() {
_requestScreenshareInfoRetryTimer->setSingleShot(true); _requestScreenshareInfoRetryTimer->setSingleShot(true);
_requestScreenshareInfoRetryTimer->setInterval(SCREENSHARE_INFO_REQUEST_RETRY_TIMEOUT_MS); _requestScreenshareInfoRetryTimer->setInterval(SCREENSHARE_INFO_REQUEST_RETRY_TIMEOUT_MS);
connect(_requestScreenshareInfoRetryTimer, &QTimer::timeout, this, &ScreenshareScriptingInterface::requestScreenshareInfo); connect(_requestScreenshareInfoRetryTimer, &QTimer::timeout, this, &ScreenshareScriptingInterface::requestScreenshareInfo);
auto nodeList = DependencyManager::get<NodeList>();
PacketReceiver& packetReceiver = nodeList->getPacketReceiver();
packetReceiver.registerListener(PacketType::AvatarZonePresence, this, "processAvatarZonePresencePacketOnClient");
}; };
ScreenshareScriptingInterface::~ScreenshareScriptingInterface() { ScreenshareScriptingInterface::~ScreenshareScriptingInterface() {
stopScreenshare(); stopScreenshare();
} }
void ScreenshareScriptingInterface::processAvatarZonePresencePacketOnClient(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
QUuid zone = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
if (zone.isNull()) {
qWarning() << "Ignoring avatar zone presence packet that doesn't specify a zone.";
return;
}
_lastAuthorizedZoneID = zone;
if (_waitingForAuthorization) {
requestScreenshareInfo();
}
}
static const int MAX_NUM_SCREENSHARE_INFO_REQUEST_RETRIES = 5; static const int MAX_NUM_SCREENSHARE_INFO_REQUEST_RETRIES = 5;
void ScreenshareScriptingInterface::requestScreenshareInfo() { void ScreenshareScriptingInterface::requestScreenshareInfo() {
if (_screenshareZoneID != _lastAuthorizedZoneID) {
qDebug() << "Client not yet authorized to screenshare. Waiting for authorization message from domain server...";
_waitingForAuthorization = true;
return;
}
_waitingForAuthorization = false;
_requestScreenshareInfoRetries++; _requestScreenshareInfoRetries++;
if (_requestScreenshareInfoRetries >= MAX_NUM_SCREENSHARE_INFO_REQUEST_RETRIES) { if (_requestScreenshareInfoRetries >= MAX_NUM_SCREENSHARE_INFO_REQUEST_RETRIES) {
@ -174,6 +201,7 @@ void ScreenshareScriptingInterface::stopScreenshare() {
_projectAPIKey = ""; _projectAPIKey = "";
_sessionID = ""; _sessionID = "";
_isPresenter = false; _isPresenter = false;
_waitingForAuthorization = false;
} }
// Called when the Metaverse returns the information necessary to start/view a screen share. // Called when the Metaverse returns the information necessary to start/view a screen share.

View file

@ -41,6 +41,8 @@ private slots:
void handleFailedScreenshareInfoGet(QNetworkReply* reply); void handleFailedScreenshareInfoGet(QNetworkReply* reply);
private: private:
void processAvatarZonePresencePacketOnClient(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
#if DEV_BUILD #if DEV_BUILD
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
const QString SCREENSHARE_EXE_PATH{ PathUtils::projectRootPath() + "/screenshare/hifi-screenshare-win32-x64/hifi-screenshare.exe" }; const QString SCREENSHARE_EXE_PATH{ PathUtils::projectRootPath() + "/screenshare/hifi-screenshare-win32-x64/hifi-screenshare.exe" };
@ -82,6 +84,9 @@ private:
QUuid _screenshareZoneID; QUuid _screenshareZoneID;
QUuid _smartboardEntityID; QUuid _smartboardEntityID;
bool _isPresenter{ false }; bool _isPresenter{ false };
QUuid _lastAuthorizedZoneID;
bool _waitingForAuthorization{ false };
}; };
#endif // hifi_ScreenshareScriptingInterface_h #endif // hifi_ScreenshareScriptingInterface_h

View file

@ -61,6 +61,18 @@ JSONCallbackParameters::JSONCallbackParameters(QObject* callbackReceiver,
} }
JSONCallbackParameters::JSONCallbackParameters(QObject* callbackReceiver,
const QString& jsonCallbackMethod,
const QString& errorCallbackMethod,
const QJsonObject& callbackData) :
callbackReceiver(callbackReceiver),
jsonCallbackMethod(jsonCallbackMethod),
errorCallbackMethod(errorCallbackMethod),
callbackData(callbackData)
{
}
QJsonObject AccountManager::dataObjectFromResponse(QNetworkReply* requestReply) { QJsonObject AccountManager::dataObjectFromResponse(QNetworkReply* requestReply) {
QJsonObject jsonObject = QJsonDocument::fromJson(requestReply->readAll()).object(); QJsonObject jsonObject = QJsonDocument::fromJson(requestReply->readAll()).object();
@ -348,9 +360,17 @@ void AccountManager::sendRequest(const QString& path,
[callbackParams, networkReply] { [callbackParams, networkReply] {
if (networkReply->error() == QNetworkReply::NoError) { if (networkReply->error() == QNetworkReply::NoError) {
if (!callbackParams.jsonCallbackMethod.isEmpty()) { if (!callbackParams.jsonCallbackMethod.isEmpty()) {
bool invoked = QMetaObject::invokeMethod(callbackParams.callbackReceiver, bool invoked = false;
qPrintable(callbackParams.jsonCallbackMethod), if (callbackParams.callbackData.isEmpty()) {
Q_ARG(QNetworkReply*, networkReply)); invoked = QMetaObject::invokeMethod(callbackParams.callbackReceiver,
qPrintable(callbackParams.jsonCallbackMethod),
Q_ARG(QNetworkReply*, networkReply));
} else {
invoked = QMetaObject::invokeMethod(callbackParams.callbackReceiver,
qPrintable(callbackParams.jsonCallbackMethod),
Q_ARG(QNetworkReply*, networkReply),
Q_ARG(QJsonObject, callbackParams.callbackData));
}
if (!invoked) { if (!invoked) {
QString error = "Could not invoke " + callbackParams.jsonCallbackMethod + " with QNetworkReply* " QString error = "Could not invoke " + callbackParams.jsonCallbackMethod + " with QNetworkReply* "
@ -366,9 +386,18 @@ void AccountManager::sendRequest(const QString& path,
} }
} else { } else {
if (!callbackParams.errorCallbackMethod.isEmpty()) { if (!callbackParams.errorCallbackMethod.isEmpty()) {
bool invoked = QMetaObject::invokeMethod(callbackParams.callbackReceiver, bool invoked = false;
qPrintable(callbackParams.errorCallbackMethod), if (callbackParams.callbackData.isEmpty()) {
Q_ARG(QNetworkReply*, networkReply)); invoked = QMetaObject::invokeMethod(callbackParams.callbackReceiver,
qPrintable(callbackParams.errorCallbackMethod),
Q_ARG(QNetworkReply*, networkReply));
}
else {
invoked = QMetaObject::invokeMethod(callbackParams.callbackReceiver,
qPrintable(callbackParams.errorCallbackMethod),
Q_ARG(QNetworkReply*, networkReply),
Q_ARG(QJsonObject, callbackParams.callbackData));
}
if (!invoked) { if (!invoked) {
QString error = "Could not invoke " + callbackParams.errorCallbackMethod + " with QNetworkReply* " QString error = "Could not invoke " + callbackParams.errorCallbackMethod + " with QNetworkReply* "

View file

@ -31,13 +31,15 @@ class JSONCallbackParameters {
public: public:
JSONCallbackParameters(QObject* callbackReceiver = nullptr, JSONCallbackParameters(QObject* callbackReceiver = nullptr,
const QString& jsonCallbackMethod = QString(), const QString& jsonCallbackMethod = QString(),
const QString& errorCallbackMethod = QString()); const QString& errorCallbackMethod = QString(),
const QJsonObject& callbackData = QJsonObject());
bool isEmpty() const { return !callbackReceiver; } bool isEmpty() const { return !callbackReceiver; }
QObject* callbackReceiver; QObject* callbackReceiver;
QString jsonCallbackMethod; QString jsonCallbackMethod;
QString errorCallbackMethod; QString errorCallbackMethod;
QJsonObject callbackData;
}; };
namespace AccountManagerAuth { namespace AccountManagerAuth {