testsecurefiledrop.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. /*
  2. * This software is in the public domain, furnished "as is", without technical
  3. * support, and with no warranty, express or implied, as to its usefulness for
  4. * any purpose.
  5. *
  6. */
  7. #include "updatefiledropmetadata.h"
  8. #include "syncengine.h"
  9. #include "syncenginetestutils.h"
  10. #include "testhelper.h"
  11. #include "owncloudpropagator_p.h"
  12. #include "propagatorjobs.h"
  13. #include "clientsideencryption.h"
  14. #include <QtTest>
  15. namespace
  16. {
  17. constexpr auto fakeE2eeFolderName = "fake_e2ee_folder";
  18. const QString fakeE2eeFolderPath = QStringLiteral("/") + fakeE2eeFolderName;
  19. };
  20. using namespace OCC;
  21. class TestSecureFileDrop : public QObject
  22. {
  23. Q_OBJECT
  24. FakeFolder _fakeFolder{FileInfo()};
  25. QSharedPointer<OwncloudPropagator> _propagator;
  26. QScopedPointer<FolderMetadata> _parsedMetadataWithFileDrop;
  27. QScopedPointer<FolderMetadata> _parsedMetadataAfterProcessingFileDrop;
  28. int _lockCallsCount = 0;
  29. int _unlockCallsCount = 0;
  30. int _propFindCallsCount = 0;
  31. int _getMetadataCallsCount = 0;
  32. int _putMetadataCallsCount = 0;
  33. private slots:
  34. void initTestCase()
  35. {
  36. _fakeFolder.remoteModifier().mkdir(fakeE2eeFolderName);
  37. _fakeFolder.remoteModifier().insert(fakeE2eeFolderName + QStringLiteral("/") + QStringLiteral("fake_e2ee_file"), 100);
  38. {
  39. QFile e2eTestFakeCert(QStringLiteral("e2etestsfakecert.pem"));
  40. if (e2eTestFakeCert.open(QFile::ReadOnly)) {
  41. _fakeFolder.syncEngine().account()->e2e()->_certificate = QSslCertificate(e2eTestFakeCert.readAll());
  42. e2eTestFakeCert.close();
  43. }
  44. }
  45. {
  46. QFile e2etestsfakecertpublickey(QStringLiteral("e2etestsfakecertpublickey.pem"));
  47. if (e2etestsfakecertpublickey.open(QFile::ReadOnly)) {
  48. _fakeFolder.syncEngine().account()->e2e()->_publicKey = QSslKey(e2etestsfakecertpublickey.readAll(), QSsl::KeyAlgorithm::Rsa, QSsl::EncodingFormat::Pem, QSsl::KeyType::PublicKey);
  49. e2etestsfakecertpublickey.close();
  50. }
  51. }
  52. {
  53. QFile e2etestsfakecertprivatekey(QStringLiteral("e2etestsfakecertprivatekey.pem"));
  54. if (e2etestsfakecertprivatekey.open(QFile::ReadOnly)) {
  55. _fakeFolder.syncEngine().account()->e2e()->_privateKey = e2etestsfakecertprivatekey.readAll();
  56. e2etestsfakecertprivatekey.close();
  57. }
  58. }
  59. _fakeFolder.setServerOverride([this](QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *device) {
  60. Q_UNUSED(device);
  61. QNetworkReply *reply = nullptr;
  62. const auto path = req.url().path();
  63. if (path.contains(QStringLiteral("/end_to_end_encryption/api/v1/lock/"))) {
  64. if (op == QNetworkAccessManager::DeleteOperation) {
  65. reply = new FakePayloadReply(op, req, {}, nullptr);
  66. ++_unlockCallsCount;
  67. } else if (op == QNetworkAccessManager::PostOperation) {
  68. QFile fakeJsonReplyFile(QStringLiteral("fake2eelocksucceeded.json"));
  69. if (fakeJsonReplyFile.open(QFile::ReadOnly)) {
  70. const auto jsonDoc = QJsonDocument::fromJson(fakeJsonReplyFile.readAll());
  71. reply = new FakePayloadReply(op, req, jsonDoc.toJson(), nullptr);
  72. ++_lockCallsCount;
  73. } else {
  74. qCritical() << "Could not open fake JSON file!";
  75. reply = new FakePayloadReply(op, req, {}, nullptr);
  76. }
  77. }
  78. } else if (path.contains(QStringLiteral("/end_to_end_encryption/api/v1/meta-data/"))) {
  79. if (op == QNetworkAccessManager::GetOperation) {
  80. QFile fakeJsonReplyFile(QStringLiteral("fakefiledrope2eefoldermetadata.json"));
  81. if (fakeJsonReplyFile.open(QFile::ReadOnly)) {
  82. const auto jsonDoc = QJsonDocument::fromJson(fakeJsonReplyFile.readAll());
  83. _parsedMetadataWithFileDrop.reset(new FolderMetadata(_fakeFolder.syncEngine().account(), FolderMetadata::RequiredMetadataVersion::Version1_2, jsonDoc.toJson()));
  84. _parsedMetadataAfterProcessingFileDrop.reset(new FolderMetadata(_fakeFolder.syncEngine().account(), FolderMetadata::RequiredMetadataVersion::Version1_2, jsonDoc.toJson()));
  85. [[maybe_unused]] const auto result = _parsedMetadataAfterProcessingFileDrop->moveFromFileDropToFiles();
  86. reply = new FakePayloadReply(op, req, jsonDoc.toJson(), nullptr);
  87. ++_getMetadataCallsCount;
  88. } else {
  89. qCritical() << "Could not open fake JSON file!";
  90. reply = new FakePayloadReply(op, req, {}, nullptr);
  91. }
  92. } else if (op == QNetworkAccessManager::PutOperation) {
  93. reply = new FakePayloadReply(op, req, {}, nullptr);
  94. ++_putMetadataCallsCount;
  95. }
  96. } else if (req.attribute(QNetworkRequest::CustomVerbAttribute) == QStringLiteral("PROPFIND") && path.endsWith(fakeE2eeFolderPath)) {
  97. auto fileState = _fakeFolder.currentRemoteState();
  98. reply = new FakePropfindReply(fileState, op, req, nullptr);
  99. ++_propFindCallsCount;
  100. }
  101. return reply;
  102. });
  103. auto transProgress = connect(&_fakeFolder.syncEngine(), &SyncEngine::transmissionProgress, [&](const ProgressInfo &pi) {
  104. Q_UNUSED(pi);
  105. _propagator = _fakeFolder.syncEngine().getPropagator();
  106. });
  107. QVERIFY(_fakeFolder.syncOnce());
  108. disconnect(transProgress);
  109. };
  110. void testUpdateFileDropMetadata()
  111. {
  112. const auto updateFileDropMetadataJob = new UpdateFileDropMetadataJob(_propagator.data(), fakeE2eeFolderPath);
  113. connect(updateFileDropMetadataJob, &UpdateFileDropMetadataJob::fileDropMetadataParsedAndAdjusted, this, [this](const FolderMetadata *const metadata) {
  114. if (!metadata || metadata->files().isEmpty() || metadata->fileDrop().isEmpty()) {
  115. return;
  116. }
  117. if (_parsedMetadataAfterProcessingFileDrop->files().size() != metadata->files().size()) {
  118. return;
  119. }
  120. if (_parsedMetadataAfterProcessingFileDrop->fileDrop() != metadata->fileDrop()) {
  121. return;
  122. }
  123. bool isAnyFileDropFileMissing = false;
  124. for (const auto &key : metadata->fileDrop().keys()) {
  125. if (std::find_if(metadata->files().constBegin(), metadata->files().constEnd(), [&key](const EncryptedFile &encryptedFile) {
  126. return encryptedFile.encryptedFilename == key;
  127. }) == metadata->files().constEnd()) {
  128. isAnyFileDropFileMissing = true;
  129. }
  130. }
  131. if (!isAnyFileDropFileMissing) {
  132. emit fileDropMetadataParsedAndAdjusted();
  133. }
  134. });
  135. QSignalSpy updateFileDropMetadataJobSpy(updateFileDropMetadataJob, &UpdateFileDropMetadataJob::finished);
  136. QSignalSpy fileDropMetadataParsedAndAdjustedSpy(this, &TestSecureFileDrop::fileDropMetadataParsedAndAdjusted);
  137. QVERIFY(updateFileDropMetadataJob->scheduleSelfOrChild());
  138. QVERIFY(updateFileDropMetadataJobSpy.wait(3000));
  139. QVERIFY(_parsedMetadataWithFileDrop);
  140. QVERIFY(_parsedMetadataWithFileDrop->isFileDropPresent());
  141. QVERIFY(_parsedMetadataAfterProcessingFileDrop);
  142. QVERIFY(_parsedMetadataAfterProcessingFileDrop->files().size() != _parsedMetadataWithFileDrop->files().size());
  143. QVERIFY(!updateFileDropMetadataJobSpy.isEmpty());
  144. QVERIFY(!updateFileDropMetadataJobSpy.at(0).isEmpty());
  145. QCOMPARE(updateFileDropMetadataJobSpy.at(0).first().toInt(), SyncFileItem::Status::Success);
  146. QVERIFY(!fileDropMetadataParsedAndAdjustedSpy.isEmpty());
  147. QCOMPARE(_lockCallsCount, 1);
  148. QCOMPARE(_unlockCallsCount, 1);
  149. QCOMPARE(_propFindCallsCount, 2);
  150. QCOMPARE(_getMetadataCallsCount, 1);
  151. QCOMPARE(_putMetadataCallsCount, 1);
  152. updateFileDropMetadataJob->deleteLater();
  153. }
  154. signals:
  155. void fileDropMetadataParsedAndAdjusted();
  156. };
  157. QTEST_GUILESS_MAIN(TestSecureFileDrop)
  158. #include "testsecurefiledrop.moc"