OAuth2: Have a link to the browser in the owncloud UI
authorOlivier Goffart <ogoffart@woboq.com>
Thu, 13 Jul 2017 09:27:02 +0000 (11:27 +0200)
committerOlivier Goffart <olivier@woboq.com>
Thu, 13 Jul 2017 14:09:42 +0000 (16:09 +0200)
When the browser is open, ad a link in the ui to re-open
the browser.

Issue #5893

src/gui/accountsettings.cpp
src/gui/accountsettings.h
src/gui/accountstate.cpp
src/gui/accountstate.h
src/gui/application.cpp
src/gui/creds/httpcredentialsgui.cpp
src/gui/creds/httpcredentialsgui.h
src/gui/creds/oauth.cpp
src/gui/creds/oauth.h

index 6da34d59048d9d45efb2b0e091e70acedae7e9c8..51a6130a671fb664c6e9d20662c5efd84f82db61 100644 (file)
@@ -30,6 +30,7 @@
 #include "accountmanager.h"
 #include "owncloudsetupwizard.h"
 #include "creds/abstractcredentials.h"
+#include "creds/httpcredentialsgui.h"
 #include "tooltipupdater.h"
 #include "filesystem.h"
 
@@ -180,8 +181,8 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
 
     ui->connectLabel->setText(tr("No account configured."));
 
-    connect(_accountState, SIGNAL(stateChanged(int)), SLOT(slotAccountStateChanged(int)));
-    slotAccountStateChanged(_accountState->state());
+    connect(_accountState, &AccountState::stateChanged, this, &AccountSettings::slotAccountStateChanged);
+    slotAccountStateChanged();
 
     connect(&_quotaInfo, SIGNAL(quotaUpdated(qint64, qint64)),
         this, SLOT(slotUpdateQuota(qint64, qint64)));
@@ -622,8 +623,9 @@ void AccountSettings::slotUpdateQuota(qint64 total, qint64 used)
     }
 }
 
-void AccountSettings::slotAccountStateChanged(int state)
+void AccountSettings::slotAccountStateChanged()
 {
+    int state = _accountState ? _accountState->state() : AccountState::Disconnected;
     if (_accountState) {
         ui->sslButton->updateAccountState(_accountState);
         AccountPtr account = _accountState->account();
@@ -654,6 +656,20 @@ void AccountSettings::slotAccountStateChanged(int state)
             showConnectionLabel(tr("Server %1 is currently in maintenance mode.").arg(server));
         } else if (state == AccountState::SignedOut) {
             showConnectionLabel(tr("Signed out from %1.").arg(serverWithUser));
+        } else if (state == AccountState::AskingCredentials) {
+            QUrl url;
+            if (auto cred = qobject_cast<HttpCredentialsGui *>(account->credentials())) {
+                connect(cred, &HttpCredentialsGui::authorisationLinkChanged,
+                    this, &AccountSettings::slotAccountStateChanged, Qt::UniqueConnection);
+                url = cred->authorisationLink();
+            }
+            if (url.isValid()) {
+                showConnectionLabel(tr("Obtaining authorization from the browser. "
+                                       "<a href='%1'>Click here</a> to re-open the browser.")
+                                        .arg(url.toString(QUrl::FullyEncoded)));
+            } else {
+                showConnectionLabel(tr("Connecting to %1...").arg(serverWithUser));
+            }
         } else {
             showConnectionLabel(tr("No connection to %1 at %2.")
                                     .arg(Utility::escape(Theme::instance()->appNameGUI()), server),
index 3e45d7e4a7f68931d771c3fdd5c68f8c24d6e4a4..eaef07eb478a5fc1e349ed2e0e0f3d4a5187601b 100644 (file)
@@ -65,7 +65,7 @@ signals:
 public slots:
     void slotOpenOC();
     void slotUpdateQuota(qint64, qint64);
-    void slotAccountStateChanged(int state);
+    void slotAccountStateChanged();
 
     AccountState *accountsState() { return _accountState; }
 
index d2025dde18be683af6aabd91e2e18b4a5d8997cb..dbe998ce1aeb52b997f143a6a747c6ccfa0cbcf1 100644 (file)
@@ -133,6 +133,8 @@ QString AccountState::stateString(State state)
         return tr("Network error");
     case ConfigurationError:
         return tr("Configuration error");
+    case AskingCredentials:
+        return tr("Asking Credentials");
     }
     return tr("Unknown account state");
 }
@@ -307,7 +309,7 @@ void AccountState::slotInvalidCredentials()
         account()->credentials()->invalidateToken();
     account()->credentials()->askFromUser();
 
-    setState(ConfigurationError);
+    setState(AskingCredentials);
     _waitingForNewCredentials = true;
 }
 
