Forráskód Böngészése

Add test for deleting encrypted files

Signed-off-by: Claudio Cambra <claudio.cambra@nextcloud.com>
Claudio Cambra 3 éve
szülő
commit
12b2673ac2
4 módosított fájl, 115 hozzáadás és 3 törlés
  1. 2 2
      src/gui/folder.cpp
  2. 14 0
      test/syncenginetestutils.cpp
  3. 5 0
      test/syncenginetestutils.h
  4. 94 1
      test/testfolderman.cpp

+ 2 - 2
src/gui/folder.cpp

@@ -1353,7 +1353,7 @@ void Folder::removeLocalE2eFiles()
     const auto currentSyncPaused = syncPaused();
     setSyncPaused(true);
 
-    qCDebug(lcFolder) << "About to remove: " << e2eFiles;
+    qCInfo(lcFolder) << "About to remove: " << e2eFiles;
 
     for (const auto &e2eFilePath : qAsConst(e2eFiles)) {
         if (!_journal.deleteFileRecord(e2eFilePath, true)) {
@@ -1362,7 +1362,7 @@ void Folder::removeLocalE2eFiles()
             continue;
         }
 
-        qCDebug(lcFolder) << "Removing local copy of" << e2eFilePath;
+        qCInfo(lcFolder) << "Removing local copy of" << e2eFilePath;
 
         const auto fullPath = QString(path() + e2eFilePath);
         const QFileInfo pathInfo(fullPath);

+ 14 - 0
test/syncenginetestutils.cpp

@@ -108,6 +108,10 @@ void DiskFileModifier::modifyLockState([[maybe_unused]] const QString &relativeP
 {
 }
 
+void DiskFileModifier::setE2EE([[maybe_unused]] const QString &relativePath, [[maybe_unused]] const bool enable)
+{
+}
+
 FileInfo FileInfo::A12_B12_C12_S12()
 {
     FileInfo fi { QString {}, {
@@ -213,6 +217,13 @@ void FileInfo::modifyLockState(const QString &relativePath, LockState lockState,
     file->lockTimeout = lockTimeout;
 }
 
+void FileInfo::setE2EE(const QString &relativePath, const bool enable)
+{
+    FileInfo *file = findInvalidatingEtags(relativePath);
+    Q_ASSERT(file);
+    file->isEncrypted = enable;
+}
+
 FileInfo *FileInfo::find(PathComponents pathComponents, const bool invalidateEtags)
 {
     if (pathComponents.isEmpty()) {
@@ -366,6 +377,7 @@ FakePropfindReply::FakePropfindReply(FileInfo &remoteRootFileInfo, QNetworkAcces
         xml.writeTextElement(ncUri, QStringLiteral("lock-owner-editor"), fileInfo.lockOwnerId);
         xml.writeTextElement(ncUri, QStringLiteral("lock-time"), QString::number(fileInfo.lockTime));
         xml.writeTextElement(ncUri, QStringLiteral("lock-timeout"), QString::number(fileInfo.lockTimeout));
+        xml.writeTextElement(ncUri, QStringLiteral("is-encrypted"), fileInfo.isEncrypted ? QString::number(1) : QString::number(0));
         buffer.write(fileInfo.extraDavProperties);
         xml.writeEndElement(); // prop
         xml.writeTextElement(davUri, QStringLiteral("status"), QStringLiteral("HTTP/1.1 200 OK"));
@@ -1000,11 +1012,13 @@ QNetworkReply *FakeQNAM::createRequest(QNetworkAccessManager::Operation op, cons
     newRequest.setRawHeader("X-Request-ID", OCC::AccessManager::generateRequestId());
     auto contentType = request.header(QNetworkRequest::ContentTypeHeader).toString();
     if (_override) {
+        qDebug() << "Using override!";
         if (auto _reply = _override(op, newRequest, outgoingData)) {
             reply = _reply;
         }
     }
     if (!reply) {
+        qDebug() << newRequest.url();
         reply = overrideReplyWithError(getFilePathFromUrl(newRequest.url()), op, newRequest);
     }
     if (!reply) {

+ 5 - 0
test/syncenginetestutils.h

@@ -90,6 +90,7 @@ public:
     virtual void rename(const QString &relativePath, const QString &relativeDestinationDirectory) = 0;
     virtual void setModTime(const QString &relativePath, const QDateTime &modTime) = 0;
     virtual void modifyLockState(const QString &relativePath, LockState lockState, int lockType, const QString &lockOwner, const QString &lockOwnerId, const QString &lockEditorId, quint64 lockTime, quint64 lockTimeout) = 0;
+    virtual void setE2EE(const QString &relativepath, const bool enabled) = 0;
 };
 
 class DiskFileModifier : public FileModifier
@@ -106,6 +107,7 @@ public:
     void rename(const QString &from, const QString &to) override;
     void setModTime(const QString &relativePath, const QDateTime &modTime) override;
     void modifyLockState(const QString &relativePath, LockState lockState, int lockType, const QString &lockOwner, const QString &lockOwnerId, const QString &lockEditorId, quint64 lockTime, quint64 lockTimeout) override;
+    void setE2EE(const QString &relativepath, const bool enabled) override;
 };
 
 class FileInfo : public FileModifier
@@ -140,6 +142,8 @@ public:
 
     void modifyLockState(const QString &relativePath, LockState lockState, int lockType, const QString &lockOwner, const QString &lockOwnerId, const QString &lockEditorId, quint64 lockTime, quint64 lockTimeout) override;
 
+    void setE2EE(const QString &relativepath, const bool enabled) override;
+
     FileInfo *find(PathComponents pathComponents, const bool invalidateEtags = false);
 
     FileInfo *createDir(const QString &relativePath);
@@ -180,6 +184,7 @@ public:
     QString lockEditorId;
     quint64 lockTime = 0;
     quint64 lockTimeout = 0;
+    bool isEncrypted = false;
 
     // Sorted by name to be able to compare trees
     QMap<QString, FileInfo> children;

+ 94 - 1
test/testfolderman.cpp

@@ -9,6 +9,7 @@
 #include <QTemporaryDir>
 #include <QtTest>
 
+#include "QtTest/qtestcase.h"
 #include "common/utility.h"
 #include "folderman.h"
 #include "account.h"
@@ -20,6 +21,14 @@
 
 using namespace OCC;
 
+bool itemDidCompleteSuccessfully(const ItemCompletedSpy &spy, const QString &path)
+{
+    if (auto item = spy.findItem(path)) {
+        return item->_status == SyncFileItem::Success;
+    }
+    return false;
+}
+
 class TestFolderMan: public QObject
 {
     Q_OBJECT
@@ -311,7 +320,91 @@ private slots:
         QCOMPARE(folderman->findGoodPathForNewSyncFolder(dirPath + "/ownCloud2", url),
             QString(dirPath + "/ownCloud22"));
     }
+
+    void testDeleteEncryptedFiles()
+    {
+        FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
+        QCOMPARE(fakeFolder.currentLocalState().children.count(), 4);
+
+        ItemCompletedSpy completeSpy(fakeFolder);
+        fakeFolder.localModifier().mkdir("encrypted");
+        fakeFolder.localModifier().setE2EE("encrypted", true);
+        fakeFolder.remoteModifier().mkdir("encrypted");
+        fakeFolder.remoteModifier().setE2EE("encrypted", true);
+
+        const auto fakeFileInfo = fakeFolder.remoteModifier().find("encrypted");
+        QVERIFY(fakeFileInfo);
+        QCOMPARE(fakeFolder.currentLocalState().children.count(), 5);
+
+        const auto fakeFileId = fakeFileInfo->fileId;
+        const auto fakeQnam = new FakeQNAM({});
+        // Let's avoid the null filename assert in the default FakeQNAM request creation
+        const auto fakeQnamOverride = [this, fakeFileId](const QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *device) {
+            Q_UNUSED(device)
+            QNetworkReply *reply = nullptr;
+
+            const auto reqUrl = req.url();
+            const auto reqRawPath = reqUrl.path();
+            const auto reqPath = reqRawPath.startsWith("/owncloud/") ? reqRawPath.mid(10) : reqRawPath;
+
+            if (reqPath.startsWith(QStringLiteral("ocs/v2.php/apps/end_to_end_encryption/api/v1/meta-data/"))) {
+                const auto splitUrlPath = reqPath.split('/');
+                const auto fileId = splitUrlPath.last();
+
+                const QUrlQuery urlQuery(req.url());
+                const auto formatParam = urlQuery.queryItemValue(QStringLiteral("format"));
+
+                if(fileId == fakeFileId && formatParam == QStringLiteral("json")) {
+                    reply = new FakePayloadReply(op, req, QJsonDocument().toJson(), this);
+                }
+            }
+            return reply;
+        };
+        fakeFolder.setServerOverride(fakeQnamOverride);
+        fakeQnam->setOverride(fakeQnamOverride);
+
+        const auto account = Account::create();
+        const auto capabilities = QVariantMap {
+            {QStringLiteral("end-to-end-encryption"), QVariantMap {
+                {QStringLiteral("enabled"), true},
+                {QStringLiteral("api-version"), QString::number(2.0)},
+            }},
+        };
+        account->setCapabilities(capabilities);
+        account->setCredentials(new FakeCredentials{fakeQnam});
+        account->setUrl(QUrl(("owncloud://somehost/owncloud")));
+        const auto accountState = new FakeAccountState(account);
+        QVERIFY(accountState->isConnected());
+
+        QVERIFY(fakeFolder.syncOnce());
+        QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
+
+        const auto folder = FolderMan::instance()->addFolder(accountState, folderDefinition(fakeFolder.localPath()));
+        QVERIFY(folder);
+        QSignalSpy folderSyncDone(folder, &Folder::syncFinished);
+
+        QDir dir(folder->path() + QStringLiteral("encrypted"));
+        QVERIFY(dir.exists());
+        QVERIFY(fakeFolder.remoteModifier().find("encrypted"));
+        QVERIFY(fakeFolder.currentLocalState().find("encrypted"));
+        QCOMPARE(fakeFolder.currentLocalState().children.count(), 5);
+
+        // Rather than go through the pain of trying to replicate the E2EE response from
+        // the server, let's just manually set the encryption bool in the folder journal
+        SyncJournalFileRecord rec;
+        QVERIFY(folder->journalDb()->getFileRecord(QStringLiteral("encrypted"), &rec));
+        rec._isE2eEncrypted = true;
+        rec._path = QStringLiteral("encrypted").toUtf8();
+        QVERIFY(folder->journalDb()->setFileRecord(rec));
+        FolderMan::instance()->removeE2eFiles(account);
+
+        QVERIFY(folderSyncDone.wait());
+
+        QVERIFY(fakeFolder.currentRemoteState().find("encrypted"));
+        QVERIFY(!fakeFolder.currentLocalState().find("encrypted"));
+        QCOMPARE(fakeFolder.currentLocalState().children.count(), 4);
+    }
 };
 
-QTEST_GUILESS_MAIN(TestFolderMan)
+QTEST_MAIN(TestFolderMan)
 #include "testfolderman.moc"