edit locally requires a valid token
authorMatthieu Gallien <matthieu.gallien@nextcloud.com>
Wed, 12 Oct 2022 16:01:15 +0000 (18:01 +0200)
committerMatthieu Gallien <matthieu.gallien@nextcloud.com>
Mon, 17 Oct 2022 07:02:26 +0000 (09:02 +0200)
check on server that the token received during a request to open a local
file is indeed a valid one

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
src/gui/application.cpp
src/gui/folderman.cpp
src/gui/folderman.h

index 21cce1a67c74865f65d53ff32134756d73f0dbef..6f891e022edfc1247d5f9f0b2d5d1ae185419a3b 100644 (file)
@@ -61,6 +61,7 @@
 #include <QMessageBox>
 #include <QDesktopServices>
 #include <QGuiApplication>
+#include <QUrlQuery>
 
 class QSocket;
 
@@ -764,8 +765,16 @@ void Application::handleEditLocally(const QUrl &url) const
     // for a sample URL "nc://open/admin@nextcloud.lan:8080/Photos/lovely.jpg", QUrl::path would return "admin@nextcloud.lan:8080/Photos/lovely.jpg"
     const auto accountDisplayName = pathSplit.takeFirst();
     const auto fileRemotePath = pathSplit.join('/');
+    const auto urlQuery = QUrlQuery{url};
 
-    FolderMan::instance()->editFileLocally(accountDisplayName, fileRemotePath);
+    auto token = QString{};
+    if (urlQuery.hasQueryItem(QStringLiteral("token"))) {
+        token = urlQuery.queryItemValue(QStringLiteral("token"));
+    } else {
+        qCWarning(lcApplication) << "Invalid URL for file local editing: missing token";
+    }
+
+    FolderMan::instance()->editFileLocally(accountDisplayName, fileRemotePath, token);
 }
 
 QString substLang(const QString &lang)
index 220313891dd7bc8d4375a9143998ad725a487beb..d76a4fccd0ce03cc5648dba20829f7cb27b4ad3a 100644 (file)
@@ -1422,7 +1422,7 @@ void FolderMan::setDirtyNetworkLimits()
     }
 }
 
-void FolderMan::editFileLocally(const QString &accountDisplayName, const QString &relPath)
+void FolderMan::editFileLocally(const QString &accountDisplayName, const QString &relPath, const QString &token)
 {
     const auto showError = [this](const OCC::AccountStatePtr accountState, const QString &errorMessage, const QString &subject) {
         if (accountState && accountState->account()) {
@@ -1447,6 +1447,12 @@ void FolderMan::editFileLocally(const QString &accountDisplayName, const QString
         messageBox->raise();
     };
 
+    if (token.isEmpty()) {
+        qCWarning(lcFolderMan) << "Edit locally request is missing a valid token. Impossible to open the file.";
+        showError({}, tr("Edit locally request is not valid. Opening the file is forbidden."), accountDisplayName);
+        return;
+    }
+
     const auto accountFound = AccountManager::instance()->account(accountDisplayName);
 
     if (!accountFound) {
@@ -1488,23 +1494,38 @@ void FolderMan::editFileLocally(const QString &accountDisplayName, const QString
         showError(accountFound, tr("Could not find a file for local editing. Make sure its path is valid and it is synced locally."), relPath);
         return;
     }
-    folderForFile->startSync();
-    _localFileEditingSyncFinishedConnections.insert(localFilePath, QObject::connect(folderForFile, &Folder::syncFinished, this,
-        [this, localFilePath](const OCC::SyncResult &result) {
-        Q_UNUSED(result);
-        const auto foundConnectionIt = _localFileEditingSyncFinishedConnections.find(localFilePath);
-        if (foundConnectionIt != std::end(_localFileEditingSyncFinishedConnections) && foundConnectionIt.value()) {
-            QObject::disconnect(foundConnectionIt.value());
-            _localFileEditingSyncFinishedConnections.erase(foundConnectionIt);
-        }
-        // In case the VFS mode is enabled and a file is not yet hydrated, we must call QDesktopServices::openUrl
-        // from a separate thread, or, there will be a freeze. To avoid searching for a specific folder and checking
-        // if the VFS is enabled - we just always call it from a separate thread.
-        QtConcurrent::run([localFilePath]() {
-            QDesktopServices::openUrl(QUrl::fromLocalFile(localFilePath));
+
+    const auto checkTokenForEditLocally = new SimpleApiJob(accountFound->account(), QStringLiteral("/ocs/v2.php/apps/files/api/v1/openlocaleditor/%1").arg(token));
+    checkTokenForEditLocally->setVerb(SimpleApiJob::Verb::Post);
+    checkTokenForEditLocally->setBody(QByteArray{"path=/"}.append(relPath.toUtf8()));
+    connect(checkTokenForEditLocally, &SimpleApiJob::resultReceived, checkTokenForEditLocally, [this, folderForFile, localFilePath, showError, accountFound, relPath] (int statusCode) {
+        constexpr auto HTTP_OK_CODE = 200;
+        if (statusCode != HTTP_OK_CODE) {
             Systray::instance()->destroyEditFileLocallyLoadingDialog();
-        });
-    }));
+            showError(accountFound, tr("Could not validate the request to open a file from server."), relPath);
+            qCInfo(lcFolderMan()) << "token check result" << statusCode;
+            return;
+        }
+
+        folderForFile->startSync();
+        _localFileEditingSyncFinishedConnections.insert(localFilePath, QObject::connect(folderForFile, &Folder::syncFinished, this,
+                                                                                        [this, localFilePath](const OCC::SyncResult &result) {
+                                                                                            Q_UNUSED(result);
+                                                                                            const auto foundConnectionIt = _localFileEditingSyncFinishedConnections.find(localFilePath);
+                                                                                            if (foundConnectionIt != std::end(_localFileEditingSyncFinishedConnections) && foundConnectionIt.value()) {
+                                                                                                QObject::disconnect(foundConnectionIt.value());
+                                                                                                _localFileEditingSyncFinishedConnections.erase(foundConnectionIt);
+                                                                                            }
+                                                                                            // In case the VFS mode is enabled and a file is not yet hydrated, we must call QDesktopServices::openUrl
+                                                                                            // from a separate thread, or, there will be a freeze. To avoid searching for a specific folder and checking
+                                                                                            // if the VFS is enabled - we just always call it from a separate thread.
+                                                                                            QtConcurrent::run([localFilePath]() {
+                                                                                                QDesktopServices::openUrl(QUrl::fromLocalFile(localFilePath));
+                                                                                                Systray::instance()->destroyEditFileLocallyLoadingDialog();
+                                                                                            });
+                                                                                        }));
+    });
+    checkTokenForEditLocally->start();
 }
 
 void FolderMan::trayOverallStatus(const QList<Folder *> &folders,
index 77a6b6682b0dc8a6622d1aff32c3fef2a0c83cb4..2c9b920e6db48ac76c9879ffb430f36179bfe417 100644 (file)
@@ -214,7 +214,7 @@ public:
     void setDirtyNetworkLimits();
 
     /** opens a file with default app, if the file is present **/
-    void editFileLocally(const QString &accountDisplayName, const QString &relPath);
+    void editFileLocally(const QString &accountDisplayName, const QString &relPath, const QString &token);
 
 signals:
     /**