Merge pull request #12280 from misslivirose/feat/expose-identity-verification

Expose functionality to enable entity scripts to check ownership verification on avatar entities
This commit is contained in:
Liv 2018-02-01 10:07:58 -08:00 committed by GitHub
commit 6363ded74b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 117 additions and 86 deletions

View file

@ -16,6 +16,7 @@ CheckoutProxy::CheckoutProxy(QObject* qmlObject, QObject* parent) : QmlWrapper(q
}
WalletScriptingInterface::WalletScriptingInterface() {
}
void WalletScriptingInterface::refreshWalletStatus() {
@ -26,4 +27,19 @@ void WalletScriptingInterface::refreshWalletStatus() {
void WalletScriptingInterface::setWalletStatus(const uint& status) {
_walletStatus = status;
emit DependencyManager::get<Wallet>()->walletStatusResult(status);
}
void WalletScriptingInterface::proveAvatarEntityOwnershipVerification(const QUuid& entityID) {
QSharedPointer<ContextOverlayInterface> contextOverlayInterface = DependencyManager::get<ContextOverlayInterface>();
EntityItemProperties entityProperties = DependencyManager::get<EntityScriptingInterface>()->getEntityProperties(entityID,
contextOverlayInterface->getEntityPropertyFlags());
if (entityProperties.getClientOnly()) {
if (!entityID.isNull() && entityProperties.getCertificateID().length() > 0) {
contextOverlayInterface->requestOwnershipVerification(entityID);
} else {
qCDebug(entities) << "Failed to prove ownership of:" << entityID << "is null or not a certified item";
}
} else {
qCDebug(entities) << "Failed to prove ownership of:" << entityID << "is not an avatar entity";
}
}

View file

@ -21,6 +21,7 @@
#include <OffscreenUi.h>
#include "Application.h"
#include "commerce/Wallet.h"
#include "ui/overlays/ContextOverlayInterface.h"
class CheckoutProxy : public QmlWrapper {
Q_OBJECT
@ -39,6 +40,7 @@ public:
Q_INVOKABLE void refreshWalletStatus();
Q_INVOKABLE uint getWalletStatus() { return _walletStatus; }
Q_INVOKABLE void proveAvatarEntityOwnershipVerification(const QUuid& entityID);
// setWalletStatus() should never be made Q_INVOKABLE. If it were,
// scripts could cause the Wallet to incorrectly report its status.
void setWalletStatus(const uint& status);
@ -46,6 +48,8 @@ public:
signals:
void walletStatusChanged();
void walletNotSetup();
void ownershipVerificationSuccess(const QUuid& entityID);
void ownershipVerificationFailed(const QUuid& entityID);
private:
uint _walletStatus;

View file

@ -266,6 +266,93 @@ void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemI
}
}
void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID) {
setLastInspectedEntity(entityID);
EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_lastInspectedEntity, _entityPropertyFlags);
auto nodeList = DependencyManager::get<NodeList>();
if (entityProperties.getClientOnly()) {
if (entityProperties.verifyStaticCertificateProperties()) {
SharedNodePointer entityServer = nodeList->soloNodeOfType(NodeType::EntityServer);
if (entityServer) {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest;
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/transfer");
QJsonObject request;
request["certificate_id"] = entityProperties.getCertificateID();
networkRequest.setUrl(requestURL);
QNetworkReply* networkReply = NULL;
networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
connect(networkReply, &QNetworkReply::finished, [=]() {
QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
jsonObject = jsonObject["data"].toObject();
if (networkReply->error() == QNetworkReply::NoError) {
if (!jsonObject["invalid_reason"].toString().isEmpty()) {
qCDebug(entities) << "invalid_reason not empty";
} else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") {
qCDebug(entities) << "'transfer_status' is 'failed'";
} else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") {
qCDebug(entities) << "'transfer_status' is 'pending'";
} else {
QString ownerKey = jsonObject["transfer_recipient_key"].toString();
QByteArray certID = entityProperties.getCertificateID().toUtf8();
QByteArray text = DependencyManager::get<EntityTreeRenderer>()->getTree()->computeNonce(certID, ownerKey);
QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122();
int certIDByteArraySize = certID.length();
int textByteArraySize = text.length();
int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length();
auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest,
certIDByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int),
true);
challengeOwnershipPacket->writePrimitive(certIDByteArraySize);
challengeOwnershipPacket->writePrimitive(textByteArraySize);
challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize);
challengeOwnershipPacket->write(certID);
challengeOwnershipPacket->write(text);
challengeOwnershipPacket->write(nodeToChallengeByteArray);
nodeList->sendPacket(std::move(challengeOwnershipPacket), *entityServer);
// Kickoff a 10-second timeout timer that marks the cert if we don't get an ownership response in time
if (thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer");
return;
} else {
startChallengeOwnershipTimer();
}
}
} else {
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() <<
"More info:" << networkReply->readAll();
}
networkReply->deleteLater();
});
} else {
qCWarning(context_overlay) << "Couldn't get Entity Server!";
}
} else {
auto ledger = DependencyManager::get<Ledger>();
_challengeOwnershipTimeoutTimer.stop();
emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED));
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationFailed(_lastInspectedEntity);
qCDebug(context_overlay) << "Entity" << _lastInspectedEntity << "failed static certificate verification!";
}
}
}
static const QString INSPECTION_CERTIFICATE_QML_PATH = "hifi/commerce/inspectionCertificate/InspectionCertificate.qml";
void ContextOverlayInterface::openInspectionCertificate() {
// lets open the tablet to the inspection certificate QML
@ -275,87 +362,7 @@ void ContextOverlayInterface::openInspectionCertificate() {
_hmdScriptingInterface->openTablet();
setLastInspectedEntity(_currentEntityWithContextOverlay);
EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_lastInspectedEntity, _entityPropertyFlags);
auto nodeList = DependencyManager::get<NodeList>();
if (entityProperties.getClientOnly()) {
if (entityProperties.verifyStaticCertificateProperties()) {
SharedNodePointer entityServer = nodeList->soloNodeOfType(NodeType::EntityServer);
if (entityServer) {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest;
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/transfer");
QJsonObject request;
request["certificate_id"] = entityProperties.getCertificateID();
networkRequest.setUrl(requestURL);
QNetworkReply* networkReply = NULL;
networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
connect(networkReply, &QNetworkReply::finished, [=]() {
QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
jsonObject = jsonObject["data"].toObject();
if (networkReply->error() == QNetworkReply::NoError) {
if (!jsonObject["invalid_reason"].toString().isEmpty()) {
qCDebug(entities) << "invalid_reason not empty";
} else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") {
qCDebug(entities) << "'transfer_status' is 'failed'";
} else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") {
qCDebug(entities) << "'transfer_status' is 'pending'";
} else {
QString ownerKey = jsonObject["transfer_recipient_key"].toString();
QByteArray certID = entityProperties.getCertificateID().toUtf8();
QByteArray text = DependencyManager::get<EntityTreeRenderer>()->getTree()->computeNonce(certID, ownerKey);
QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122();
int certIDByteArraySize = certID.length();
int textByteArraySize = text.length();
int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length();
auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest,
certIDByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int),
true);
challengeOwnershipPacket->writePrimitive(certIDByteArraySize);
challengeOwnershipPacket->writePrimitive(textByteArraySize);
challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize);
challengeOwnershipPacket->write(certID);
challengeOwnershipPacket->write(text);
challengeOwnershipPacket->write(nodeToChallengeByteArray);
nodeList->sendPacket(std::move(challengeOwnershipPacket), *entityServer);
// Kickoff a 10-second timeout timer that marks the cert if we don't get an ownership response in time
if (thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer");
return;
} else {
startChallengeOwnershipTimer();
}
}
} else {
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() <<
"More info:" << networkReply->readAll();
}
networkReply->deleteLater();
});
} else {
qCWarning(context_overlay) << "Couldn't get Entity Server!";
}
} else {
auto ledger = DependencyManager::get<Ledger>();
_challengeOwnershipTimeoutTimer.stop();
emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED));
qCDebug(context_overlay) << "Entity" << _lastInspectedEntity << "failed static certificate verification!";
}
}
requestOwnershipVerification(_lastInspectedEntity);
}
}
@ -397,6 +404,7 @@ void ContextOverlayInterface::startChallengeOwnershipTimer() {
connect(&_challengeOwnershipTimeoutTimer, &QTimer::timeout, this, [=]() {
qCDebug(entities) << "Ownership challenge timed out for" << _lastInspectedEntity;
emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_TIMEOUT));
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationFailed(_lastInspectedEntity);
});
_challengeOwnershipTimeoutTimer.start(5000);
@ -421,7 +429,9 @@ void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer
if (verificationSuccess) {
emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS));
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationSuccess(_lastInspectedEntity);
} else {
emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED));
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationFailed(_lastInspectedEntity);
}
}

