diff options
-rw-r--r-- | kde-apps/libkgapi/files/libkgapi-17.04.0-auth1.patch | 168 | ||||
-rw-r--r-- | kde-apps/libkgapi/files/libkgapi-17.04.0-auth2.patch | 36 | ||||
-rw-r--r-- | kde-apps/libkgapi/files/libkgapi-17.04.0-auth3.patch | 310 | ||||
-rw-r--r-- | kde-apps/libkgapi/libkgapi-17.04.0-r1.ebuild | 36 |
4 files changed, 550 insertions, 0 deletions
diff --git a/kde-apps/libkgapi/files/libkgapi-17.04.0-auth1.patch b/kde-apps/libkgapi/files/libkgapi-17.04.0-auth1.patch new file mode 100644 index 000000000000..eb426465947b --- /dev/null +++ b/kde-apps/libkgapi/files/libkgapi-17.04.0-auth1.patch @@ -0,0 +1,168 @@ +From 68b89bce22d0da234345ccffb869ae6863592624 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Daniel=20Vr=C3=A1til?= <dvratil@kde.org> +Date: Thu, 27 Apr 2017 17:22:27 +0200 +Subject: [PATCH 1/3] Auth: Adapt to changes in Google OAuth token retrieval + process + +URLs and HTML code have changed a bit, which breaks authentication. Hopefully +they won't change it too often in the future. +--- + src/core/ui/authwidget_p.cpp | 109 ++++++++++++++++++++++++------------------- + src/core/ui/authwidget_p.h | 6 +++ + 2 files changed, 67 insertions(+), 48 deletions(-) + +diff --git a/src/core/ui/authwidget_p.cpp b/src/core/ui/authwidget_p.cpp +index 6de33f5..75d38cd 100644 +--- a/src/core/ui/authwidget_p.cpp ++++ b/src/core/ui/authwidget_p.cpp +@@ -112,11 +112,35 @@ void AuthWidgetPrivate::emitError(const enum Error errCode, const QString& msg) + + void AuthWidgetPrivate::webviewUrlChanged(const QUrl &url) + { +- qCDebug(KGAPIDebug) << url; ++ qCDebug(KGAPIDebug) << "URLChange:" << url; + +- /* Access token here - hide browser and tell user to wait until we +- * finish the authentication process ourselves */ +- if (url.host() == QLatin1String("accounts.google.com") && url.path() == QLatin1String("/o/oauth2/approval")) { ++ if (!isGoogleHost(url)) { ++ return; ++ } ++ ++ // Username and password inputs are loaded dynamically, so we only get ++ // urlChanged, but not urlFinished. ++ if (isUsernameFrame(url)) { ++#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) ++ if (!username.isEmpty()) { ++ webview->page()->runJavaScript(QStringLiteral("document.getElementById(\"identifierId\").value = \"%1\";").arg(username)); ++ } ++#endif ++ } else if (isPasswordFrame(url)) { ++#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) ++ if (!password.isEmpty()) { ++ webview->page()->runJavaScript(QStringLiteral("var elems = document.getElementsByTagName(\"input\");" ++ "for (var i = 0; i < elems.length; i++) {" ++ " if (elems[i].type == \"password\" && elems[i].name == \"password\") {" ++ " elems[i].value = \"%1\";" ++ " break;" ++ " }" ++ "}").arg(password)); ++ } ++#endif ++ } else if (isTokenPage(url)) { ++ /* Access token here - hide browser and tell user to wait until we ++ * finish the authentication process ourselves */ + webview->setVisible(false); + progressbar->setVisible(false); + label->setVisible(true); +@@ -131,57 +155,46 @@ void AuthWidgetPrivate::webviewFinished(bool ok) + qCWarning(KGAPIDebug) << "Failed to load" << webview->url(); + } + +- QUrl url = webview->url(); +- qCDebug(KGAPIDebug) << url; +- +- if (url.host() == QLatin1String("accounts.google.com") && url.path() == QLatin1String("/ServiceLogin")) { +- if (username.isEmpty() && password.isEmpty()) { +- return; +- } +- +-#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) +- const auto js = QStringLiteral("document.getElementById(\"%1\").value = \"%2\";"); +- if (!username.isEmpty()) { +- webview->page()->runJavaScript(js.arg(QStringLiteral("Email"), username)); +- } +- +- if (!password.isEmpty()) { +- webview->page()->runJavaScript(js.arg(QStringLiteral("Passwd"), password)); +- } +-#endif ++ const QUrl url = webview->url(); ++ qCDebug(KGAPIDebug) << "URLFinished:" << url; + ++ if (!isGoogleHost(url)) { + return; + } + +- if (url.host() == QLatin1String("accounts.google.com") && url.path() == QLatin1String("/o/oauth2/approval")) { +- QString title = webview->title(); +- QString token; +- +- if (title.startsWith(QLatin1String("success"), Qt::CaseInsensitive)) { +- int pos = title.indexOf(QLatin1String("code=")); +- /* Skip the 'code=' string as well */ +- token = title.mid (pos + 5); ++ if (isTokenPage(url)) { ++ const auto token = url.queryItemValue(QStringLiteral("approvalCode")); ++ if (!token.isEmpty()) { ++ qCDebug(KGAPIDebug) << "Got token: " << token; ++ auto fetch = new KGAPI2::NewTokensFetchJob(token, apiKey, secretKey); ++ connect(fetch, &Job::finished, this, &AuthWidgetPrivate::tokensReceived); + } else { +- webview->page()->toHtml([title](const QString &html) { +- qCDebug(KGAPIDebug) << "Parsing token page failed. Title:" << title; +- qCDebug(KGAPIDebug) << html; +- }); ++#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) ++ qCWarning(KGAPIDebug) << "Failed to parse token from URL, peaking into HTML..."; ++ webview->page()->runJavaScript( ++ QStringLiteral("document.getElementById(\"code\").value;"), ++ [this](const QVariant &result) { ++ const auto token = result.toString(); ++ if (token.isEmpty()) { ++ qCWarning(KGAPIDebug) << "Peaked into HTML, but cound not find token :("; ++ webview->page()->toHtml([](const QString &html) { ++ qCDebug(KGAPIDebug) << "Parsing token page failed"; ++ qCDebug(KGAPIDebug) << html; ++ }); ++ emitError(AuthError, tr("Parsing token page failed.")); ++ return; ++ } ++ qCDebug(KGAPIDebug) << "Peaked into HTML and found token: " << token; ++ auto fetch = new KGAPI2::NewTokensFetchJob(token, apiKey, secretKey); ++ connect(fetch, &Job::finished, this, &AuthWidgetPrivate::tokensReceived); ++ }); ++#else ++ qCWarning(KGAPIDebug) << "Failed to parse token from URL!"; + emitError(AuthError, tr("Parsing token page failed.")); +- return; +- } +- +- if (token.isEmpty()) { +- webview->page()->toHtml([](const QString &html) { +- qCDebug(KGAPIDebug) << "Failed to obtain token."; +- qCDebug(KGAPIRaw) << html; +- }); +- emitError(AuthError, tr("Failed to obtain token.")); +- return; ++#endif + } +- +- KGAPI2::NewTokensFetchJob *fetchJob = new KGAPI2::NewTokensFetchJob(token, apiKey, secretKey); +- connect(fetchJob, &Job::finished, +- this, &AuthWidgetPrivate::tokensReceived); ++ } else { ++ //qCDebug(KGAPIDebug) << "Unhandled page:" << url.host() << ", " << url.path(); + } + } + +diff --git a/src/core/ui/authwidget_p.h b/src/core/ui/authwidget_p.h +index 673b0cb..9c488be 100644 +--- a/src/core/ui/authwidget_p.h ++++ b/src/core/ui/authwidget_p.h +@@ -82,6 +82,12 @@ class Q_DECL_HIDDEN AuthWidgetPrivate: public QObject { + void setupUi(); + void setProgress(AuthWidget::Progress progress); + ++ bool isGoogleHost(const QUrl &url) const { return url.host() == QLatin1String("accounts.google.com"); } ++ bool isSigninPage(const QUrl &url) const { return url.path() == QLatin1String("/signin/oauth"); } ++ bool isUsernameFrame(const QUrl &url) { return url.path() == QLatin1String("/signin/oauth/identifier"); } ++ bool isPasswordFrame(const QUrl &url) { return url.path() == QLatin1String("/signin/v2/challenge/pwd"); } ++ bool isTokenPage(const QUrl &url) { return url.path() == QLatin1String("/o/oauth2/approval/v2"); } ++ + AuthWidget *q; + + friend class AuthWidget; +-- +2.12.2 + diff --git a/kde-apps/libkgapi/files/libkgapi-17.04.0-auth2.patch b/kde-apps/libkgapi/files/libkgapi-17.04.0-auth2.patch new file mode 100644 index 000000000000..cba162a4f189 --- /dev/null +++ b/kde-apps/libkgapi/files/libkgapi-17.04.0-auth2.patch @@ -0,0 +1,36 @@ +From 5a20c7494f48da93914f50bbb54423ef540ef998 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Daniel=20Vr=C3=A1til?= <dvratil@kde.org> +Date: Thu, 27 Apr 2017 17:24:03 +0200 +Subject: [PATCH 2/3] Auth: don't store cookies + +Don't store WebEngine cookies, otherwise Google will display the account +that user signed in with the last time, which may not be desirable. +--- + src/core/ui/authwidget_p.cpp | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/core/ui/authwidget_p.cpp b/src/core/ui/authwidget_p.cpp +index 75d38cd..a51f4a9 100644 +--- a/src/core/ui/authwidget_p.cpp ++++ b/src/core/ui/authwidget_p.cpp +@@ -26,6 +26,7 @@ + #include "../../debug.h" + + #include <QWebEngineView> ++#include <QWebEngineProfile> + #include <QNetworkReply> + #include <QContextMenuEvent> + +@@ -36,6 +37,9 @@ using namespace KGAPI2; + WebView::WebView(QWidget *parent) + : QWebEngineView(parent) + { ++ // Don't store cookies, so that subsequent invocations of AuthJob won't remember ++ // the previous accounts. ++ QWebEngineProfile::defaultProfile()->setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies); + } + + +-- +2.12.2 + diff --git a/kde-apps/libkgapi/files/libkgapi-17.04.0-auth3.patch b/kde-apps/libkgapi/files/libkgapi-17.04.0-auth3.patch new file mode 100644 index 000000000000..61d3b306bed4 --- /dev/null +++ b/kde-apps/libkgapi/files/libkgapi-17.04.0-auth3.patch @@ -0,0 +1,310 @@ +From 7b0934611ef72fb7e7c405813a1d2bb8b944dadc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Daniel=20Vr=C3=A1til?= <dvratil@kde.org> +Date: Thu, 27 Apr 2017 18:41:45 +0200 +Subject: [PATCH 3/3] Auth: add URL bar and SSL indicator to the authentication + page + +To increase trust-worthiness of the authentication dialog we now display +the URL and an SSL indicator above the webview. +--- + src/core/ui/authwidget.cpp | 2 + + src/core/ui/authwidget_p.cpp | 137 +++++++++++++++++++++++++++++++++++++++---- + src/core/ui/authwidget_p.h | 25 ++++---- + 3 files changed, 136 insertions(+), 28 deletions(-) + +diff --git a/src/core/ui/authwidget.cpp b/src/core/ui/authwidget.cpp +index 18d2106..ac09b63 100644 +--- a/src/core/ui/authwidget.cpp ++++ b/src/core/ui/authwidget.cpp +@@ -107,6 +107,8 @@ void AuthWidget::authenticate() + + qCDebug(KGAPIRaw) << "Requesting new token:" << url; + ++ d->sslIndicator->setVisible(true); ++ d->urlEdit->setVisible(true); + d->webview->setVisible(true); + if (d->showProgressBar) { + d->progressbar->setVisible(true); +diff --git a/src/core/ui/authwidget_p.cpp b/src/core/ui/authwidget_p.cpp +index a51f4a9..f732935 100644 +--- a/src/core/ui/authwidget_p.cpp ++++ b/src/core/ui/authwidget_p.cpp +@@ -25,35 +25,79 @@ + #include "private/newtokensfetchjob_p.h" + #include "../../debug.h" + +-#include <QWebEngineView> + #include <QWebEngineProfile> + #include <QNetworkReply> + #include <QContextMenuEvent> + ++#include <QVBoxLayout> ++#include <QLabel> ++#include <QTimer> ++#include <QMessageBox> ++ + #include <QDateTime> + + using namespace KGAPI2; + +-WebView::WebView(QWidget *parent) +- : QWebEngineView(parent) ++namespace + { +- // Don't store cookies, so that subsequent invocations of AuthJob won't remember +- // the previous accounts. +- QWebEngineProfile::defaultProfile()->setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies); +-} + +- +-WebView::~WebView() ++class WebView : public QWebEngineView + { ++ Q_OBJECT ++public: ++ explicit WebView(QWidget *parent = nullptr) ++ : QWebEngineView(parent) ++ { ++ // Don't store cookies, so that subsequent invocations of AuthJob won't remember ++ // the previous accounts. ++ QWebEngineProfile::defaultProfile()->setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies); ++ } + +-} ++ void contextMenuEvent(QContextMenuEvent *e) Q_DECL_OVERRIDE ++ { ++ // No menu ++ e->accept(); ++ } ++}; + +-void WebView::contextMenuEvent(QContextMenuEvent *e) ++class WebPage : public QWebEnginePage + { +- // No menu +- e->accept(); ++ Q_OBJECT ++public: ++ explicit WebPage(QObject *parent = nullptr) ++ : QWebEnginePage(parent) ++ , mLastError(nullptr) ++ { ++ } ++ ++ QWebEngineCertificateError *lastCertificateError() const ++ { ++ return mLastError; ++ } ++ ++ bool certificateError(const QWebEngineCertificateError &err) Q_DECL_OVERRIDE ++ { ++ if (mLastError) { ++ delete mLastError; ++ } ++ mLastError = new QWebEngineCertificateError(err.error(), err.url(), err.isOverridable(), err.errorDescription()); ++ Q_EMIT sslError(); ++ ++ return false; // don't let it through ++ } ++ ++Q_SIGNALS: ++ void sslError(); ++ ++private: ++ QWebEngineCertificateError *mLastError; ++}; ++ + } + ++ ++ ++ + AuthWidgetPrivate::AuthWidgetPrivate(AuthWidget *parent): + QObject(), + showProgressBar(true), +@@ -67,6 +111,15 @@ AuthWidgetPrivate::~AuthWidgetPrivate() + { + } + ++void AuthWidgetPrivate::setSslIcon(const QString &iconName) ++{ ++ // FIXME: workaround for silly Breeze icons: the small 22x22 icons are ++ // monochromatic, which is absolutely useless since we are trying to security ++ // information here, so instead we force use the bigger 48x48 icons which ++ // have colors and downscale them ++ sslIndicator->setIcon(QIcon::fromTheme(iconName).pixmap(48)); ++} ++ + void AuthWidgetPrivate::setupUi() + { + vbox = new QVBoxLayout(q); +@@ -79,6 +132,26 @@ void AuthWidgetPrivate::setupUi() + label->setVisible(false); + vbox->addWidget(label); + ++ auto hbox = new QHBoxLayout; ++ hbox->setSpacing(0); ++ sslIndicator = new QToolButton(q); ++ connect(sslIndicator, &QToolButton::clicked, ++ this, [this]() { ++ auto page = qobject_cast<WebPage*>(webview->page()); ++ if (auto err = page->lastCertificateError()) { ++ QMessageBox msg; ++ msg.setIconPixmap(QIcon::fromTheme(QStringLiteral("security-low")).pixmap(64)); ++ msg.setText(err->errorDescription()); ++ msg.addButton(QMessageBox::Ok); ++ msg.exec(); ++ } ++ }); ++ hbox->addWidget(sslIndicator); ++ urlEdit = new QLineEdit(q); ++ urlEdit->setReadOnly(true); ++ hbox->addWidget(urlEdit); ++ vbox->addLayout(hbox); ++ + progressbar = new QProgressBar(q); + progressbar->setMinimum(0); + progressbar->setMaximum(100); +@@ -87,6 +160,13 @@ void AuthWidgetPrivate::setupUi() + + webview = new WebView(q); + ++ auto webpage = new WebPage(webview); ++ connect(webpage, &WebPage::sslError, ++ this, [this]() { ++ setSslIcon(QStringLiteral("security-low")); ++ }); ++ webview->setPage(webpage); ++ + vbox->addWidget(webview); + connect(webview, &QWebEngineView::loadProgress, progressbar, &QProgressBar::setValue); + connect(webview, &QWebEngineView::urlChanged, this, &AuthWidgetPrivate::webviewUrlChanged); +@@ -104,6 +184,8 @@ void AuthWidgetPrivate::setProgress(AuthWidget::Progress progress) + void AuthWidgetPrivate::emitError(const enum Error errCode, const QString& msg) + { + label->setVisible(true); ++ sslIndicator->setVisible(false); ++ urlEdit->setVisible(false); + webview->setVisible(false); + progressbar->setVisible(false); + +@@ -118,10 +200,33 @@ void AuthWidgetPrivate::webviewUrlChanged(const QUrl &url) + { + qCDebug(KGAPIDebug) << "URLChange:" << url; + ++ // Whoa! That should not happen! ++ if (url.scheme() != QLatin1String("https")) { ++ QTimer::singleShot(0, this, [this, url]() { ++ QUrl sslUrl = url; ++ sslUrl.setScheme(QStringLiteral("https")); ++ webview->setUrl(sslUrl); ++ }); ++ return; ++ } ++ + if (!isGoogleHost(url)) { ++ // We handled SSL above, so we are secure. We are however outside of ++ // accounts.google.com, which is a little suspicious in context of this class ++ setSslIcon(QStringLiteral("security-medium")); + return; + } + ++ if (qobject_cast<WebPage*>(webview->page())->lastCertificateError()) { ++ setSslIcon(QStringLiteral("security-low")); ++ } else { ++ // We have no way of obtaining current SSL certifiace from QWebEngine, but we ++ // handled SSL and accounts.google.com cases above and QWebEngine did not report ++ // any SSL error to us, so we can assume we are safe. ++ setSslIcon(QStringLiteral("security-high")); ++ } ++ ++ + // Username and password inputs are loaded dynamically, so we only get + // urlChanged, but not urlFinished. + if (isUsernameFrame(url)) { +@@ -145,6 +250,8 @@ void AuthWidgetPrivate::webviewUrlChanged(const QUrl &url) + } else if (isTokenPage(url)) { + /* Access token here - hide browser and tell user to wait until we + * finish the authentication process ourselves */ ++ sslIndicator->setVisible(false); ++ urlEdit->setVisible(false); + webview->setVisible(false); + progressbar->setVisible(false); + label->setVisible(true); +@@ -160,6 +267,8 @@ void AuthWidgetPrivate::webviewFinished(bool ok) + } + + const QUrl url = webview->url(); ++ urlEdit->setText(url.toDisplayString(QUrl::PrettyDecoded)); ++ urlEdit->setCursorPosition(0); + qCDebug(KGAPIDebug) << "URLFinished:" << url; + + if (!isGoogleHost(url)) { +@@ -238,3 +347,5 @@ void AuthWidgetPrivate::accountInfoReceived(KGAPI2::Job* job) + setProgress(AuthWidget::Finished); + } + ++ ++#include "authwidget_p.moc" +diff --git a/src/core/ui/authwidget_p.h b/src/core/ui/authwidget_p.h +index 9c488be..78b0e7f 100644 +--- a/src/core/ui/authwidget_p.h ++++ b/src/core/ui/authwidget_p.h +@@ -26,27 +26,18 @@ + #include "ui/authwidget.h" + #include "types.h" + ++#include <QLineEdit> ++#include <QToolButton> + #include <QProgressBar> +-#include <QVBoxLayout> + #include <QWebEngineView> +-#include <QLabel> ++ ++class QVBoxLayout; ++class QLabel; + + namespace KGAPI2 { + + class Job; + +-class WebView : public QWebEngineView +-{ +- Q_OBJECT +-public: +- explicit WebView(QWidget *parent=0); +- ~WebView(); +- +-protected: +- void contextMenuEvent(QContextMenuEvent *) Q_DECL_OVERRIDE; +-}; +- +- + class Q_DECL_HIDDEN AuthWidgetPrivate: public QObject { + + Q_OBJECT +@@ -65,9 +56,11 @@ class Q_DECL_HIDDEN AuthWidgetPrivate: public QObject { + QString apiKey; + QString secretKey; + ++ QToolButton *sslIndicator; ++ QLineEdit *urlEdit; + QProgressBar *progressbar; + QVBoxLayout *vbox; +- WebView *webview; ++ QWebEngineView *webview; + QLabel *label; + + private Q_SLOTS: +@@ -88,6 +81,8 @@ class Q_DECL_HIDDEN AuthWidgetPrivate: public QObject { + bool isPasswordFrame(const QUrl &url) { return url.path() == QLatin1String("/signin/v2/challenge/pwd"); } + bool isTokenPage(const QUrl &url) { return url.path() == QLatin1String("/o/oauth2/approval/v2"); } + ++ void setSslIcon(const QString &icon); ++ + AuthWidget *q; + + friend class AuthWidget; +-- +2.12.2 + diff --git a/kde-apps/libkgapi/libkgapi-17.04.0-r1.ebuild b/kde-apps/libkgapi/libkgapi-17.04.0-r1.ebuild new file mode 100644 index 000000000000..d48cb19e0dc8 --- /dev/null +++ b/kde-apps/libkgapi/libkgapi-17.04.0-r1.ebuild @@ -0,0 +1,36 @@ +# Copyright 1999-2017 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +EAPI=6 + +KDE_BLOCK_SLOT4="false" +KDE_TEST="true" +VIRTUALX_REQUIRED="test" +inherit kde5 + +DESCRIPTION="Library for accessing Google calendar and contact resources" +HOMEPAGE="https://projects.kde.org/projects/extragear/libs/libkgapi" + +LICENSE="LGPL-2.1+" +KEYWORDS="~amd64 ~x86" +IUSE="nls" + +COMMON_DEPEND=" + $(add_frameworks_dep kio) + $(add_frameworks_dep kwindowsystem) + $(add_kdeapps_dep kcalcore) + $(add_kdeapps_dep kcontacts) + $(add_qt_dep qtgui) + $(add_qt_dep qtnetwork) + $(add_qt_dep qtwebengine) + $(add_qt_dep qtwidgets) + $(add_qt_dep qtxml) +" +DEPEND="${COMMON_DEPEND} + nls? ( $(add_qt_dep linguist-tools) ) +" +RDEPEND="${COMMON_DEPEND} + !kde-apps/kdepim-l10n +" + +PATCHES=( "${FILESDIR}"/${P}-auth{1,2,3}.patch ) |