summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--kde-apps/libkgapi/files/libkgapi-17.04.0-auth1.patch168
-rw-r--r--kde-apps/libkgapi/files/libkgapi-17.04.0-auth2.patch36
-rw-r--r--kde-apps/libkgapi/files/libkgapi-17.04.0-auth3.patch310
-rw-r--r--kde-apps/libkgapi/libkgapi-17.04.0-r1.ebuild36
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 )