index 2333bcece8e370d2da9dcd4bc76aa95bb8a310ce..ce36c94fbe78b8307f47411bfe3e3d672aebde30 100644 (file)
@@ -64,8 +64,11 @@ public:
         /// again automatically.
         NetworkError,
 
-        /// An error like invalid credentials where retrying won't help.
-        ConfigurationError
+        /// Server configuration error. (For example: unsupported version)
+        ConfigurationError,
+
+        /// We are currently asking the user for credentials
+        AskingCredentials
     };
 
     /// The actual current connectivity status.
index 2cb11ae30ba3a8476b2c7de43a567defc708266e..3ebbb8fc5d6f2a2f1f92b3949e0dfa4977e5e53d 100644 (file)
@@ -308,7 +308,8 @@ void Application::slotCheckConnection()
         // Don't check if we're manually signed out or
         // when the error is permanent.
         if (state != AccountState::SignedOut
-            && state != AccountState::ConfigurationError) {
+            && state != AccountState::ConfigurationError
+            && state != AccountState::AskingCredentials) {
             accountState->checkConnectivity();
         }
     }
index 9c629ce654d838b932e66347184b157437033886..dbc1c7e18522e1744ec03c00616de2c689de8af8 100644 (file)
@@ -42,7 +42,10 @@ void HttpCredentialsGui::askFromUser()
             _asyncAuth.reset(new OAuth(_account, this));
             connect(_asyncAuth.data(), &OAuth::result,
                 this, &HttpCredentialsGui::asyncAuthResult);
+            connect(_asyncAuth.data(), &OAuth::destroyed,
+                this, &HttpCredentialsGui::authorisationLinkChanged);
             _asyncAuth->start();
+            emit authorisationLinkChanged();
         } else if (reply->error() == QNetworkReply::AuthenticationRequiredError) {
             // Show the dialog
             // We will re-enter the event loop, so better wait the next iteration
index 0eaeeee812229fd0f8bc1ac766fb25a582ac62d1..fefc4dd1d28e319cd5a43115287083ced367bb6e 100644 (file)
@@ -49,12 +49,21 @@ public:
      * or call showDialog to ask the password
      */
     Q_INVOKABLE void askFromUser() Q_DECL_OVERRIDE;
+    /**
+     * In case of oauth, return an URL to the link to open the browser.
+     * An invalid URL otherwise
+     */
+    QUrl authorisationLink() const { return _asyncAuth ? _asyncAuth->authorisationLink() : QUrl(); }
+
 
     static QString requestAppPasswordText(const Account *account);
 private slots:
     void asyncAuthResult(OAuth::Result, const QString &user, const QString &accessToken, const QString &refreshToken);
     void showDialog();
 
+signals:
+    void authorisationLinkChanged();
+
 private:
     QScopedPointer<OAuth, QScopedPointerObjectDeleteLater<OAuth>> _asyncAuth;
 };
index 710e190ac1badcb7ea60286799c1154c4c3b1eee..43133f91f6b0463d7c6d056f7f9db0dfb627c6d8 100644 (file)
@@ -131,17 +131,18 @@ void OAuth::start()
     QTimer::singleShot(5 * 60 * 1000, this, [this] { result(Error); });
 }
 
-
-bool OAuth::openBrowser()
+QUrl OAuth::authorisationLink() const
 {
     Q_ASSERT(_server.isListening());
-    auto url = QUrl(_account->url().toString()
+    return QUrl(_account->url().toString()
         + QLatin1String("/index.php/apps/oauth2/authorize?response_type=code&client_id=")
         + Theme::instance()->oauthClientId()
         + QLatin1String("&redirect_uri=http://localhost:") + QString::number(_server.serverPort()));
+}
 
-
-    if (!QDesktopServices::openUrl(url)) {
+bool OAuth::openBrowser()
+{
+    if (!QDesktopServices::openUrl(authorisationLink())) {
         // We cannot open the browser, then we claim we don't support OAuth.
         emit result(NotSupported, QString());
         return false;
index fe7fd1c402650b9c9f20d5cfd9a03e7705ee1bfa..943f294f04d01be35c8929161450830e14e9e223 100644 (file)
@@ -15,6 +15,7 @@
 #pragma once
 #include <QPointer>
 #include <QTcpServer>
+#include <QUrl>
 
 namespace OCC {
 
@@ -53,6 +54,7 @@ public:
     Q_ENUM(Result);
     void start();
     bool openBrowser();
+    QUrl authorisationLink() const;
 
 signals:
     /**