Merge pull request #2139 from nextcloud/fix_e2ee_folder_sync_connection

Fix e2ee folder sync connection
This commit is contained in:
Kevin Ottens 2020-07-01 19:10:44 +02:00 committed by GitHub
commit 56a6fe4731
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 96 additions and 23 deletions

View file

@ -1098,6 +1098,7 @@ bool SyncJournalDb::getFilesBelowPath(const QByteArray &path, const std::functio
if (!_getFilesBelowPathQuery.initOrReset(QByteArrayLiteral(
GET_FILE_RECORD_QUERY
" WHERE " IS_PREFIX_PATH_OF("?1", "path")
" OR " IS_PREFIX_PATH_OF("?1", "e2eMangledName")
// We want to ensure that the contents of a directory are sorted
// directly behind the directory itself. Without this ORDER BY
// an ordering like foo, foo-2, foo/file would be returned.
@ -1133,7 +1134,7 @@ bool SyncJournalDb::postSyncCleanup(const QSet<QString> &filepathsToKeep,
}
SqlQuery query(_db);
query.prepare("SELECT phash, path FROM metadata order by path");
query.prepare("SELECT phash, path, e2eMangledName FROM metadata order by path");
if (!query.exec()) {
return false;
@ -1142,11 +1143,12 @@ bool SyncJournalDb::postSyncCleanup(const QSet<QString> &filepathsToKeep,
QByteArrayList superfluousItems;
while (query.next()) {
const QString file = query.baValue(1);
bool keep = filepathsToKeep.contains(file);
const auto file = QString(query.baValue(1));
const auto mangledPath = QString(query.baValue(2));
bool keep = filepathsToKeep.contains(file) || filepathsToKeep.contains(mangledPath);
if (!keep) {
foreach (const QString &prefix, prefixesToKeep) {
if (file.startsWith(prefix)) {
if (file.startsWith(prefix) || mangledPath.startsWith(prefix)) {
keep = true;
break;
}

View file

@ -153,13 +153,17 @@ QVariant FolderStatusModel::data(const QModelIndex &index, int role) const
return QString(QLatin1String("<qt>") + Utility::escape(x._size < 0 ? x._name : tr("%1 (%2)").arg(x._name, Utility::octetsToString(x._size))) + QLatin1String("</qt>"));
case Qt::CheckStateRole:
return x._checked;
case Qt::DecorationRole:
if (_accountState->account()->e2e()->isFolderEncrypted(x._path)) {
case Qt::DecorationRole: {
Q_ASSERT(x._folder->remotePath().startsWith('/'));
const auto rootPath = x._folder->remotePath().mid(1);
const auto absoluteRemotePath = rootPath.isEmpty() ? x._path : rootPath + '/' + x._path;
if (_accountState->account()->e2e()->isFolderEncrypted(absoluteRemotePath)) {
return QIcon(QLatin1String(":/client/theme/lock-https.svg"));
} else if (x._size > 0 && _accountState->account()->e2e()->isAnyParentFolderEncrypted(x._path)) {
} else if (x._size > 0 && _accountState->account()->e2e()->isAnyParentFolderEncrypted(absoluteRemotePath)) {
return QIcon(QLatin1String(":/client/theme/lock-broken.svg"));
}
return QFileIconProvider().icon(x._isExternal ? QFileIconProvider::Network : QFileIconProvider::Folder);
}
case Qt::ForegroundRole:
if (x._isUndecided) {
return QColor(Qt::red);

View file

@ -319,6 +319,13 @@ void FolderWizardRemotePath::slotUpdateDirectories(const QStringList &list)
Utility::sortFilenames(sortedList);
foreach (QString path, sortedList) {
path.remove(webdavFolder);
// Don't allow to select subfolders of encrypted subfolders
if (_account->capabilities().clientSideEncryptionAvailable() &&
_account->e2e()->isAnyParentFolderEncrypted(path)) {
continue;
}
QStringList paths = path.split('/');
if (paths.last().isEmpty())
paths.removeLast();
@ -344,6 +351,12 @@ void FolderWizardRemotePath::slotCurrentItemChanged(QTreeWidgetItem *item)
{
if (item) {
QString dir = item->data(0, Qt::UserRole).toString();
// We don't want to allow creating subfolders in encrypted folders outside of the sync logic
const auto encrypted = _account->capabilities().clientSideEncryptionAvailable() &&
_account->e2e()->isFolderEncrypted(dir + '/');
_ui.addFolderButton->setEnabled(!encrypted);
if (!dir.startsWith(QLatin1Char('/'))) {
dir.prepend(QLatin1Char('/'));
}

View file

@ -248,6 +248,13 @@ void SelectiveSyncWidget::slotUpdateDirectories(QStringList list)
foreach (QString path, list) {
auto size = job ? job->_folderInfos[path].size : 0;
path.remove(pathToRemove);
// Don't allow to select subfolders of encrypted subfolders
if (_account->capabilities().clientSideEncryptionAvailable() &&
_account->e2e()->isAnyParentFolderEncrypted(_rootName + '/' + path)) {
continue;
}
QStringList paths = path.split('/');
if (paths.last().isEmpty())
paths.removeLast();

View file

@ -351,7 +351,8 @@ void PropagateDownloadFile::start()
return result;
}
}();
const auto remotePath = QString(rootPath + _item->_file);
const auto remoteFilename = _item->_encryptedFileName.isEmpty() ? _item->_file : _item->_encryptedFileName;
const auto remotePath = QString(rootPath + remoteFilename);
const auto remoteParentPath = remotePath.left(remotePath.lastIndexOf('/'));
const auto account = propagator()->account();
@ -359,9 +360,12 @@ void PropagateDownloadFile::start()
!account->e2e()->isFolderEncrypted(remoteParentPath + '/')) {
startAfterIsEncryptedIsChecked();
} else {
const auto slashPosition = remoteFilename.lastIndexOf('/');
const auto relativeRemoteParentPath = slashPosition >= 0 ? remoteFilename.left(slashPosition) : QString();
SyncJournalFileRecord parentRec;
propagator()->_journal->getFileRecordByE2eMangledName(remoteParentPath, &parentRec);
const auto parentPath = parentRec.isValid() ? parentRec._path : remoteParentPath;
propagator()->_journal->getFileRecordByE2eMangledName(relativeRemoteParentPath, &parentRec);
const auto parentPath = parentRec.isValid() ? parentRec._path : relativeRemoteParentPath;
_downloadEncryptedHelper = new PropagateDownloadEncrypted(propagator(), parentPath, _item, this);
connect(_downloadEncryptedHelper, &PropagateDownloadEncrypted::folderStatusNotEncrypted, [this] {

View file

@ -21,7 +21,19 @@ void PropagateDownloadEncrypted::start() {
void PropagateDownloadEncrypted::checkFolderEncryptedStatus()
{
auto getEncryptedStatus = new GetFolderEncryptStatusJob(_propagator->account(), _info.path());
const auto rootPath = [=]() {
const auto result = _propagator->_remoteFolder;
if (result.startsWith('/')) {
return result.mid(1);
} else {
return result;
}
}();
const auto remoteFilename = _item->_encryptedFileName.isEmpty() ? _item->_file : _item->_encryptedFileName;
const auto remotePath = QString(rootPath + remoteFilename);
const auto remoteParentPath = remotePath.left(remotePath.lastIndexOf('/'));
auto getEncryptedStatus = new GetFolderEncryptStatusJob(_propagator->account(), remoteParentPath, this);
connect(getEncryptedStatus, &GetFolderEncryptStatusJob::encryptStatusFolderReceived,
this, &PropagateDownloadEncrypted::folderStatusReceived);
@ -89,8 +101,14 @@ void PropagateDownloadEncrypted::checkFolderEncryptedMetadata(const QJsonDocumen
for (const EncryptedFile &file : files) {
if (encryptedFilename == file.encryptedFilename) {
_encryptedInfo = file;
_item->_encryptedFileName = _item->_file;
_item->_file = _localParentPath + QLatin1Char('/') + _encryptedInfo.originalFilename;
if (_item->_encryptedFileName.isEmpty()) {
_item->_encryptedFileName = _item->_file;
}
if (!_localParentPath.isEmpty()) {
_item->_file = _localParentPath + QLatin1Char('/') + _encryptedInfo.originalFilename;
} else {
_item->_file = _encryptedInfo.originalFilename;
}
qCDebug(lcPropagateDownloadEncrypted) << "Found matching encrypted metadata for file, starting download";
emit folderStatusEncrypted();

View file

@ -111,8 +111,9 @@ void PropagateRemoteMkdir::slotMkdir()
return result;
}
}();
const auto path = QString(rootPath + _item->_file);
const auto parentPath = path.left(path.lastIndexOf('/'));
const auto path = _item->_file;
const auto slashPosition = path.lastIndexOf('/');
const auto parentPath = slashPosition >= 0 ? path.left(slashPosition) : QString();
SyncJournalFileRecord parentRec;
bool ok = propagator()->_journal->getFileRecord(parentPath, &parentRec);
@ -122,11 +123,12 @@ void PropagateRemoteMkdir::slotMkdir()
}
const auto remoteParentPath = parentRec._e2eMangledName.isEmpty() ? parentPath : parentRec._e2eMangledName;
const auto absoluteRemoteParentPath = remoteParentPath.isEmpty() ? rootPath : rootPath + remoteParentPath + '/';
const auto account = propagator()->account();
if (!account->capabilities().clientSideEncryptionAvailable() ||
(!account->e2e()->isFolderEncrypted(remoteParentPath + '/') &&
!account->e2e()->isAnyParentFolderEncrypted(remoteParentPath + '/'))) {
(!account->e2e()->isFolderEncrypted(absoluteRemoteParentPath) &&
!account->e2e()->isAnyParentFolderEncrypted(absoluteRemoteParentPath))) {
slotStartMkcolJob();
return;
}

View file

@ -179,8 +179,9 @@ void PropagateUploadFileCommon::start()
return result;
}
}();
const auto path = QString(rootPath + _item->_file);
const auto parentPath = path.left(path.lastIndexOf('/'));
const auto path = _item->_file;
const auto slashPosition = path.lastIndexOf('/');
const auto parentPath = slashPosition >= 0 ? path.left(slashPosition) : QString();
SyncJournalFileRecord parentRec;
bool ok = propagator()->_journal->getFileRecord(parentPath, &parentRec);
@ -190,10 +191,11 @@ void PropagateUploadFileCommon::start()
}
const auto remoteParentPath = parentRec._e2eMangledName.isEmpty() ? parentPath : parentRec._e2eMangledName;
const auto absoluteRemoteParentPath = remoteParentPath.isEmpty() ? rootPath : rootPath + remoteParentPath + '/';
const auto account = propagator()->account();
if (!account->capabilities().clientSideEncryptionAvailable() ||
!account->e2e()->isFolderEncrypted(remoteParentPath + '/')) {
!account->e2e()->isFolderEncrypted(absoluteRemoteParentPath)) {
setupUnencryptedFile();
return;
}

View file

@ -27,6 +27,23 @@ PropagateUploadEncrypted::PropagateUploadEncrypted(OwncloudPropagator *propagato
void PropagateUploadEncrypted::start()
{
const auto rootPath = [=]() {
const auto result = _propagator->_remoteFolder;
if (result.startsWith('/')) {
return result.mid(1);
} else {
return result;
}
}();
const auto absoluteRemoteParentPath = [=]{
auto path = QString(rootPath + _remoteParentPath);
if (path.endsWith('/')) {
path.chop(1);
}
return path;
}();
/* If the file is in a encrypted-enabled nextcloud instance, we need to
* do the long road: Fetch the folder status of the encrypted bit,
* if it's encrypted, find the ID of the folder.
@ -40,7 +57,7 @@ void PropagateUploadEncrypted::start()
* If the folder is unencrypted we just follow the old way.
*/
qCDebug(lcPropagateUploadEncrypted) << "Starting to send an encrypted file!";
auto getEncryptedStatus = new GetFolderEncryptStatusJob(_propagator->account(), _remoteParentPath, this);
auto getEncryptedStatus = new GetFolderEncryptStatusJob(_propagator->account(), absoluteRemoteParentPath, this);
connect(getEncryptedStatus, &GetFolderEncryptStatusJob::encryptStatusFolderReceived,
this, &PropagateUploadEncrypted::slotFolderEncryptedStatusFetched);

View file

@ -174,9 +174,13 @@ void PropagateLocalMkdir::start()
!account->e2e()->isFolderEncrypted(remoteParentPath + '/')) {
startLocalMkdir();
} else {
const auto relativeRemotePath = _item->_file;
const auto slashPosition = relativeRemotePath.lastIndexOf('/');
const auto relativeRemoteParentPath = slashPosition >= 0 ? relativeRemotePath.left(slashPosition) : QString();
SyncJournalFileRecord parentRec;
propagator()->_journal->getFileRecordByE2eMangledName(remoteParentPath, &parentRec);
const auto parentPath = parentRec.isValid() ? parentRec._path : remoteParentPath;
propagator()->_journal->getFileRecordByE2eMangledName(relativeRemoteParentPath, &parentRec);
const auto parentPath = parentRec.isValid() ? parentRec._path : relativeRemoteParentPath;
startDemanglingName(parentPath);
}
}