View file

@ -26,6 +26,7 @@
#include "ui/overlays/Overlays.h"
#include "scripting/HMDScriptingInterface.h"
#include "scripting/SelectionScriptingInterface.h"
#include "scripting/WalletScriptingInterface.h"
#include "EntityTree.h"
#include "ContextOverlayLogging.h"
@ -33,12 +34,13 @@
/**jsdoc
* @namespace ContextOverlay
*/
class ContextOverlayInterface : public QObject, public Dependency {
class ContextOverlayInterface : public QObject, public Dependency {
Q_OBJECT
Q_PROPERTY(QUuid entityWithContextOverlay READ getCurrentEntityWithContextOverlay WRITE setCurrentEntityWithContextOverlay)
Q_PROPERTY(bool enabled READ getEnabled WRITE setEnabled)
Q_PROPERTY(bool isInMarketplaceInspectionMode READ getIsInMarketplaceInspectionMode WRITE setIsInMarketplaceInspectionMode)
QSharedPointer<EntityScriptingInterface> _entityScriptingInterface;
EntityPropertyFlags _entityPropertyFlags;
QSharedPointer<HMDScriptingInterface> _hmdScriptingInterface;
@ -47,9 +49,7 @@ class ContextOverlayInterface : public QObject, public Dependency {
OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID };
std::shared_ptr<Image3DOverlay> _contextOverlay { nullptr };
public:
ContextOverlayInterface();
Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; }
void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; }
void setLastInspectedEntity(const QUuid& entityID) { _challengeOwnershipTimeoutTimer.stop(); _lastInspectedEntity = entityID; }
@ -57,6 +57,8 @@ public:
bool getEnabled() { return _enabled; }
bool getIsInMarketplaceInspectionMode() { return _isInMarketplaceInspectionMode; }
void setIsInMarketplaceInspectionMode(bool mode) { _isInMarketplaceInspectionMode = mode; }
void requestOwnershipVerification(const QUuid& entityID);
EntityPropertyFlags getEntityPropertyFlags() { return _entityPropertyFlags; }
signals:
void contextOverlayClicked(const QUuid& currentEntityWithContextOverlay);
@ -80,8 +82,7 @@ private:
enum {
MAX_SELECTION_COUNT = 16
};
bool _verboseLogging { true };
bool _verboseLogging{ true };
bool _enabled { true };
EntityItemID _currentEntityWithContextOverlay{};
EntityItemID _lastInspectedEntity{};