diff --git a/src/gui/folderman.cpp b/src/gui/folderman.cpp index ae388eb8d412a..ac1d755605876 100644 --- a/src/gui/folderman.cpp +++ b/src/gui/folderman.cpp @@ -1789,27 +1789,7 @@ void FolderMan::leaveShare(const QString &localFile) SyncJournalFileRecord rec; if (folder->journalDb()->getFileRecord(filePathRelative, &rec) && rec.isValid() && rec.isE2eEncrypted()) { - - if (_removeE2eeShareJob) { - _removeE2eeShareJob->deleteLater(); - } - - _removeE2eeShareJob = new UpdateE2eeFolderUsersMetadataJob(folder->accountState()->account(), - folder->journalDb(), - folder->remotePath(), - UpdateE2eeFolderUsersMetadataJob::Remove, - folder->remotePathTrailingSlash() + filePathRelative, - folder->accountState()->account()->davUser()); - _removeE2eeShareJob->setParent(this); - _removeE2eeShareJob->start(true); - connect(_removeE2eeShareJob, &UpdateE2eeFolderUsersMetadataJob::finished, this, [localFileNoTrailingSlash, this](int code, const QString &message) { - if (code != 200) { - qCWarning(lcFolderMan) << "Could not remove share from E2EE folder's metadata!" << code << message; - return; - } - slotLeaveShare(localFileNoTrailingSlash, _removeE2eeShareJob->folderToken()); - }); - + qCWarning(lcFolderMan) << "You cannot remove yourself from an encrypted share"; return; } slotLeaveShare(localFileNoTrailingSlash); @@ -1832,13 +1812,6 @@ void FolderMan::slotLeaveShare(const QString &localFile, const QByteArray &folde connect(leaveShareJob, &SimpleApiJob::resultReceived, this, [this, folder, localFile](int statusCode) { qCDebug(lcFolderMan) << "slotLeaveShare callback statusCode" << statusCode; Q_UNUSED(statusCode); - if (_removeE2eeShareJob) { - _removeE2eeShareJob->unlockFolder(EncryptedFolderMetadataHandler::UnlockFolderWithResult::Success); - connect(_removeE2eeShareJob.data(), &UpdateE2eeFolderUsersMetadataJob::folderUnlocked, this, [this, folder] { - scheduleFolder(folder); - }); - return; - } scheduleFolder(folder); }); leaveShareJob->start(); diff --git a/src/gui/folderman.h b/src/gui/folderman.h index 0b91633371010..f4ef0d03f2524 100644 --- a/src/gui/folderman.h +++ b/src/gui/folderman.h @@ -404,8 +404,6 @@ private slots: NavigationPaneHelper _navigationPaneHelper; #endif - QPointer _removeE2eeShareJob; - bool _appRestartRequired = false; static FolderMan *_instance; diff --git a/src/gui/socketapi/socketapi.cpp b/src/gui/socketapi/socketapi.cpp index 39949ea206f8f..ce8ccda9ee5eb 100644 --- a/src/gui/socketapi/socketapi.cpp +++ b/src/gui/socketapi/socketapi.cpp @@ -1143,7 +1143,7 @@ void SocketApi::sendSharingContextMenuOptions(const FileData &fileData, SocketLi if (!capabilities.shareAPI() || !(theme->userGroupSharing() || (theme->linkSharing() && capabilities.sharePublicLink()))) return; - if (record._isShared && !record._sharedByMe) { + if (record._isShared && !record._sharedByMe && itemEncryptionFlag == SharingContextItemEncryptedFlag::NotEncryptedItem) { listener->sendMessage(QLatin1String("MENU_ITEM:LEAVESHARE") + flagString + tr("Leave this share")); } diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp index 3e196602408d9..1b459ee3267b0 100644 --- a/src/libsync/discovery.cpp +++ b/src/libsync/discovery.cpp @@ -743,8 +743,13 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo(const SyncFileItemPtr &it Q_ASSERT(item->_e2eEncryptionStatus != SyncFileItem::EncryptionStatus::NotEncrypted); } item->_encryptedFileName = [=, this] { + auto result = QString{}; + + if (item->_e2eEncryptionStatus == SyncFileItem::EncryptionStatus::NotEncrypted) { + return result; + } if (serverEntry.e2eMangledName.isEmpty()) { - return QString(); + return result; } Q_ASSERT(_discoveryData->_remoteFolder.startsWith('/')); @@ -752,7 +757,8 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo(const SyncFileItemPtr &it const auto rootPath = _discoveryData->_remoteFolder.mid(1); Q_ASSERT(serverEntry.e2eMangledName.startsWith(rootPath)); - return serverEntry.e2eMangledName.mid(rootPath.length()); + result = serverEntry.e2eMangledName.mid(rootPath.length()); + return result; }(); item->_locked = serverEntry.locked; item->_lockOwnerDisplayName = serverEntry.lockOwnerDisplayName; diff --git a/src/libsync/discoveryphase.cpp b/src/libsync/discoveryphase.cpp index 602ded62931ba..ba5230aed31a2 100644 --- a/src/libsync/discoveryphase.cpp +++ b/src/libsync/discoveryphase.cpp @@ -671,11 +671,17 @@ void DiscoverySingleDirectoryJob::metadataReceived(const QJsonDocument &json, in break; } + const auto rootEncryptedFolderInfo = RootEncryptedFolderInfo{Utility::fullRemotePathToRemoteSyncRootRelative(topLevelFolderPath, _remoteRootFolderPath)}; + + const auto folderType = _topLevelE2eeFolderPaths.contains(_subPath) ? FolderMetadata::FolderType::Root : FolderMetadata::FolderType::Nested; + Q_ASSERT((folderType == FolderMetadata::FolderType::Root) == (rootEncryptedFolderInfo.path == QStringLiteral("/"))); + const auto e2EeFolderMetadata = new FolderMetadata(_account, _remoteRootFolderPath, jsonMetadata, - RootEncryptedFolderInfo(Utility::fullRemotePathToRemoteSyncRootRelative(topLevelFolderPath, _remoteRootFolderPath)), - job->signature()); + rootEncryptedFolderInfo, + job->signature(), + folderType); connect(e2EeFolderMetadata, &FolderMetadata::setupComplete, this, [this, e2EeFolderMetadata] { e2EeFolderMetadata->deleteLater(); if (!e2EeFolderMetadata->isValid()) { diff --git a/src/libsync/encryptedfoldermetadatahandler.cpp b/src/libsync/encryptedfoldermetadatahandler.cpp index 6fa3fe52aef50..956d485b6691c 100644 --- a/src/libsync/encryptedfoldermetadatahandler.cpp +++ b/src/libsync/encryptedfoldermetadatahandler.cpp @@ -14,6 +14,8 @@ #include #include +using namespace Qt::StringLiterals; + namespace OCC { Q_LOGGING_CATEGORY(lcFetchAndUploadE2eeFolderMetadataJob, "nextcloud.sync.propagator.encryptedfoldermetadatahandler", QtInfoMsg) @@ -183,7 +185,12 @@ void EncryptedFolderMetadataHandler::slotMetadataReceived(const QJsonDocument &j const auto rawMetadata = statusCode == 404 ? QByteArray{} : json.toJson(QJsonDocument::Compact); - const auto metadata(QSharedPointer::create(_account, _remoteFolderRoot, rawMetadata, _rootEncryptedFolderInfo, job->signature())); + const auto metadata(QSharedPointer::create(_account, + _remoteFolderRoot, + rawMetadata, + _rootEncryptedFolderInfo, + job->signature(), + (_rootEncryptedFolderInfo.path == u"/"_s ? FolderMetadata::FolderType::Root : FolderMetadata::FolderType::Nested))); connect(metadata.data(), &FolderMetadata::setupComplete, this, [this, metadata] { if (!metadata->isValid()) { qCWarning(lcFetchAndUploadE2eeFolderMetadataJob) << "Error parsing or decrypting metadata for folder" << _folderFullRemotePath; diff --git a/src/libsync/encryptfolderjob.cpp b/src/libsync/encryptfolderjob.cpp index 5da898abffd09..92b33003596cd 100644 --- a/src/libsync/encryptfolderjob.cpp +++ b/src/libsync/encryptfolderjob.cpp @@ -10,6 +10,8 @@ #include "foldermetadata.h" #include +using namespace Qt::StringLiterals; + namespace OCC { Q_LOGGING_CATEGORY(lcEncryptFolderJob, "nextcloud.sync.propagator.encryptfolder", QtInfoMsg) @@ -103,12 +105,15 @@ void EncryptFolderJob::uploadMetadata() return; } + const auto rootEncryptedFolderInfo = RootEncryptedFolderInfo{RootEncryptedFolderInfo::createRootPath(currentPathRelative, rec.path())}; + const auto emptyMetadata(QSharedPointer::create( _account, _remoteSyncRootPath, QByteArray{}, - RootEncryptedFolderInfo(RootEncryptedFolderInfo::createRootPath(currentPathRelative, rec.path())), - QByteArray{})); + rootEncryptedFolderInfo, + QByteArray{}, + rootEncryptedFolderInfo.path == u"/"_s ? FolderMetadata::FolderType::Root : FolderMetadata::FolderType::Nested)); connect(emptyMetadata.data(), &FolderMetadata::setupComplete, this, [this, emptyMetadata] { const auto encryptedMetadata = !emptyMetadata->isValid() ? QByteArray{} : emptyMetadata->encryptedMetadata(); diff --git a/src/libsync/foldermetadata.cpp b/src/libsync/foldermetadata.cpp index d628eff04777d..3cd3e2c8705ff 100644 --- a/src/libsync/foldermetadata.cpp +++ b/src/libsync/foldermetadata.cpp @@ -68,17 +68,19 @@ FolderMetadata::FolderMetadata(AccountPtr account, const QByteArray &metadata, const RootEncryptedFolderInfo &rootEncryptedFolderInfo, const QByteArray &signature, + FolderType folderType, QObject *parent) : QObject(parent) , _account(account) , _remoteFolderRoot(Utility::noLeadingSlashPath(Utility::noTrailingSlashPath(remoteFolderRoot))) , _initialMetadata(metadata) - , _isRootEncryptedFolder(rootEncryptedFolderInfo.path == QStringLiteral("/")) + , _isRootEncryptedFolder(folderType == FolderType::Root) , _binaryMetadataKeyForEncryption(rootEncryptedFolderInfo.binaryKeyForEncryption) , _binaryMetadataKeyForDecryption(rootEncryptedFolderInfo.binaryKeyForDecryption) , _keyChecksums(rootEncryptedFolderInfo.keyChecksums) , _initialSignature(signature) { + Q_ASSERT(_isRootEncryptedFolder == (rootEncryptedFolderInfo.path == QStringLiteral("/"))); Q_ASSERT(!_remoteFolderRoot.isEmpty()); _existingMetadataVersion = setupVersionFromExistingMetadata(metadata); @@ -131,6 +133,9 @@ void FolderMetadata::setupExistingMetadata(const QByteArray &metadata) const auto folderUsers = metaDataDoc[usersKey].toArray(); const auto isUsersArrayValid = (!_isRootEncryptedFolder && folderUsers.isEmpty()) || (_isRootEncryptedFolder && !folderUsers.isEmpty()); + if (!isUsersArrayValid) { + qCCritical(lcCseMetadata()) << (_isRootEncryptedFolder ? "is root encrypted folder" : "is child encrypted folder") << folderUsers; + } Q_ASSERT(isUsersArrayValid); if (!isUsersArrayValid) { diff --git a/src/libsync/foldermetadata.h b/src/libsync/foldermetadata.h index c1b126ac21342..496f118edb19a 100644 --- a/src/libsync/foldermetadata.h +++ b/src/libsync/foldermetadata.h @@ -92,7 +92,7 @@ class OWNCLOUDSYNC_EXPORT FolderMetadata : public QObject }; Q_ENUM(MetadataVersion) - FolderMetadata(AccountPtr account, const QString &remoteFolderRoot, FolderType folderType = FolderType::Nested); + FolderMetadata(AccountPtr account, const QString &remoteFolderRoot, FolderType folderType); /* * construct metadata based on RootEncryptedFolderInfo * as per E2EE V2, the encryption key and users that have access are only stored in root(top-level) encrypted folder's metadata @@ -103,6 +103,7 @@ class OWNCLOUDSYNC_EXPORT FolderMetadata : public QObject const QByteArray &metadata, const RootEncryptedFolderInfo &rootEncryptedFolderInfo, const QByteArray &signature, + FolderType folderType, QObject *parent = nullptr); [[nodiscard]] QVector files() const; diff --git a/test/testclientsideencryptionv2.cpp b/test/testclientsideencryptionv2.cpp index 9e75af70e3ae7..faf394a8924b7 100644 --- a/test/testclientsideencryptionv2.cpp +++ b/test/testclientsideencryptionv2.cpp @@ -176,7 +176,7 @@ private slots: QScopedPointer metadataFromJson(new FolderMetadata(_account, "/", ocsDoc.toJson(), - RootEncryptedFolderInfo::makeDefault(), signature)); + RootEncryptedFolderInfo::makeDefault(), signature, FolderMetadata::FolderType::Root)); QSignalSpy metadataSetupExistingCompleteSpy(metadataFromJson.data(), &FolderMetadata::setupComplete); metadataSetupExistingCompleteSpy.wait(); QCOMPARE(metadataSetupExistingCompleteSpy.count(), 1); @@ -208,7 +208,8 @@ private slots: QScopedPointer metadataFromJson(new FolderMetadata(_account, "/", ocsDoc.toJson(), RootEncryptedFolderInfo::makeDefault(), - emptySignature)); + emptySignature, + FolderMetadata::FolderType::Root)); QSignalSpy metadataSetupExistingCompleteSpy(metadataFromJson.data(), &FolderMetadata::setupComplete); metadataSetupExistingCompleteSpy.wait(); @@ -314,7 +315,7 @@ private slots: QJsonDocument ocsDoc = QJsonDocument::fromJson(QStringLiteral("{\"ocs\": {\"data\": {\"meta-data\": \"%1\"}}}").arg(QString::fromUtf8(encryptedMetadataCopy)).toUtf8()); - QScopedPointer metadataFromJsonForSecondUser(new FolderMetadata(_secondAccount, "/", ocsDoc.toJson(), RootEncryptedFolderInfo::makeDefault(), signature)); + QScopedPointer metadataFromJsonForSecondUser(new FolderMetadata(_secondAccount, "/", ocsDoc.toJson(), RootEncryptedFolderInfo::makeDefault(), signature, FolderMetadata::FolderType::Root)); QSignalSpy metadataSetupExistingCompleteSpy(metadataFromJsonForSecondUser.data(), &FolderMetadata::setupComplete); metadataSetupExistingCompleteSpy.wait(); QCOMPARE(metadataSetupExistingCompleteSpy.count(), 1); @@ -338,10 +339,12 @@ private slots: QJsonDocument ocsDocFromSecondUser = QJsonDocument::fromJson( QStringLiteral("{\"ocs\": {\"data\": {\"meta-data\": \"%1\"}}}").arg(QString::fromUtf8(encryptedMetadataFromSecondUser)).toUtf8()); - QScopedPointer metadataFromJsonForFirstUserToCheckCrossSharing(new FolderMetadata(_account, "/", + QScopedPointer metadataFromJsonForFirstUserToCheckCrossSharing(new FolderMetadata(_account, + "/", ocsDocFromSecondUser.toJson(), RootEncryptedFolderInfo::makeDefault(), - signatureAfterSecondUserModification)); + signatureAfterSecondUserModification, + FolderMetadata::FolderType::Root)); QSignalSpy metadataSetupForCrossSharingCompleteSpy(metadataFromJsonForFirstUserToCheckCrossSharing.data(), &FolderMetadata::setupComplete); metadataSetupForCrossSharingCompleteSpy.wait(); QCOMPARE(metadataSetupForCrossSharingCompleteSpy.count(), 1); diff --git a/test/testsecurefiledrop.cpp b/test/testsecurefiledrop.cpp index 8e7162ae34d7d..bcae215bdb307 100644 --- a/test/testsecurefiledrop.cpp +++ b/test/testsecurefiledrop.cpp @@ -141,7 +141,7 @@ private slots: const auto signature = metadata->metadataSignature(); QJsonDocument ocsDoc = QJsonDocument::fromJson(QStringLiteral("{\"ocs\": {\"data\": {\"meta-data\": \"%1\"}}}").arg(QString::fromUtf8(encryptedMetadata)).toUtf8()); - _parsedMetadataWithFileDrop.reset(new FolderMetadata(_account, "/", ocsDoc.toJson(), RootEncryptedFolderInfo::makeDefault(), signature)); + _parsedMetadataWithFileDrop.reset(new FolderMetadata(_account, "/", ocsDoc.toJson(), RootEncryptedFolderInfo::makeDefault(), signature, FolderMetadata::FolderType::Root)); QSignalSpy metadataWithFileDropSetupCompleteSpy(_parsedMetadataWithFileDrop.data(), &FolderMetadata::setupComplete); metadataWithFileDropSetupCompleteSpy.wait(); @@ -164,7 +164,7 @@ private slots: QJsonDocument ocsDoc = QJsonDocument::fromJson(QStringLiteral("{\"ocs\": {\"data\": {\"meta-data\": \"%1\"}}}").arg(QString::fromUtf8(encryptedMetadata)).toUtf8()); - _parsedMetadataAfterProcessingFileDrop.reset(new FolderMetadata(_account, "/", ocsDoc.toJson(), RootEncryptedFolderInfo::makeDefault(), signature)); + _parsedMetadataAfterProcessingFileDrop.reset(new FolderMetadata(_account, "/", ocsDoc.toJson(), RootEncryptedFolderInfo::makeDefault(), signature, FolderMetadata::FolderType::Root)); QSignalSpy metadataAfterProcessingFileDropSetupCompleteSpy(_parsedMetadataAfterProcessingFileDrop.data(), &FolderMetadata::setupComplete); metadataAfterProcessingFileDropSetupCompleteSpy.wait();