From ba959f7cf92542618b884ac6be49651d083c675c Mon Sep 17 00:00:00 2001 From: Daniel Molkentin Date: Wed, 14 May 2014 11:11:45 +0200 Subject: [PATCH] CookieJar refactoring required to overcome issues in Shibboleth support The shibboleth implementation no longer maintains its own QNAM. Instead, MirallAccessManager now holds a custom QNAM implementation which saves cookies to a file on disk. This patch also reduces some complexity wrt the browser window, which used to be deleted via a roundtrip to its callee, which is not longer required. Fixes #1764 and Enterprise bug #165 Going forward, AbstractCredentials::getQNAM() could maybe removed entirely. --- src/CMakeLists.txt | 4 +- .../shibboleth/shibbolethaccessmanager.cpp | 55 ------- .../shibboleth/shibbolethaccessmanager.h | 43 ----- src/creds/shibboleth/shibbolethconfigfile.cpp | 104 ------------ src/creds/shibboleth/shibbolethconfigfile.h | 38 ----- src/creds/shibboleth/shibbolethcookiejar.cpp | 34 ---- src/creds/shibboleth/shibbolethcookiejar.h | 41 ----- src/creds/shibboleth/shibbolethwebview.cpp | 74 +++------ src/creds/shibboleth/shibbolethwebview.h | 6 +- src/creds/shibbolethcredentials.cpp | 153 +++++++++--------- src/creds/shibbolethcredentials.h | 19 ++- src/mirall/account.cpp | 17 +- src/mirall/account.h | 3 + src/mirall/application.cpp | 1 + src/mirall/cookiejar.cpp | 148 +++++++++++++++++ src/mirall/cookiejar.h | 44 +++++ src/mirall/mirallaccessmanager.cpp | 3 + src/wizard/owncloudshibbolethcredspage.cpp | 58 +------ src/wizard/owncloudshibbolethcredspage.h | 9 +- 19 files changed, 336 insertions(+), 518 deletions(-) delete mode 100644 src/creds/shibboleth/shibbolethaccessmanager.cpp delete mode 100644 src/creds/shibboleth/shibbolethaccessmanager.h delete mode 100644 src/creds/shibboleth/shibbolethconfigfile.cpp delete mode 100644 src/creds/shibboleth/shibbolethconfigfile.h delete mode 100644 src/creds/shibboleth/shibbolethcookiejar.cpp delete mode 100644 src/creds/shibboleth/shibbolethcookiejar.h create mode 100644 src/mirall/cookiejar.cpp create mode 100644 src/mirall/cookiejar.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3085c10234..669854fd28 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -104,6 +104,7 @@ set(libsync_SRCS mirall/quotainfo.cpp mirall/clientproxy.cpp mirall/syncrunfilelog.cpp + mirall/cookiejar.cpp creds/dummycredentials.cpp creds/abstractcredentials.cpp creds/credentialsfactory.cpp @@ -121,11 +122,8 @@ else() ${libsync_SRCS} creds/httpcredentials.cpp creds/shibbolethcredentials.cpp - creds/shibboleth/shibbolethaccessmanager.cpp - creds/shibboleth/shibbolethcookiejar.cpp creds/shibboleth/shibbolethwebview.cpp creds/shibboleth/shibbolethrefresher.cpp - creds/shibboleth/shibbolethconfigfile.cpp creds/shibboleth/authenticationdialog.cpp creds/shibboleth/shibbolethuserjob.cpp ) diff --git a/src/creds/shibboleth/shibbolethaccessmanager.cpp b/src/creds/shibboleth/shibbolethaccessmanager.cpp deleted file mode 100644 index 451115cfc6..0000000000 --- a/src/creds/shibboleth/shibbolethaccessmanager.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) by Krzesimir Nowak - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - */ - -#include -#include -#include - -#include "creds/shibboleth/shibbolethaccessmanager.h" - -namespace Mirall -{ - -ShibbolethAccessManager::ShibbolethAccessManager(const QNetworkCookie& cookie, QObject* parent) - : MirallAccessManager (parent), - _cookie(cookie) -{} - -QNetworkReply* ShibbolethAccessManager::createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest& request, QIODevice* outgoingData) -{ - if (!_cookie.name().isEmpty()) { - QNetworkCookieJar* jar(cookieJar()); - QUrl url(request.url()); - QList cookies; - Q_FOREACH(const QNetworkCookie& cookie, jar->cookiesForUrl(url)) { - if (!cookie.name().startsWith("_shibsession_")) { - cookies << cookie; - } - } - - cookies << _cookie; // this line and the line above replace all cookies with self and then add the shibboleth cookie (filtering the current shib cookie) - jar->setCookiesFromUrl(cookies, url); - } - - qDebug() << "Creating a request to " << request.url().toString() << " with shibboleth cookie:" << _cookie.name(); - - return MirallAccessManager::createRequest (op, request, outgoingData); -} - -void ShibbolethAccessManager::setCookie(const QNetworkCookie& cookie) -{ - qDebug() << "Got new shibboleth cookie:" << cookie.name(); - _cookie = cookie; -} - -} // ns Mirall diff --git a/src/creds/shibboleth/shibbolethaccessmanager.h b/src/creds/shibboleth/shibbolethaccessmanager.h deleted file mode 100644 index e6ee4d25e4..0000000000 --- a/src/creds/shibboleth/shibbolethaccessmanager.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) by Krzesimir Nowak - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - */ - -#ifndef MIRALL_WIZARD_SHIBBOLETH_ACCESS_MANAGER_H -#define MIRALL_WIZARD_SHIBBOLETH_ACCESS_MANAGER_H - -#include - -#include "mirall/mirallaccessmanager.h" - -namespace Mirall -{ - -class ShibbolethAccessManager : public MirallAccessManager -{ - Q_OBJECT - -public: - ShibbolethAccessManager(const QNetworkCookie& cookie, QObject* parent = 0); - -public Q_SLOTS: - void setCookie(const QNetworkCookie& cookie); - -protected: - QNetworkReply* createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest& request, QIODevice* outgoingData = 0); - -private: - QNetworkCookie _cookie; -}; - -} // ns Mirall - -#endif diff --git a/src/creds/shibboleth/shibbolethconfigfile.cpp b/src/creds/shibboleth/shibbolethconfigfile.cpp deleted file mode 100644 index fa24bef76b..0000000000 --- a/src/creds/shibboleth/shibbolethconfigfile.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) by Krzesimir Nowak - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - */ - -#include -#include - -#include "creds/shibboleth/shibbolethconfigfile.h" -#include "creds/shibboleth/shibbolethcookiejar.h" - -namespace Mirall -{ - -namespace -{ - -const char otherCookiesC[] = "otherCookies"; - -} // ns - -void ShibbolethConfigFile::storeCookies(const QMap >& cookiesForUrl) -{ - if (cookiesForUrl.isEmpty()) { - removeData(QString(), QString::fromLatin1(otherCookiesC)); - } else { - QByteArray data; - QTextStream stream(&data); - - Q_FOREACH (const QUrl& url, cookiesForUrl.keys()) { - const QList& cookies(cookiesForUrl[url]); - - if (cookies.isEmpty()) { - continue; - } - stream << "URL: " << url.toString().toUtf8() << "\n"; - qDebug() << "URL: " << url.toString().toUtf8(); - - Q_FOREACH (const QNetworkCookie& cookie, cookies) { - stream << cookie.toRawForm(QNetworkCookie::NameAndValueOnly) << "\n"; - qDebug() << cookie.toRawForm(QNetworkCookie::NameAndValueOnly); - } - } - - stream.flush(); - - const QByteArray encodedCookies(data.toBase64()); - - qDebug() << "Raw cookies:\n" << data; - qDebug() << "Encoded cookies: " << encodedCookies; - - storeData(QString(), QString::fromLatin1(otherCookiesC), QVariant(encodedCookies)); - } -} - -ShibbolethCookieJar* ShibbolethConfigFile::createCookieJar() const -{ - ShibbolethCookieJar* jar = new ShibbolethCookieJar(); - const QVariant variant(retrieveData(QString(), QString::fromLatin1(otherCookiesC))); - - if (variant.isValid()) { - QByteArray data(QByteArray::fromBase64(variant.toByteArray())); - QTextStream stream (&data); - const QString urlHeader(QString::fromLatin1("URL: ")); - QUrl currentUrl; - QList currentCookies; - - qDebug() << "Got valid cookies variant: " << data; - - while (!stream.atEnd()) { - const QString line(stream.readLine()); - - qDebug() << line; - - if (line.startsWith(urlHeader)) { - if (!currentUrl.isEmpty() && !currentCookies.isEmpty()) { - jar->setCookiesFromUrl(currentCookies, currentUrl); - currentCookies.clear(); - currentUrl.clear(); - } - currentUrl = QUrl(line.mid(5)); - } else if (!currentUrl.isEmpty()) { - const int equalPos(line.indexOf('=')); - - currentCookies << QNetworkCookie(line.left(equalPos).toUtf8(), line.mid(equalPos + 1).toUtf8()); - } - } - if (!currentUrl.isEmpty() && !currentCookies.isEmpty()) { - jar->setCookiesFromUrl(currentCookies, currentUrl); - } - } - - return jar; -} - -} // ns Mirall diff --git a/src/creds/shibboleth/shibbolethconfigfile.h b/src/creds/shibboleth/shibbolethconfigfile.h deleted file mode 100644 index 2efc0f729f..0000000000 --- a/src/creds/shibboleth/shibbolethconfigfile.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) by Krzesimir Nowak - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - */ - -#ifndef MIRALL_CREDS_SHIBBOLETH_CONFIG_FILE_H -#define MIRALL_CREDS_SHIBBOLETH_CONFIG_FILE_H - -#include -#include -#include -#include - -#include "mirall/mirallconfigfile.h" - -namespace Mirall -{ - -class ShibbolethCookieJar; - -class ShibbolethConfigFile : public MirallConfigFile -{ -public: - void storeCookies(const QMap >& cookies); - ShibbolethCookieJar* createCookieJar() const; -}; - -} // ns Mirall - -#endif diff --git a/src/creds/shibboleth/shibbolethcookiejar.cpp b/src/creds/shibboleth/shibbolethcookiejar.cpp deleted file mode 100644 index ad86d6ca60..0000000000 --- a/src/creds/shibboleth/shibbolethcookiejar.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) by Krzesimir Nowak - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - */ - -#include "creds/shibboleth/shibbolethcookiejar.h" - -namespace Mirall -{ - -ShibbolethCookieJar::ShibbolethCookieJar (QObject* parent) - : QNetworkCookieJar (parent) -{} - -bool ShibbolethCookieJar::setCookiesFromUrl (const QList& cookieList, const QUrl& url) -{ - if (QNetworkCookieJar::setCookiesFromUrl (cookieList, url)) { - Q_EMIT newCookiesForUrl (cookieList, url); - - return true; - } - - return false; -} - -} // ns Mirall diff --git a/src/creds/shibboleth/shibbolethcookiejar.h b/src/creds/shibboleth/shibbolethcookiejar.h deleted file mode 100644 index 4e4459ba5a..0000000000 --- a/src/creds/shibboleth/shibbolethcookiejar.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) by Krzesimir Nowak - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - */ - -#ifndef MIRALL_WIZARD_SHIBBOLETH_COOKIE_JAR_H -#define MIRALL_WIZARD_SHIBBOLETH_COOKIE_JAR_H - -#include -#include - -class QUrl; -class QNetworkCookie; - -namespace Mirall -{ - -class ShibbolethCookieJar : public QNetworkCookieJar -{ - Q_OBJECT - -public: - ShibbolethCookieJar (QObject* parent = 0); - - virtual bool setCookiesFromUrl (const QList& cookieList, const QUrl& url); - -Q_SIGNALS: - void newCookiesForUrl (const QList& cookieList, const QUrl& url); -}; - -} // ns Mirall - -#endif diff --git a/src/creds/shibboleth/shibbolethwebview.cpp b/src/creds/shibboleth/shibbolethwebview.cpp index b8e8e7ee3b..d33bed4538 100644 --- a/src/creds/shibboleth/shibbolethwebview.cpp +++ b/src/creds/shibboleth/shibbolethwebview.cpp @@ -20,9 +20,9 @@ #include #include -#include "creds/shibboleth/shibbolethcookiejar.h" -#include "creds/shibboleth/shibbolethwebview.h" #include "creds/shibboleth/authenticationdialog.h" +#include "creds/shibboleth/shibbolethwebview.h" +#include "creds/shibbolethcredentials.h" #include "mirall/account.h" #include "mirall/mirallaccessmanager.h" #include "mirall/theme.h" @@ -30,80 +30,50 @@ namespace Mirall { -void ShibbolethWebView::setup(Account *account, ShibbolethCookieJar* jar) +ShibbolethWebView::ShibbolethWebView(Account* account, QWidget* parent) + : QWebView(parent) + , _account(account) { - _account = account; - MirallAccessManager* nm = new MirallAccessManager(this); - // we need our own QNAM, but the we offload the SSL error handling to - // the account object, which already can do this - connect(nm, SIGNAL(sslErrors(QNetworkReply*,QList)), - account, SLOT(slotHandleErrors(QNetworkReply*,QList))); - connect(nm, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), - SLOT(slotHandleAuthentication(QNetworkReply*,QAuthenticator*))); - + // no minimize + setWindowFlags(Qt::Dialog); + setAttribute(Qt::WA_DeleteOnClose); QWebPage* page = new QWebPage(this); - - jar->setParent(this); - connect(jar, SIGNAL (newCookiesForUrl (QList, QUrl)), - this, SLOT (onNewCookiesForUrl (QList, QUrl))); + page->setNetworkAccessManager(account->networkAccessManager()); connect(page, SIGNAL(loadStarted()), this, SLOT(slotLoadStarted())); connect(page, SIGNAL(loadFinished(bool)), this, SLOT(slotLoadFinished(bool))); - nm->setCookieJar(jar); - page->setNetworkAccessManager(nm); + + connect(page->networkAccessManager()->cookieJar(), + SIGNAL(newCookiesForUrl (QList, QUrl)), + this, SLOT(onNewCookiesForUrl (QList, QUrl))); page->mainFrame()->load(account->url()); this->setPage(page); setWindowTitle(tr("%1 - Authenticate").arg(Theme::instance()->appNameGUI())); } -ShibbolethWebView::ShibbolethWebView(Account* account, QWidget* parent) - : QWebView(parent) -{ - setup(account, new ShibbolethCookieJar(this)); -} - ShibbolethWebView::~ShibbolethWebView() { slotLoadFinished(); } -ShibbolethWebView::ShibbolethWebView(Account* account, ShibbolethCookieJar* jar, QWidget* parent) - : QWebView(parent) -{ - setup(account, jar); -} - void ShibbolethWebView::onNewCookiesForUrl (const QList& cookieList, const QUrl& url) { - QList otherCookies; - QNetworkCookie shibCookie; - - Q_FOREACH (const QNetworkCookie& cookie, cookieList) { - if (cookie.name().startsWith ("_shibsession_")) { - if (shibCookie.name().isEmpty()) { - shibCookie = cookie; - } else { - qWarning() << "Too many Shibboleth session cookies at once!"; - } - } else { - otherCookies << cookie; + qDebug() << "Received cookies for URL" << url << ":" << cookieList << ". Account object has URL" << _account->url(); + if (url.host() == _account->url().host()) { + QNetworkCookie shibCookie = ShibbolethCredentials::findShibCookie(_account, cookieList); + if (shibCookie != QNetworkCookie()) { + Q_EMIT shibbolethCookieReceived(shibCookie, _account); + close(); + } } - } - - if (!otherCookies.isEmpty()) { - Q_EMIT otherCookiesReceived(otherCookies, url); - } - if (!shibCookie.name().isEmpty()) { - Q_EMIT shibbolethCookieReceived(shibCookie, _account); - } } -void ShibbolethWebView::hideEvent(QHideEvent* event) +void ShibbolethWebView::closeEvent(QCloseEvent *event) { Q_EMIT viewHidden(); - QWebView::hideEvent(event); + QWebView::closeEvent(event); } void ShibbolethWebView::slotLoadStarted() diff --git a/src/creds/shibboleth/shibbolethwebview.h b/src/creds/shibboleth/shibbolethwebview.h index 5a988fe46a..eb0db63347 100644 --- a/src/creds/shibboleth/shibbolethwebview.h +++ b/src/creds/shibboleth/shibbolethwebview.h @@ -37,13 +37,11 @@ public: ShibbolethWebView(Account *account, ShibbolethCookieJar* jar, QWidget* parent = 0); ~ShibbolethWebView(); -protected: - void hideEvent(QHideEvent* event); + void closeEvent(QCloseEvent *event); Q_SIGNALS: - void shibbolethCookieReceived(const QNetworkCookie& cookie, Account* account); + void shibbolethCookieReceived(const QNetworkCookie &cookie, Account *account); void viewHidden(); - void otherCookiesReceived(const QList& cookieList, const QUrl& url); private Q_SLOTS: void onNewCookiesForUrl(const QList& cookieList, const QUrl& url); diff --git a/src/creds/shibbolethcredentials.cpp b/src/creds/shibbolethcredentials.cpp index fda7ab4485..b074cd7b0f 100644 --- a/src/creds/shibbolethcredentials.cpp +++ b/src/creds/shibbolethcredentials.cpp @@ -16,18 +16,19 @@ #include #include #include -#include +#include #include "creds/shibbolethcredentials.h" -#include "creds/shibboleth/shibbolethaccessmanager.h" #include "creds/shibboleth/shibbolethwebview.h" #include "creds/shibboleth/shibbolethrefresher.h" -#include "creds/shibboleth/shibbolethconfigfile.h" +#include "creds/shibbolethcredentials.h" #include "shibboleth/shibbolethuserjob.h" #include "creds/credentialscommon.h" +#include "mirall/mirallaccessmanager.h" #include "mirall/account.h" #include "mirall/theme.h" +#include "mirall/cookiejar.h" #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include @@ -45,6 +46,7 @@ namespace // Not "user" because it has a special meaning for http const char userC[] = "shib_user"; +const char shibCookieNameC[] = "_shibsession_"; int shibboleth_redirect_callback(CSYNC* csync_ctx, const char* uri) @@ -66,7 +68,6 @@ int shibboleth_redirect_callback(CSYNC* csync_ctx, Account *account = AccountManager::instance()->account(); ShibbolethCredentials* creds = qobject_cast(account->credentials()); - if (!creds) { qDebug() << "Not a Shibboleth creds instance!"; return 1; @@ -85,18 +86,9 @@ int shibboleth_redirect_callback(CSYNC* csync_ctx, ShibbolethCredentials::ShibbolethCredentials() : AbstractCredentials(), _url(), - _shibCookie(), _ready(false), _stillValid(false), - _browser(0), - _otherCookies() -{} - -ShibbolethCredentials::ShibbolethCredentials(const QNetworkCookie& cookie, const QMap >& otherCookies) - : _shibCookie(cookie), - _ready(true), - _browser(0), - _otherCookies(otherCookies) + _browser(0) {} void ShibbolethCredentials::syncContextPreInit(CSYNC* ctx) @@ -111,29 +103,11 @@ QByteArray ShibbolethCredentials::prepareCookieData() const // have any way to get "session_key" module property from // csync. Had we have it, then we could just append shibboleth // cookies to the "session_key" value and set it in csync module. - QList cookies(AccountManager::instance()->account()->lastAuthCookies()); - QMap uniqueCookies; + Account *account = AccountManager::instance()->account(); + QList cookies = accountCookies(account); - cookies << _shibCookie; - // Stuff cookies inside csync, then we can avoid the intermediate HTTP 401 reply - // when https://github.com/owncloud/core/pull/4042 is merged. - foreach(QNetworkCookie c, cookies) { - const QString cookieName(c.name()); - - if (cookieName.startsWith("_shibsession_")) { - continue; - } - uniqueCookies.insert(cookieName, c.value()); - } - - if (!_shibCookie.name().isEmpty()) { - uniqueCookies.insert(_shibCookie.name(), _shibCookie.value()); - } - foreach(const QString& cookieName, uniqueCookies.keys()) { - cookiesAsString += cookieName; - cookiesAsString += '='; - cookiesAsString += uniqueCookies[cookieName]; - cookiesAsString += "; "; + foreach(const QNetworkCookie &cookie, cookies) { + cookiesAsString += cookie.toRawForm(QNetworkCookie::NameAndValueOnly) + QLatin1String("; "); } return cookiesAsString.toLatin1(); @@ -153,7 +127,7 @@ bool ShibbolethCredentials::changed(AbstractCredentials* credentials) const { ShibbolethCredentials* other(dynamic_cast< ShibbolethCredentials* >(credentials)); - if (!other || other->cookie() != this->cookie()) { + if (_shibCookie != other->_shibCookie || _user != other->_user) { return true; } @@ -170,17 +144,9 @@ QString ShibbolethCredentials::user() const return _user; } -QNetworkCookie ShibbolethCredentials::cookie() const -{ - return _shibCookie; -} - QNetworkAccessManager* ShibbolethCredentials::getQNAM() const { - ShibbolethAccessManager* qnam(new ShibbolethAccessManager(_shibCookie)); - - connect(this, SIGNAL(newCookie(QNetworkCookie)), - qnam, SLOT(setCookie(QNetworkCookie))); + QNetworkAccessManager* qnam(new MirallAccessManager); connect(qnam, SIGNAL(finished(QNetworkReply*)), this, SLOT(slotReplyFinished(QNetworkReply*))); return qnam; @@ -231,44 +197,30 @@ bool ShibbolethCredentials::stillValid(QNetworkReply *reply) void ShibbolethCredentials::persist(Account* account) { - ShibbolethConfigFile cfg; - - cfg.storeCookies(_otherCookies); - storeShibCookie(_shibCookie, account); - if (!_user.isEmpty()) + if (!_user.isEmpty()) { account->setCredentialSetting(QLatin1String(userC), _user); + } } // only used by Application::slotLogout(). Use invalidateAndFetch for normal usage void ShibbolethCredentials::invalidateToken(Account *account) { Q_UNUSED(account) + if (!removeFromCookieJar(_shibCookie)) { + qDebug() << "invalidateToken() called but no shibCookie in in cookie jar!"; + } + removeShibCookie(account); _shibCookie = QNetworkCookie(); - storeShibCookie(_shibCookie, account); // store/erase cookie - // ### access to ctx missing, but might not be required at all //csync_set_module_property(ctx, "session_key", ""); } -void ShibbolethCredentials::disposeBrowser() +void ShibbolethCredentials::onShibbolethCookieReceived(const QNetworkCookie& shibCookie, Account *account) { - qDebug() << Q_FUNC_INFO; - disconnect(_browser, SIGNAL(viewHidden()), - this, SLOT(slotBrowserHidden())); - disconnect(_browser, SIGNAL(shibbolethCookieReceived(QNetworkCookie, Account*)), - this, SLOT(onShibbolethCookieReceived(QNetworkCookie, Account*))); - _browser->hide(); - _browser->deleteLater(); - _browser = 0; -} - -void ShibbolethCredentials::onShibbolethCookieReceived(const QNetworkCookie& cookie, Account* account) -{ - disposeBrowser(); - _shibCookie = cookie; - storeShibCookie(_shibCookie, account); - Q_EMIT newCookie(_shibCookie); + storeShibCookie(shibCookie, account); + _shibCookie = shibCookie; + addToCookieJar(shibCookie); // Now fetch the user... // But we must first do a request to webdav so the session is enabled. @@ -311,9 +263,7 @@ void ShibbolethCredentials::slotUserFetched(const QString &user) void ShibbolethCredentials::slotBrowserHidden() { - disposeBrowser(); _ready = false; - _shibCookie = QNetworkCookie(); Q_EMIT fetched(); } @@ -356,16 +306,16 @@ void ShibbolethCredentials::slotReadJobDone(QKeychain::Job *job) if (job->error() == QKeychain::NoError) { ReadPasswordJob *readJob = static_cast(job); delete readJob->settings(); - qDebug() << Q_FUNC_INFO; QList cookies = QNetworkCookie::parseCookies(readJob->textData().toUtf8()); if (cookies.count() > 0) { _shibCookie = cookies.first(); + addToCookieJar(_shibCookie); } + // access job->setSettings(account->settingsWithGroup(Theme::instance()->appName(), job)); _ready = true; _stillValid = true; - Q_EMIT newCookie(_shibCookie); Q_EMIT fetched(); } else { showLoginWindow(account); @@ -374,14 +324,13 @@ void ShibbolethCredentials::slotReadJobDone(QKeychain::Job *job) void ShibbolethCredentials::showLoginWindow(Account* account) { - if (_browser) { + if (!_browser.isNull()) { _browser->activateWindow(); _browser->raise(); // FIXME On OS X this does not raise properly return; } - ShibbolethConfigFile cfg; - _browser = new ShibbolethWebView(account, cfg.createCookieJar()); + _browser = new ShibbolethWebView(account); connect(_browser, SIGNAL(shibbolethCookieReceived(QNetworkCookie, Account*)), this, SLOT(onShibbolethCookieReceived(QNetworkCookie, Account*))); connect(_browser, SIGNAL(viewHidden()), @@ -392,6 +341,30 @@ void ShibbolethCredentials::showLoginWindow(Account* account) _browser->show(); } +QList ShibbolethCredentials::accountCookies(Account *account) +{ + return account->networkAccessManager()->cookieJar()->cookiesForUrl(account->url()); +} + +QNetworkCookie ShibbolethCredentials::findShibCookie(Account *account, QList cookies) +{ + if(cookies.isEmpty()) { + cookies = accountCookies(account); + } + + Q_FOREACH(QNetworkCookie cookie, cookies) { + if (cookie.name().startsWith(shibCookieNameC)) { + return cookie; + } + } + return QNetworkCookie(); +} + +QByteArray ShibbolethCredentials::shibCookieName() +{ + return QByteArray(shibCookieNameC); +} + void ShibbolethCredentials::storeShibCookie(const QNetworkCookie &cookie, Account *account) { WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName()); @@ -403,5 +376,31 @@ void ShibbolethCredentials::storeShibCookie(const QNetworkCookie &cookie, Accoun job->start(); } +void ShibbolethCredentials::removeShibCookie(Account *account) +{ + DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName()); + job->setSettings(account->settingsWithGroup(Theme::instance()->appName(), job)); + job->setKey(keychainKey(account->url().toString(), "shibAssertion")); + job->start(); +} + +void ShibbolethCredentials::addToCookieJar(const QNetworkCookie &cookie) +{ + QList cookies; + cookies << cookie; + Account *account = AccountManager::instance()->account(); + QNetworkCookieJar *jar = account->networkAccessManager()->cookieJar(); + jar->blockSignals(true); // otherwise we'd call ourselves + jar->setCookiesFromUrl(cookies, account->url()); + jar->blockSignals(false); +} + +bool ShibbolethCredentials::removeFromCookieJar(const QNetworkCookie &cookie) +{ + Account *account = AccountManager::instance()->account(); + CookieJar *jar = qobject_cast(account->networkAccessManager()->cookieJar()); + return jar->deleteCookie(cookie); +} + } // ns Mirall diff --git a/src/creds/shibbolethcredentials.h b/src/creds/shibbolethcredentials.h index 217f852b22..a8d5d72407 100644 --- a/src/creds/shibbolethcredentials.h +++ b/src/creds/shibbolethcredentials.h @@ -18,6 +18,7 @@ #include #include #include +#include #include "creds/abstractcredentials.h" @@ -36,7 +37,6 @@ Q_OBJECT public: ShibbolethCredentials(); - ShibbolethCredentials(const QNetworkCookie& cookie, const QMap >& otherCookies); void syncContextPreInit(CSYNC* ctx); void syncContextPreStart(CSYNC* ctx); @@ -50,15 +50,17 @@ public: void persist(Account *account); void invalidateToken(Account *account); - QNetworkCookie cookie() const; - void showLoginWindow(Account*); + static QList accountCookies(Account*); + static QNetworkCookie findShibCookie(Account*, QList cookies = QList()); + static QByteArray shibCookieName(); + public Q_SLOTS: void invalidateAndFetch(Account *account); private Q_SLOTS: - void onShibbolethCookieReceived(const QNetworkCookie& cookie, Account*); + void onShibbolethCookieReceived(const QNetworkCookie&, Account*); void slotBrowserHidden(); void onFetched(); void slotReadJobDone(QKeychain::Job*); @@ -73,15 +75,16 @@ Q_SIGNALS: private: void storeShibCookie(const QNetworkCookie &cookie, Account *account); + void removeShibCookie(Account *account); + void addToCookieJar(const QNetworkCookie &cookie); + bool removeFromCookieJar(const QNetworkCookie &cookie); QUrl _url; QByteArray prepareCookieData() const; - void disposeBrowser(); - QNetworkCookie _shibCookie; bool _ready; bool _stillValid; - ShibbolethWebView* _browser; - QMap > _otherCookies; + QPointer _browser; + QNetworkCookie _shibCookie; QString _user; }; diff --git a/src/mirall/account.cpp b/src/mirall/account.cpp index f04c06330b..4e376d0538 100644 --- a/src/mirall/account.cpp +++ b/src/mirall/account.cpp @@ -12,9 +12,11 @@ */ #include "mirall/account.h" +#include "mirall/cookiejar.h" #include "mirall/theme.h" #include "mirall/networkjobs.h" #include "mirall/mirallconfigfile.h" +#include "mirall/mirallaccessmanager.h" #include "mirall/quotainfo.h" #include "creds/abstractcredentials.h" #include "creds/credentialsfactory.h" @@ -75,6 +77,7 @@ Account::Account(AbstractSslErrorHandler *sslErrorHandler, QObject *parent) Account::~Account() { + delete _am; } void Account::save() @@ -161,7 +164,11 @@ AbstractCredentials *Account::credentials() const void Account::setCredentials(AbstractCredentials *cred) { // set active credential manager + QNetworkCookieJar *jar = 0; if (_am) { + jar = _am->cookieJar(); + jar->setParent(0); + _am->deleteLater(); } @@ -170,6 +177,9 @@ void Account::setCredentials(AbstractCredentials *cred) } _credentials = cred; _am = _credentials->getQNAM(); + if (jar) { + _am->setCookieJar(jar); + } connect(_am, SIGNAL(sslErrors(QNetworkReply*,QList)), SLOT(slotHandleErrors(QNetworkReply*,QList))); } @@ -186,7 +196,12 @@ QList Account::lastAuthCookies() const void Account::clearCookieJar() { - _am->setCookieJar(new QNetworkCookieJar); + _am->setCookieJar(new CookieJar); +} + +QNetworkAccessManager *Account::networkAccessManager() +{ + return _am; } QNetworkReply *Account::headRequest(const QString &relPath) diff --git a/src/mirall/account.h b/src/mirall/account.h index 32f19e6092..d782a7498d 100644 --- a/src/mirall/account.h +++ b/src/mirall/account.h @@ -34,6 +34,7 @@ namespace Mirall { class AbstractCredentials; class Account; class QuotaInfo; +class MirallAccessManager; class OWNCLOUDSYNC_EXPORT AccountManager : public QObject { Q_OBJECT @@ -143,6 +144,8 @@ public: void clearCookieJar(); + QNetworkAccessManager* networkAccessManager(); + QuotaInfo *quotaInfo(); signals: void stateChanged(int state); diff --git a/src/mirall/application.cpp b/src/mirall/application.cpp index aa1ed30811..9f16e5bd93 100644 --- a/src/mirall/application.cpp +++ b/src/mirall/application.cpp @@ -159,6 +159,7 @@ Application::Application(int &argc, char **argv) : Application::~Application() { + delete AccountManager::instance()->account(); // qDebug() << "* Mirall shutdown"; } diff --git a/src/mirall/cookiejar.cpp b/src/mirall/cookiejar.cpp new file mode 100644 index 0000000000..49f8b64f23 --- /dev/null +++ b/src/mirall/cookiejar.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (C) by Daniel Molkentin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "cookiejar.h" + +#include "mirall/mirallconfigfile.h" + +#include +#include +#include + +namespace Mirall { + +namespace { + const unsigned int JAR_VERSION = 23; +} + +QDataStream &operator<<(QDataStream &stream, const QList &list) +{ + stream << JAR_VERSION; + stream << quint32(list.size()); + for (int i = 0; i < list.size(); ++i) + stream << list.at(i).toRawForm(); + return stream; +} + +QDataStream &operator>>(QDataStream &stream, QList &list) +{ + list.clear(); + + quint32 version; + stream >> version; + + if (version != JAR_VERSION) + return stream; + + quint32 count; + stream >> count; + for(quint32 i = 0; i < count; ++i) + { + QByteArray value; + stream >> value; + QList newCookies = QNetworkCookie::parseCookies(value); + if (newCookies.count() == 0 && value.length() != 0) { + qWarning() << "CookieJar: Unable to parse saved cookie:" << value; + } + for (int j = 0; j < newCookies.count(); ++j) + list.append(newCookies.at(j)); + if (stream.atEnd()) + break; + } + return stream; +} + +CookieJar::CookieJar(QObject *parent) : + QNetworkCookieJar(parent) +{ + restore(); +} + +CookieJar::~CookieJar() +{ + save(); +} + +bool CookieJar::setCookiesFromUrl(const QList& cookieList, const QUrl& url) +{ + if (QNetworkCookieJar::setCookiesFromUrl(cookieList, url)) { + Q_EMIT newCookiesForUrl(cookieList, url); + return true; + } + + return false; +} + +QList CookieJar::cookiesForUrl(const QUrl &url) +{ + QList cookies = QNetworkCookieJar::cookiesForUrl(url); + qDebug() << url << "requests:" << cookies; + return cookies; +} + +bool CookieJar::deleteCookie(const QNetworkCookie &delCookie) +{ + QList cookies = allCookies(); + bool removeSucceeded = false; + foreach(const QNetworkCookie &cookie, cookies) { + // ### cookies are not identical in attriutes, why? + if (cookie.name() == delCookie.name()) { + cookies.removeOne(cookie); + removeSucceeded = true; + } + } + setAllCookies(cookies); + return removeSucceeded; +} + +void CookieJar::save() +{ + QFile file; + file.setFileName(storagePath()); + qDebug() << storagePath(); + file.open(QIODevice::WriteOnly); + QDataStream stream(&file); + stream << allCookies(); + file.close(); +} + +void CookieJar::restore() +{ + QFile file; + file.setFileName(storagePath()); + file.open(QIODevice::ReadOnly); + QDataStream stream(&file); + QList list; + stream >> list; + setAllCookies(removeExpired(list)); + file.close(); +} + +QList CookieJar::removeExpired(const QList &cookies) +{ + QList updatedList; + foreach(const QNetworkCookie &cookie, cookies) { + if (cookie.expirationDate() > QDateTime::currentDateTime()) { + updatedList << cookie; + } + } + return updatedList; +} + +QString CookieJar::storagePath() const +{ + MirallConfigFile cfg; + return cfg.configPath() + "/cookies.db"; +} + +} // namespace Mirall diff --git a/src/mirall/cookiejar.h b/src/mirall/cookiejar.h new file mode 100644 index 0000000000..2c7959bcc9 --- /dev/null +++ b/src/mirall/cookiejar.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) by Daniel Molkentin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef MIRALL_COOKIEJAR_H +#define MIRALL_COOKIEJAR_H + +#include + +namespace Mirall { + +class CookieJar : public QNetworkCookieJar +{ + Q_OBJECT +public: + explicit CookieJar(QObject *parent = 0); + ~CookieJar(); + bool setCookiesFromUrl(const QList &cookieList, const QUrl &url); + QList cookiesForUrl(const QUrl &url); + + virtual bool deleteCookie(const QNetworkCookie & cookie); + +signals: + void newCookiesForUrl(const QList& cookieList, const QUrl& url); +private: + void save(); + void restore(); + QList removeExpired(const QList &cookies); + QString storagePath() const; + +}; + +} // namespace Mirall + +#endif // MIRALL_COOKIEJAR_H diff --git a/src/mirall/mirallaccessmanager.cpp b/src/mirall/mirallaccessmanager.cpp index de0322b6a3..08de69210e 100644 --- a/src/mirall/mirallaccessmanager.cpp +++ b/src/mirall/mirallaccessmanager.cpp @@ -15,6 +15,7 @@ #include #include +#include "mirall/cookiejar.h" #include "mirall/mirallaccessmanager.h" #include "mirall/utility.h" @@ -30,6 +31,8 @@ MirallAccessManager::MirallAccessManager(QObject* parent) proxy.setHostName(" "); setProxy(proxy); #endif + setCookieJar(new CookieJar); + cookieJar()->setParent(0); QObject::connect(this, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), this, SLOT(slotProxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); } diff --git a/src/wizard/owncloudshibbolethcredspage.cpp b/src/wizard/owncloudshibbolethcredspage.cpp index a71417ca40..10d8639c6a 100644 --- a/src/wizard/owncloudshibbolethcredspage.cpp +++ b/src/wizard/owncloudshibbolethcredspage.cpp @@ -26,24 +26,20 @@ namespace Mirall OwncloudShibbolethCredsPage::OwncloudShibbolethCredsPage() : AbstractCredentialsWizardPage(), _browser(0), - _cookie(), - _afterInitialSetup(false), - _cookiesForUrl() + _afterInitialSetup(false) {} void OwncloudShibbolethCredsPage::setupBrowser() { - if (_browser) { + if (!_browser.isNull()) { return; } OwncloudWizard *ocWizard = qobject_cast(wizard()); _browser = new ShibbolethWebView(ocWizard->account()); - connect(_browser, SIGNAL(shibbolethCookieReceived(QNetworkCookie, Account*)), - this, SLOT(slotShibbolethCookieReceived(QNetworkCookie, Account*))); + connect(_browser, SIGNAL(shibbolethCookieReceived(const QNetworkCookie&, Account*)), + this, SLOT(slotShibbolethCookieReceived())); connect(_browser, SIGNAL(viewHidden()), this, SLOT(slotViewHidden())); - connect(_browser, SIGNAL(otherCookiesReceived(QList, QUrl)), - this, SLOT(slotOtherCookiesReceived(QList, QUrl))); _browser->move(ocWizard->x(), ocWizard->y()); _browser->show(); @@ -60,9 +56,6 @@ void OwncloudShibbolethCredsPage::setVisible(bool visible) if (isVisible() == visible) { return; } - if (_browser) { - disposeBrowser(); - } if (visible) { setupBrowser(); wizard()->hide(); @@ -74,23 +67,6 @@ void OwncloudShibbolethCredsPage::setVisible(bool visible) void OwncloudShibbolethCredsPage::initializePage() { _afterInitialSetup = true; - _cookie = QNetworkCookie(); - _cookiesForUrl.clear(); -} - -void OwncloudShibbolethCredsPage::disposeBrowser() -{ - if (_browser) { - disconnect(_browser, SIGNAL(otherCookiesReceived(QList, QUrl)), - this, SLOT(slotOtherCookiesReceived(QList, QUrl))); - disconnect(_browser, SIGNAL(viewHidden()), - this, SLOT(slotViewHidden())); - disconnect(_browser, SIGNAL(shibbolethCookieReceived(QNetworkCookie, Account*)), - this, SLOT(slotShibbolethCookieReceived(QNetworkCookie, Account*))); - _browser->hide(); - _browser->deleteLater(); - _browser = 0; - } } int OwncloudShibbolethCredsPage::nextId() const @@ -105,38 +81,16 @@ void OwncloudShibbolethCredsPage::setConnected() AbstractCredentials* OwncloudShibbolethCredsPage::getCredentials() const { - return new ShibbolethCredentials(_cookie, _cookiesForUrl); + return new ShibbolethCredentials; } -void OwncloudShibbolethCredsPage::slotShibbolethCookieReceived(const QNetworkCookie& cookie, Account*) +void OwncloudShibbolethCredsPage::slotShibbolethCookieReceived() { - disposeBrowser(); - _cookie = cookie; emit connectToOCUrl(field("OCUrl").toString().simplified()); } -void OwncloudShibbolethCredsPage::slotOtherCookiesReceived(const QList& cookieList, const QUrl& url) -{ - QList& cookies(_cookiesForUrl[url]); - QMap uniqueCookies; - - Q_FOREACH (const QNetworkCookie& c, cookieList) { - if (!c.isSessionCookie()) { - cookies << c; - } - } - Q_FOREACH (const QNetworkCookie& c, cookies) { - uniqueCookies[c.name()] = c.value(); - } - cookies.clear(); - Q_FOREACH (const QByteArray& name, uniqueCookies.keys()) { - cookies << QNetworkCookie(name, uniqueCookies[name]); - } -} - void OwncloudShibbolethCredsPage::slotViewHidden() { - disposeBrowser(); wizard()->back(); wizard()->show(); } diff --git a/src/wizard/owncloudshibbolethcredspage.h b/src/wizard/owncloudshibbolethcredspage.h index c67bfd4428..7b2e8be567 100644 --- a/src/wizard/owncloudshibbolethcredspage.h +++ b/src/wizard/owncloudshibbolethcredspage.h @@ -18,6 +18,7 @@ #include #include #include +#include #include "wizard/abstractcredswizardpage.h" @@ -45,18 +46,14 @@ public Q_SLOTS: void setVisible(bool visible); private Q_SLOTS: - void slotShibbolethCookieReceived(const QNetworkCookie& cookie, Account*); - void slotOtherCookiesReceived(const QList& cookieList, const QUrl& url); + void slotShibbolethCookieReceived(); void slotViewHidden(); private: void setupBrowser(); - void disposeBrowser(); - ShibbolethWebView* _browser; - QNetworkCookie _cookie; + QPointer _browser; bool _afterInitialSetup; - QMap > _cookiesForUrl; }; } // ns Mirall