mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge pull request #15275 from howard-stearns/ddv-all
DDV all, including domainUnlimited
This commit is contained in:
commit
f2ff5bebc7
8 changed files with 209 additions and 201 deletions
|
@ -22,7 +22,6 @@
|
|||
#include <ScriptCache.h>
|
||||
#include <EntityEditFilters.h>
|
||||
#include <NetworkingConstants.h>
|
||||
#include <AddressManager.h>
|
||||
#include <hfm/ModelFormatRegistry.h>
|
||||
|
||||
#include "../AssignmentDynamicFactory.h"
|
||||
|
@ -471,77 +470,7 @@ void EntityServer::startDynamicDomainVerification() {
|
|||
qCDebug(entities) << "Starting Dynamic Domain Verification...";
|
||||
|
||||
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
||||
QHash<QString, EntityItemID> localMap(tree->getEntityCertificateIDMap());
|
||||
|
||||
QHashIterator<QString, EntityItemID> i(localMap);
|
||||
qCDebug(entities) << localMap.size() << "entities in _entityCertificateIDMap";
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
const auto& certificateID = i.key();
|
||||
const auto& entityID = i.value();
|
||||
|
||||
EntityItemPointer entity = tree->findEntityByEntityItemID(entityID);
|
||||
|
||||
if (entity) {
|
||||
if (!entity->getProperties().verifyStaticCertificateProperties()) {
|
||||
qCDebug(entities) << "During Dynamic Domain Verification, a certified entity with ID" << entityID << "failed"
|
||||
<< "static certificate verification.";
|
||||
// Delete the entity if it doesn't pass static certificate verification
|
||||
tree->withWriteLock([&] {
|
||||
tree->deleteEntity(entityID, true);
|
||||
});
|
||||
} else {
|
||||
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/location");
|
||||
QJsonObject request;
|
||||
request["certificate_id"] = certificateID;
|
||||
networkRequest.setUrl(requestURL);
|
||||
|
||||
QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
|
||||
|
||||
connect(networkReply, &QNetworkReply::finished, this, [this, entityID, networkReply] {
|
||||
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
||||
|
||||
QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
|
||||
jsonObject = jsonObject["data"].toObject();
|
||||
|
||||
if (networkReply->error() == QNetworkReply::NoError) {
|
||||
QString thisDomainID = DependencyManager::get<AddressManager>()->getDomainID().remove(QRegExp("\\{|\\}"));
|
||||
if (jsonObject["domain_id"].toString() != thisDomainID) {
|
||||
EntityItemPointer entity = tree->findEntityByEntityItemID(entityID);
|
||||
if (!entity) {
|
||||
qCDebug(entities) << "Entity undergoing dynamic domain verification is no longer available:" << entityID;
|
||||
networkReply->deleteLater();
|
||||
return;
|
||||
}
|
||||
if (entity->getAge() > (_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS/MSECS_PER_SECOND)) {
|
||||
qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString()
|
||||
<< "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << entityID;
|
||||
tree->withWriteLock([&] {
|
||||
tree->deleteEntity(entityID, true);
|
||||
});
|
||||
} else {
|
||||
qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << entityID;
|
||||
}
|
||||
} else {
|
||||
qCDebug(entities) << "Entity passed dynamic domain verification:" << entityID;
|
||||
}
|
||||
} else {
|
||||
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; NOT deleting entity" << entityID
|
||||
<< "More info:" << jsonObject;
|
||||
}
|
||||
|
||||
networkReply->deleteLater();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
qCWarning(entities) << "During DDV, an entity with ID" << entityID << "was NOT found in the Entity Tree!";
|
||||
}
|
||||
}
|
||||
tree->startDynamicDomainVerificationOnServer((float) _MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS / MSECS_PER_SECOND);
|
||||
|
||||
int nextInterval = qrand() % ((_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS + 1) - _MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS) + _MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS;
|
||||
qCDebug(entities) << "Restarting Dynamic Domain Verification timer for" << nextInterval / 1000 << "seconds";
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <QJsonObject>
|
||||
#include <DependencyManager.h>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
#include <EntityItemID.h>
|
||||
#include "AccountManager.h"
|
||||
|
||||
|
||||
|
@ -65,7 +66,7 @@ signals:
|
|||
void availableUpdatesResult(QJsonObject result);
|
||||
void updateItemResult(QJsonObject result);
|
||||
|
||||
void updateCertificateStatus(const QString& certID, uint certStatus);
|
||||
void updateCertificateStatus(const EntityItemID& entityID, uint certStatus);
|
||||
|
||||
public slots:
|
||||
void buySuccess(QNetworkReply* reply);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <QPixmap>
|
||||
|
||||
#include <EntityItemID.h>
|
||||
#include <DependencyManager.h>
|
||||
|
||||
class QmlCommerce : public QObject, public Dependency {
|
||||
|
@ -49,7 +50,7 @@ signals:
|
|||
void availableUpdatesResult(QJsonObject result);
|
||||
void updateItemResult(QJsonObject result);
|
||||
|
||||
void updateCertificateStatus(const QString& certID, uint certStatus);
|
||||
void updateCertificateStatus(const EntityItemID& entityID, uint certStatus);
|
||||
|
||||
void transferAssetToNodeResult(QJsonObject result);
|
||||
void transferAssetToUsernameResult(QJsonObject result);
|
||||
|
|
|
@ -816,18 +816,18 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer<ReceivedMessage> pack
|
|||
|
||||
bool challengeOriginatedFromClient = packet->getType() == PacketType::ChallengeOwnershipRequest;
|
||||
int status;
|
||||
int certIDByteArraySize;
|
||||
int idByteArraySize;
|
||||
int textByteArraySize;
|
||||
int challengingNodeUUIDByteArraySize;
|
||||
|
||||
packet->readPrimitive(&certIDByteArraySize);
|
||||
packet->readPrimitive(&idByteArraySize);
|
||||
packet->readPrimitive(&textByteArraySize); // returns a cast char*, size
|
||||
if (challengeOriginatedFromClient) {
|
||||
packet->readPrimitive(&challengingNodeUUIDByteArraySize);
|
||||
}
|
||||
|
||||
// "encryptedText" is now a series of random bytes, a nonce
|
||||
QByteArray certID = packet->read(certIDByteArraySize);
|
||||
QByteArray id = packet->read(idByteArraySize);
|
||||
QByteArray text = packet->read(textByteArraySize);
|
||||
QByteArray challengingNodeUUID;
|
||||
if (challengeOriginatedFromClient) {
|
||||
|
@ -853,32 +853,32 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer<ReceivedMessage> pack
|
|||
textByteArray = sig.toUtf8();
|
||||
}
|
||||
textByteArraySize = textByteArray.size();
|
||||
int certIDSize = certID.size();
|
||||
int idSize = id.size();
|
||||
// setup the packet
|
||||
if (challengeOriginatedFromClient) {
|
||||
auto textPacket = NLPacket::create(PacketType::ChallengeOwnershipReply,
|
||||
certIDSize + textByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int),
|
||||
idSize + textByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int),
|
||||
true);
|
||||
|
||||
textPacket->writePrimitive(certIDSize);
|
||||
textPacket->writePrimitive(idSize);
|
||||
textPacket->writePrimitive(textByteArraySize);
|
||||
textPacket->writePrimitive(challengingNodeUUIDByteArraySize);
|
||||
textPacket->write(certID);
|
||||
textPacket->write(id);
|
||||
textPacket->write(textByteArray);
|
||||
textPacket->write(challengingNodeUUID);
|
||||
|
||||
qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing signed text" << textByteArray << "for CertID" << certID;
|
||||
qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing signed text" << textByteArray << "for id" << id;
|
||||
|
||||
nodeList->sendPacket(std::move(textPacket), *sendingNode);
|
||||
} else {
|
||||
auto textPacket = NLPacket::create(PacketType::ChallengeOwnership, certIDSize + textByteArraySize + 2 * sizeof(int), true);
|
||||
auto textPacket = NLPacket::create(PacketType::ChallengeOwnership, idSize + textByteArraySize + 2 * sizeof(int), true);
|
||||
|
||||
textPacket->writePrimitive(certIDSize);
|
||||
textPacket->writePrimitive(idSize);
|
||||
textPacket->writePrimitive(textByteArraySize);
|
||||
textPacket->write(certID);
|
||||
textPacket->write(id);
|
||||
textPacket->write(textByteArray);
|
||||
|
||||
qCDebug(commerce) << "Sending ChallengeOwnership Packet containing signed text" << textByteArray << "for CertID" << certID;
|
||||
qCDebug(commerce) << "Sending ChallengeOwnership Packet containing signed text" << textByteArray << "for id" << id;
|
||||
|
||||
nodeList->sendPacket(std::move(textPacket), *sendingNode);
|
||||
}
|
||||
|
|
|
@ -292,7 +292,7 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID
|
|||
|
||||
setLastInspectedEntity(entityID);
|
||||
|
||||
EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_lastInspectedEntity, _entityPropertyFlags);
|
||||
EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityID, _entityPropertyFlags);
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
|
@ -328,31 +328,31 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID
|
|||
} else {
|
||||
QString ownerKey = jsonObject["transfer_recipient_key"].toString();
|
||||
|
||||
QByteArray certID = entityProperties.getCertificateID().toUtf8();
|
||||
QByteArray text = DependencyManager::get<EntityTreeRenderer>()->getTree()->computeNonce(certID, ownerKey);
|
||||
QByteArray id = entityID.toByteArray();
|
||||
QByteArray text = DependencyManager::get<EntityTreeRenderer>()->getTree()->computeNonce(entityID, ownerKey);
|
||||
QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122();
|
||||
|
||||
int certIDByteArraySize = certID.length();
|
||||
int idByteArraySize = id.length();
|
||||
int textByteArraySize = text.length();
|
||||
int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length();
|
||||
|
||||
auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest,
|
||||
certIDByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int),
|
||||
idByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int),
|
||||
true);
|
||||
challengeOwnershipPacket->writePrimitive(certIDByteArraySize);
|
||||
challengeOwnershipPacket->writePrimitive(idByteArraySize);
|
||||
challengeOwnershipPacket->writePrimitive(textByteArraySize);
|
||||
challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize);
|
||||
challengeOwnershipPacket->write(certID);
|
||||
challengeOwnershipPacket->write(id);
|
||||
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");
|
||||
QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer", Q_ARG(const EntityItemID&, entityID));
|
||||
return;
|
||||
} else {
|
||||
startChallengeOwnershipTimer();
|
||||
startChallengeOwnershipTimer(entityID);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -370,14 +370,14 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID
|
|||
// so they always pass Ownership Verification. It's necessary to emit this signal
|
||||
// so that the Inspection Certificate can continue its information-grabbing process.
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS));
|
||||
emit ledger->updateCertificateStatus(entityID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS));
|
||||
}
|
||||
} 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!";
|
||||
emit ledger->updateCertificateStatus(entityID, (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED));
|
||||
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationFailed(entityID);
|
||||
qCDebug(context_overlay) << "Entity" << entityID << "failed static certificate verification!";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -395,14 +395,14 @@ void ContextOverlayInterface::deletingEntity(const EntityItemID& entityID) {
|
|||
}
|
||||
}
|
||||
|
||||
void ContextOverlayInterface::startChallengeOwnershipTimer() {
|
||||
void ContextOverlayInterface::startChallengeOwnershipTimer(const EntityItemID& entityItemID) {
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_lastInspectedEntity, _entityPropertyFlags);
|
||||
EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags);
|
||||
|
||||
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);
|
||||
qCDebug(entities) << "Ownership challenge timed out for" << entityItemID;
|
||||
emit ledger->updateCertificateStatus(entityItemID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_TIMEOUT));
|
||||
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationFailed(entityItemID);
|
||||
});
|
||||
|
||||
_challengeOwnershipTimeoutTimer.start(5000);
|
||||
|
@ -413,23 +413,22 @@ void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer
|
|||
|
||||
_challengeOwnershipTimeoutTimer.stop();
|
||||
|
||||
int certIDByteArraySize;
|
||||
int idByteArraySize;
|
||||
int textByteArraySize;
|
||||
|
||||
packet->readPrimitive(&certIDByteArraySize);
|
||||
packet->readPrimitive(&idByteArraySize);
|
||||
packet->readPrimitive(&textByteArraySize);
|
||||
|
||||
QString certID(packet->read(certIDByteArraySize));
|
||||
EntityItemID id(packet->read(idByteArraySize));
|
||||
QString text(packet->read(textByteArraySize));
|
||||
|
||||
EntityItemID id;
|
||||
bool verificationSuccess = DependencyManager::get<EntityTreeRenderer>()->getTree()->verifyNonce(certID, text, id);
|
||||
bool verificationSuccess = DependencyManager::get<EntityTreeRenderer>()->getTree()->verifyNonce(id, text);
|
||||
|
||||
if (verificationSuccess) {
|
||||
emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS));
|
||||
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationSuccess(_lastInspectedEntity);
|
||||
emit ledger->updateCertificateStatus(id, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS));
|
||||
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationSuccess(id);
|
||||
} else {
|
||||
emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED));
|
||||
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationFailed(_lastInspectedEntity);
|
||||
emit ledger->updateCertificateStatus(id, (uint)(ledger->CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED));
|
||||
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationFailed(id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ public:
|
|||
ContextOverlayInterface();
|
||||
Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; }
|
||||
void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; }
|
||||
void setLastInspectedEntity(const QUuid& entityID) { _challengeOwnershipTimeoutTimer.stop(); _lastInspectedEntity = entityID; }
|
||||
void setLastInspectedEntity(const QUuid& entityID) { _challengeOwnershipTimeoutTimer.stop(); }
|
||||
void setEnabled(bool enabled);
|
||||
bool getEnabled() { return _enabled; }
|
||||
bool getIsInMarketplaceInspectionMode() { return _isInMarketplaceInspectionMode; }
|
||||
|
@ -83,7 +83,6 @@ private:
|
|||
EntityItemID _mouseDownEntity;
|
||||
quint64 _mouseDownEntityTimestamp;
|
||||
EntityItemID _currentEntityWithContextOverlay;
|
||||
EntityItemID _lastInspectedEntity;
|
||||
QString _entityMarketplaceID;
|
||||
bool _contextOverlayJustClicked { false };
|
||||
|
||||
|
@ -94,7 +93,7 @@ private:
|
|||
|
||||
void deletingEntity(const EntityItemID& entityItemID);
|
||||
|
||||
Q_INVOKABLE void startChallengeOwnershipTimer();
|
||||
Q_INVOKABLE void startChallengeOwnershipTimer(const EntityItemID& entityItemID);
|
||||
QTimer _challengeOwnershipTimeoutTimer;
|
||||
};
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <Extents.h>
|
||||
#include <PerfStat.h>
|
||||
#include <Profile.h>
|
||||
#include <AddressManager.h>
|
||||
|
||||
#include "EntitySimulation.h"
|
||||
#include "VariantMapToScriptValue.h"
|
||||
|
@ -286,27 +287,7 @@ void EntityTree::postAddEntity(EntityItemPointer entity) {
|
|||
assert(entity);
|
||||
|
||||
if (getIsServer()) {
|
||||
QString certID(entity->getCertificateID());
|
||||
EntityItemID entityItemID = entity->getEntityItemID();
|
||||
EntityItemID existingEntityItemID;
|
||||
|
||||
{
|
||||
QWriteLocker locker(&_entityCertificateIDMapLock);
|
||||
existingEntityItemID = _entityCertificateIDMap.value(certID);
|
||||
if (!certID.isEmpty()) {
|
||||
_entityCertificateIDMap.insert(certID, entityItemID);
|
||||
qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete an already-existing entity from the tree if it has the same
|
||||
// CertificateID as the entity we're trying to add.
|
||||
if (!existingEntityItemID.isNull() && !entity->getCertificateType().contains(DOMAIN_UNLIMITED)) {
|
||||
qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID"
|
||||
<< existingEntityItemID << ". Deleting existing entity.";
|
||||
deleteEntity(existingEntityItemID, true);
|
||||
return;
|
||||
}
|
||||
addCertifiedEntityOnServer(entity);
|
||||
}
|
||||
|
||||
// check to see if we need to simulate this entity..
|
||||
|
@ -764,13 +745,7 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator)
|
|||
theEntity->die();
|
||||
|
||||
if (getIsServer()) {
|
||||
{
|
||||
QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock);
|
||||
QString certID = theEntity->getCertificateID();
|
||||
if (theEntity->getEntityItemID() == _entityCertificateIDMap.value(certID)) {
|
||||
_entityCertificateIDMap.remove(certID);
|
||||
}
|
||||
}
|
||||
removeCertifiedEntityOnServer(theEntity);
|
||||
|
||||
// set up the deleted entities ID
|
||||
QWriteLocker recentlyDeletedEntitiesLocker(&_recentlyDeletedEntitiesLock);
|
||||
|
@ -1421,11 +1396,123 @@ bool EntityTree::isScriptInWhitelist(const QString& scriptProperty) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void EntityTree::addCertifiedEntityOnServer(EntityItemPointer entity) {
|
||||
QString certID(entity->getCertificateID());
|
||||
EntityItemID existingEntityItemID;
|
||||
if (!certID.isEmpty()) {
|
||||
EntityItemID entityItemID = entity->getEntityItemID();
|
||||
QWriteLocker locker(&_entityCertificateIDMapLock);
|
||||
QList<EntityItemID>& entityList = _entityCertificateIDMap[certID]; // inserts it if needed.
|
||||
if (!entityList.isEmpty() && !entity->getCertificateType().contains(DOMAIN_UNLIMITED)) {
|
||||
existingEntityItemID = entityList.first(); // we will only care about the first, if any, below.
|
||||
entityList.removeOne(existingEntityItemID);
|
||||
}
|
||||
entityList << entityItemID; // adds to list within hash because entityList is a reference.
|
||||
qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID << "total" << entityList.size() << "entities.";
|
||||
}
|
||||
// Delete an already-existing entity from the tree if it has the same
|
||||
// CertificateID as the entity we're trying to add.
|
||||
if (!existingEntityItemID.isNull()) {
|
||||
qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID"
|
||||
<< existingEntityItemID << ". Deleting existing entity.";
|
||||
withWriteLock([&] {
|
||||
deleteEntity(existingEntityItemID, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTree::removeCertifiedEntityOnServer(EntityItemPointer entity) {
|
||||
QString certID = entity->getCertificateID();
|
||||
if (!certID.isEmpty()) {
|
||||
QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock);
|
||||
QList<EntityItemID>& entityList = _entityCertificateIDMap[certID];
|
||||
entityList.removeOne(entity->getEntityItemID());
|
||||
if (entityList.isEmpty()) {
|
||||
// hmmm, do we to make it be a hash instead of a list, so that this is faster if you stamp out 1000 of a domainUnlimited?
|
||||
_entityCertificateIDMap.remove(certID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTree::startDynamicDomainVerificationOnServer(float minimumAgeToRemove) {
|
||||
QReadLocker locker(&_entityCertificateIDMapLock);
|
||||
QHashIterator<QString, QList<EntityItemID>> i(_entityCertificateIDMap);
|
||||
qCDebug(entities) << _entityCertificateIDMap.size() << "certificates present.";
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
const auto& certificateID = i.key();
|
||||
const auto& entityIDs = i.value();
|
||||
if (entityIDs.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Examine each cert:
|
||||
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/location");
|
||||
QJsonObject request;
|
||||
request["certificate_id"] = certificateID;
|
||||
networkRequest.setUrl(requestURL);
|
||||
|
||||
QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
|
||||
|
||||
connect(networkReply, &QNetworkReply::finished, this, [this, entityIDs, networkReply, minimumAgeToRemove, &certificateID] {
|
||||
|
||||
QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
|
||||
jsonObject = jsonObject["data"].toObject();
|
||||
bool failure = networkReply->error() != QNetworkReply::NoError;
|
||||
auto failureReason = networkReply->error();
|
||||
networkReply->deleteLater();
|
||||
if (failure) {
|
||||
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << failureReason
|
||||
<< "; NOT deleting cert" << certificateID << "More info:" << jsonObject;
|
||||
return;
|
||||
}
|
||||
QString thisDomainID = DependencyManager::get<AddressManager>()->getDomainID().remove(QRegExp("\\{|\\}"));
|
||||
if (jsonObject["domain_id"].toString() == thisDomainID) {
|
||||
// Entity belongs here. Nothing to do.
|
||||
return;
|
||||
}
|
||||
// Entity does not belong here:
|
||||
QList<EntityItemID> retained;
|
||||
for (int i = 0; i < entityIDs.size(); i++) {
|
||||
EntityItemID entityID = entityIDs.at(i);
|
||||
EntityItemPointer entity = findEntityByEntityItemID(entityID);
|
||||
if (!entity) {
|
||||
qCDebug(entities) << "Entity undergoing dynamic domain verification is no longer available:" << entityID;
|
||||
continue;
|
||||
}
|
||||
if (entity->getAge() <= minimumAgeToRemove) {
|
||||
qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << entityID;
|
||||
retained << entityID;
|
||||
continue;
|
||||
}
|
||||
qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString()
|
||||
<< "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << entityID;
|
||||
withWriteLock([&] {
|
||||
deleteEntity(entityID, true);
|
||||
});
|
||||
}
|
||||
{
|
||||
QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock);
|
||||
if (retained.isEmpty()) {
|
||||
qCDebug(entities) << "Removed" << certificateID;
|
||||
_entityCertificateIDMap.remove(certificateID);
|
||||
} else {
|
||||
qCDebug(entities) << "Retained" << retained.size() << "young entities for" << certificateID;
|
||||
_entityCertificateIDMap[certificateID] = retained;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID) {
|
||||
QTimer* _challengeOwnershipTimeoutTimer = new QTimer(this);
|
||||
connect(this, &EntityTree::killChallengeOwnershipTimeoutTimer, this, [=](const QString& certID) {
|
||||
QReadLocker locker(&_entityCertificateIDMapLock);
|
||||
EntityItemID id = _entityCertificateIDMap.value(certID);
|
||||
connect(this, &EntityTree::killChallengeOwnershipTimeoutTimer, this, [=](const EntityItemID& id) {
|
||||
if (entityItemID == id && _challengeOwnershipTimeoutTimer) {
|
||||
_challengeOwnershipTimeoutTimer->stop();
|
||||
_challengeOwnershipTimeoutTimer->deleteLater();
|
||||
|
@ -1445,26 +1532,21 @@ void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID)
|
|||
_challengeOwnershipTimeoutTimer->start(5000);
|
||||
}
|
||||
|
||||
QByteArray EntityTree::computeNonce(const QString& certID, const QString ownerKey) {
|
||||
QByteArray EntityTree::computeNonce(const EntityItemID& entityID, const QString ownerKey) {
|
||||
QUuid nonce = QUuid::createUuid(); //random, 5-hex value, separated by "-"
|
||||
QByteArray nonceBytes = nonce.toByteArray();
|
||||
|
||||
QWriteLocker locker(&_certNonceMapLock);
|
||||
_certNonceMap.insert(certID, QPair<QUuid, QString>(nonce, ownerKey));
|
||||
QWriteLocker locker(&_entityNonceMapLock);
|
||||
_entityNonceMap.insert(entityID, QPair<QUuid, QString>(nonce, ownerKey));
|
||||
|
||||
return nonceBytes;
|
||||
}
|
||||
|
||||
bool EntityTree::verifyNonce(const QString& certID, const QString& nonce, EntityItemID& id) {
|
||||
{
|
||||
QReadLocker certIdMapLocker(&_entityCertificateIDMapLock);
|
||||
id = _entityCertificateIDMap.value(certID);
|
||||
}
|
||||
|
||||
bool EntityTree::verifyNonce(const EntityItemID& entityID, const QString& nonce) {
|
||||
QString actualNonce, key;
|
||||
{
|
||||
QWriteLocker locker(&_certNonceMapLock);
|
||||
QPair<QUuid, QString> sent = _certNonceMap.take(certID);
|
||||
QWriteLocker locker(&_entityNonceMapLock);
|
||||
QPair<QUuid, QString> sent = _entityNonceMap.take(entityID);
|
||||
actualNonce = sent.first.toString();
|
||||
key = sent.second;
|
||||
}
|
||||
|
@ -1474,9 +1556,9 @@ bool EntityTree::verifyNonce(const QString& certID, const QString& nonce, Entity
|
|||
bool verificationSuccess = EntityItemProperties::verifySignature(annotatedKey.toUtf8(), hashedActualNonce, QByteArray::fromBase64(nonce.toUtf8()));
|
||||
|
||||
if (verificationSuccess) {
|
||||
qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "succeeded.";
|
||||
qCDebug(entities) << "Ownership challenge for Entity ID" << entityID << "succeeded.";
|
||||
} else {
|
||||
qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed. Actual nonce:" << actualNonce <<
|
||||
qCDebug(entities) << "Ownership challenge for Entity ID" << entityID << "failed. Actual nonce:" << actualNonce <<
|
||||
"\nHashed actual nonce (digest):" << hashedActualNonce << "\nSent nonce (signature)" << nonce << "\nKey" << key;
|
||||
}
|
||||
|
||||
|
@ -1484,42 +1566,42 @@ bool EntityTree::verifyNonce(const QString& certID, const QString& nonce, Entity
|
|||
}
|
||||
|
||||
void EntityTree::processChallengeOwnershipRequestPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) {
|
||||
int certIDByteArraySize;
|
||||
int idByteArraySize;
|
||||
int textByteArraySize;
|
||||
int nodeToChallengeByteArraySize;
|
||||
|
||||
message.readPrimitive(&certIDByteArraySize);
|
||||
message.readPrimitive(&idByteArraySize);
|
||||
message.readPrimitive(&textByteArraySize);
|
||||
message.readPrimitive(&nodeToChallengeByteArraySize);
|
||||
|
||||
QByteArray certID(message.read(certIDByteArraySize));
|
||||
QByteArray id(message.read(idByteArraySize));
|
||||
QByteArray text(message.read(textByteArraySize));
|
||||
QByteArray nodeToChallenge(message.read(nodeToChallengeByteArraySize));
|
||||
|
||||
sendChallengeOwnershipRequestPacket(certID, text, nodeToChallenge, sourceNode);
|
||||
sendChallengeOwnershipRequestPacket(id, text, nodeToChallenge, sourceNode);
|
||||
}
|
||||
|
||||
void EntityTree::processChallengeOwnershipReplyPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
int certIDByteArraySize;
|
||||
int idByteArraySize;
|
||||
int textByteArraySize;
|
||||
int challengingNodeUUIDByteArraySize;
|
||||
|
||||
message.readPrimitive(&certIDByteArraySize);
|
||||
message.readPrimitive(&idByteArraySize);
|
||||
message.readPrimitive(&textByteArraySize);
|
||||
message.readPrimitive(&challengingNodeUUIDByteArraySize);
|
||||
|
||||
QByteArray certID(message.read(certIDByteArraySize));
|
||||
QByteArray id(message.read(idByteArraySize));
|
||||
QByteArray text(message.read(textByteArraySize));
|
||||
QUuid challengingNode = QUuid::fromRfc4122(message.read(challengingNodeUUIDByteArraySize));
|
||||
|
||||
auto challengeOwnershipReplyPacket = NLPacket::create(PacketType::ChallengeOwnershipReply,
|
||||
certIDByteArraySize + text.length() + 2 * sizeof(int),
|
||||
idByteArraySize + text.length() + 2 * sizeof(int),
|
||||
true);
|
||||
challengeOwnershipReplyPacket->writePrimitive(certIDByteArraySize);
|
||||
challengeOwnershipReplyPacket->writePrimitive(idByteArraySize);
|
||||
challengeOwnershipReplyPacket->writePrimitive(text.length());
|
||||
challengeOwnershipReplyPacket->write(certID);
|
||||
challengeOwnershipReplyPacket->write(id);
|
||||
challengeOwnershipReplyPacket->write(text);
|
||||
|
||||
nodeList->sendPacket(std::move(challengeOwnershipReplyPacket), *(nodeList->nodeWithUUID(challengingNode)));
|
||||
|
@ -1529,7 +1611,7 @@ void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QStri
|
|||
// 1. Obtain a nonce
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
QByteArray text = computeNonce(certID, ownerKey);
|
||||
QByteArray text = computeNonce(entityItemID, ownerKey);
|
||||
|
||||
if (text == "") {
|
||||
qCDebug(entities) << "CRITICAL ERROR: Couldn't compute nonce. Deleting entity...";
|
||||
|
@ -1539,14 +1621,14 @@ void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QStri
|
|||
} else {
|
||||
qCDebug(entities) << "Challenging ownership of Cert ID" << certID;
|
||||
// 2. Send the nonce to the rezzing avatar's node
|
||||
QByteArray certIDByteArray = certID.toUtf8();
|
||||
int certIDByteArraySize = certIDByteArray.size();
|
||||
QByteArray idByteArray = entityItemID.toByteArray();
|
||||
int idByteArraySize = idByteArray.size();
|
||||
auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership,
|
||||
certIDByteArraySize + text.length() + 2 * sizeof(int),
|
||||
idByteArraySize + text.length() + 2 * sizeof(int),
|
||||
true);
|
||||
challengeOwnershipPacket->writePrimitive(certIDByteArraySize);
|
||||
challengeOwnershipPacket->writePrimitive(idByteArraySize);
|
||||
challengeOwnershipPacket->writePrimitive(text.length());
|
||||
challengeOwnershipPacket->write(certIDByteArray);
|
||||
challengeOwnershipPacket->write(idByteArray);
|
||||
challengeOwnershipPacket->write(text);
|
||||
nodeList->sendPacket(std::move(challengeOwnershipPacket), *senderNode);
|
||||
|
||||
|
@ -1560,24 +1642,24 @@ void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QStri
|
|||
}
|
||||
}
|
||||
|
||||
void EntityTree::sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode) {
|
||||
void EntityTree::sendChallengeOwnershipRequestPacket(const QByteArray& id, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
// In this case, Client A is challenging Client B. Client A is inspecting a certified entity that it wants
|
||||
// to make sure belongs to Avatar B.
|
||||
QByteArray senderNodeUUID = senderNode->getUUID().toRfc4122();
|
||||
|
||||
int certIDByteArraySize = certID.length();
|
||||
int idByteArraySize = id.length();
|
||||
int TextByteArraySize = text.length();
|
||||
int senderNodeUUIDSize = senderNodeUUID.length();
|
||||
|
||||
auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest,
|
||||
certIDByteArraySize + TextByteArraySize + senderNodeUUIDSize + 3 * sizeof(int),
|
||||
idByteArraySize + TextByteArraySize + senderNodeUUIDSize + 3 * sizeof(int),
|
||||
true);
|
||||
challengeOwnershipPacket->writePrimitive(certIDByteArraySize);
|
||||
challengeOwnershipPacket->writePrimitive(idByteArraySize);
|
||||
challengeOwnershipPacket->writePrimitive(TextByteArraySize);
|
||||
challengeOwnershipPacket->writePrimitive(senderNodeUUIDSize);
|
||||
challengeOwnershipPacket->write(certID);
|
||||
challengeOwnershipPacket->write(id);
|
||||
challengeOwnershipPacket->write(text);
|
||||
challengeOwnershipPacket->write(senderNodeUUID);
|
||||
|
||||
|
@ -1636,22 +1718,21 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt
|
|||
}
|
||||
|
||||
void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) {
|
||||
int certIDByteArraySize;
|
||||
int idByteArraySize;
|
||||
int textByteArraySize;
|
||||
|
||||
message.readPrimitive(&certIDByteArraySize);
|
||||
message.readPrimitive(&idByteArraySize);
|
||||
message.readPrimitive(&textByteArraySize);
|
||||
|
||||
QString certID(message.read(certIDByteArraySize));
|
||||
EntityItemID id(message.read(idByteArraySize));
|
||||
QString text(message.read(textByteArraySize));
|
||||
|
||||
emit killChallengeOwnershipTimeoutTimer(certID);
|
||||
emit killChallengeOwnershipTimeoutTimer(id);
|
||||
|
||||
EntityItemID id;
|
||||
if (!verifyNonce(certID, text, id)) {
|
||||
if (!id.isNull()) {
|
||||
if (!verifyNonce(id, text)) {
|
||||
withWriteLock([&] {
|
||||
deleteEntity(id, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -157,11 +157,6 @@ public:
|
|||
return _recentlyDeletedEntityItemIDs;
|
||||
}
|
||||
|
||||
QHash<QString, EntityItemID> getEntityCertificateIDMap() const {
|
||||
QReadLocker locker(&_entityCertificateIDMapLock);
|
||||
return _entityCertificateIDMap;
|
||||
}
|
||||
|
||||
void forgetEntitiesDeletedBefore(quint64 sinceTime);
|
||||
|
||||
int processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode);
|
||||
|
@ -252,8 +247,8 @@ public:
|
|||
|
||||
static const float DEFAULT_MAX_TMP_ENTITY_LIFETIME;
|
||||
|
||||
QByteArray computeNonce(const QString& certID, const QString ownerKey);
|
||||
bool verifyNonce(const QString& certID, const QString& nonce, EntityItemID& id);
|
||||
QByteArray computeNonce(const EntityItemID& entityID, const QString ownerKey);
|
||||
bool verifyNonce(const EntityItemID& entityID, const QString& nonce);
|
||||
|
||||
QUuid getMyAvatarSessionUUID() { return _myAvatar ? _myAvatar->getSessionUUID() : QUuid(); }
|
||||
void setMyAvatar(std::shared_ptr<AvatarData> myAvatar) { _myAvatar = myAvatar; }
|
||||
|
@ -279,6 +274,7 @@ public:
|
|||
|
||||
void updateEntityQueryAACube(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender,
|
||||
bool force, bool tellServer);
|
||||
void startDynamicDomainVerificationOnServer(float minimumAgeToRemove);
|
||||
|
||||
signals:
|
||||
void deletingEntity(const EntityItemID& entityID);
|
||||
|
@ -290,7 +286,7 @@ signals:
|
|||
void entityServerScriptChanging(const EntityItemID& entityItemID, const bool reload);
|
||||
void newCollisionSoundURL(const QUrl& url, const EntityItemID& entityID);
|
||||
void clearingEntities();
|
||||
void killChallengeOwnershipTimeoutTimer(const QString& certID);
|
||||
void killChallengeOwnershipTimeoutTimer(const EntityItemID& certID);
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -327,10 +323,10 @@ protected:
|
|||
QHash<EntityItemID, EntityItemPointer> _entityMap;
|
||||
|
||||
mutable QReadWriteLock _entityCertificateIDMapLock;
|
||||
QHash<QString, EntityItemID> _entityCertificateIDMap;
|
||||
QHash<QString, QList<EntityItemID>> _entityCertificateIDMap;
|
||||
|
||||
mutable QReadWriteLock _certNonceMapLock;
|
||||
QHash<QString, QPair<QUuid, QString>> _certNonceMap;
|
||||
mutable QReadWriteLock _entityNonceMapLock;
|
||||
QHash<EntityItemID, QPair<QUuid, QString>> _entityNonceMap;
|
||||
|
||||
EntitySimulationPointer _simulation;
|
||||
|
||||
|
@ -377,8 +373,10 @@ protected:
|
|||
Q_INVOKABLE void startChallengeOwnershipTimer(const EntityItemID& entityItemID);
|
||||
|
||||
private:
|
||||
void addCertifiedEntityOnServer(EntityItemPointer entity);
|
||||
void removeCertifiedEntityOnServer(EntityItemPointer entity);
|
||||
void sendChallengeOwnershipPacket(const QString& certID, const QString& ownerKey, const EntityItemID& entityItemID, const SharedNodePointer& senderNode);
|
||||
void sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode);
|
||||
void sendChallengeOwnershipRequestPacket(const QByteArray& id, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode);
|
||||
void validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode);
|
||||
|
||||
std::shared_ptr<AvatarData> _myAvatar{ nullptr };
|
||||
|
|
Loading…
Reference in a new issue