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.
This commit is contained in:
Daniel Molkentin 2014-05-14 11:11:45 +02:00
parent e62eb62a01
commit ba959f7cf9
19 changed files with 336 additions and 518 deletions

View file

@ -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
)

View file

@ -1,55 +0,0 @@
/*
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
*
* 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 <QDebug>
#include <QNetworkRequest>
#include <QNetworkCookieJar>
#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<QNetworkCookie> 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

View file

@ -1,43 +0,0 @@
/*
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
*
* 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 <QNetworkCookie>
#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

View file

@ -1,104 +0,0 @@
/*
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
*
* 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 <QDebug>
#include <QTextStream>
#include "creds/shibboleth/shibbolethconfigfile.h"
#include "creds/shibboleth/shibbolethcookiejar.h"
namespace Mirall
{
namespace
{
const char otherCookiesC[] = "otherCookies";
} // ns
void ShibbolethConfigFile::storeCookies(const QMap<QUrl, QList<QNetworkCookie> >& cookiesForUrl)
{
if (cookiesForUrl.isEmpty()) {
removeData(QString(), QString::fromLatin1(otherCookiesC));
} else {
QByteArray data;
QTextStream stream(&data);
Q_FOREACH (const QUrl& url, cookiesForUrl.keys()) {
const QList<QNetworkCookie>& 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<QNetworkCookie> 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

View file

@ -1,38 +0,0 @@
/*
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
*
* 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 <QList>
#include <QMap>
#include <QNetworkCookie>
#include <QUrl>
#include "mirall/mirallconfigfile.h"
namespace Mirall
{
class ShibbolethCookieJar;
class ShibbolethConfigFile : public MirallConfigFile
{
public:
void storeCookies(const QMap<QUrl, QList<QNetworkCookie> >& cookies);
ShibbolethCookieJar* createCookieJar() const;
};
} // ns Mirall
#endif

View file

@ -1,34 +0,0 @@
/*
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
*
* 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<QNetworkCookie>& cookieList, const QUrl& url)
{
if (QNetworkCookieJar::setCookiesFromUrl (cookieList, url)) {
Q_EMIT newCookiesForUrl (cookieList, url);
return true;
}
return false;
}
} // ns Mirall

View file

@ -1,41 +0,0 @@
/*
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
*
* 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 <QNetworkCookieJar>
#include <QList>
class QUrl;
class QNetworkCookie;
namespace Mirall
{
class ShibbolethCookieJar : public QNetworkCookieJar
{
Q_OBJECT
public:
ShibbolethCookieJar (QObject* parent = 0);
virtual bool setCookiesFromUrl (const QList<QNetworkCookie>& cookieList, const QUrl& url);
Q_SIGNALS:
void newCookiesForUrl (const QList<QNetworkCookie>& cookieList, const QUrl& url);
};
} // ns Mirall
#endif

View file

@ -20,9 +20,9 @@
#include <QAuthenticator>
#include <QNetworkReply>
#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<QSslError>)),
account, SLOT(slotHandleErrors(QNetworkReply*,QList<QSslError>)));
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<QNetworkCookie>, QUrl)),
this, SLOT (onNewCookiesForUrl (QList<QNetworkCookie>, 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<QNetworkCookie>, QUrl)),
this, SLOT(onNewCookiesForUrl (QList<QNetworkCookie>, 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<QNetworkCookie>& cookieList, const QUrl& url)
{
QList<QNetworkCookie> 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()

View file

@ -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<QNetworkCookie>& cookieList, const QUrl& url);
private Q_SLOTS:
void onNewCookiesForUrl(const QList<QNetworkCookie>& cookieList, const QUrl& url);

View file

@ -16,18 +16,19 @@
#include <QSettings>
#include <QNetworkReply>
#include <QMessageBox>
#include <qdebug.h>
#include <QDebug>
#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 <qt5keychain/keychain.h>
@ -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<ShibbolethCredentials*>(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<QUrl, QList<QNetworkCookie> >& 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<QNetworkCookie> cookies(AccountManager::instance()->account()->lastAuthCookies());
QMap<QString, QString> uniqueCookies;
Account *account = AccountManager::instance()->account();
QList<QNetworkCookie> 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<ReadPasswordJob*>(job);
delete readJob->settings();
qDebug() << Q_FUNC_INFO;
QList<QNetworkCookie> 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<QNetworkCookie> ShibbolethCredentials::accountCookies(Account *account)
{
return account->networkAccessManager()->cookieJar()->cookiesForUrl(account->url());
}
QNetworkCookie ShibbolethCredentials::findShibCookie(Account *account, QList<QNetworkCookie> 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<QNetworkCookie> 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<CookieJar*>(account->networkAccessManager()->cookieJar());
return jar->deleteCookie(cookie);
}
} // ns Mirall

View file

@ -18,6 +18,7 @@
#include <QMap>
#include <QNetworkCookie>
#include <QUrl>
#include <QPointer>
#include "creds/abstractcredentials.h"
@ -36,7 +37,6 @@ Q_OBJECT
public:
ShibbolethCredentials();
ShibbolethCredentials(const QNetworkCookie& cookie, const QMap<QUrl, QList<QNetworkCookie> >& 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<QNetworkCookie> accountCookies(Account*);
static QNetworkCookie findShibCookie(Account*, QList<QNetworkCookie> cookies = QList<QNetworkCookie>());
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<QUrl, QList<QNetworkCookie> > _otherCookies;
QPointer<ShibbolethWebView> _browser;
QNetworkCookie _shibCookie;
QString _user;
};

View file

@ -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<QSslError>)),
SLOT(slotHandleErrors(QNetworkReply*,QList<QSslError>)));
}
@ -186,7 +196,12 @@ QList<QNetworkCookie> 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)

View file

@ -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);

View file

@ -159,6 +159,7 @@ Application::Application(int &argc, char **argv) :
Application::~Application()
{
delete AccountManager::instance()->account();
// qDebug() << "* Mirall shutdown";
}

148
src/mirall/cookiejar.cpp Normal file
View file

@ -0,0 +1,148 @@
/*
* Copyright (C) by Daniel Molkentin <danimo@owncloud.com>
*
* 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 <QDebug>
#include <QFile>
#include <QDateTime>
namespace Mirall {
namespace {
const unsigned int JAR_VERSION = 23;
}
QDataStream &operator<<(QDataStream &stream, const QList<QNetworkCookie> &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<QNetworkCookie> &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<QNetworkCookie> 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<QNetworkCookie>& cookieList, const QUrl& url)
{
if (QNetworkCookieJar::setCookiesFromUrl(cookieList, url)) {
Q_EMIT newCookiesForUrl(cookieList, url);
return true;
}
return false;
}
QList<QNetworkCookie> CookieJar::cookiesForUrl(const QUrl &url)
{
QList<QNetworkCookie> cookies = QNetworkCookieJar::cookiesForUrl(url);
qDebug() << url << "requests:" << cookies;
return cookies;
}
bool CookieJar::deleteCookie(const QNetworkCookie &delCookie)
{
QList<QNetworkCookie> 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<QNetworkCookie> list;
stream >> list;
setAllCookies(removeExpired(list));
file.close();
}
QList<QNetworkCookie> CookieJar::removeExpired(const QList<QNetworkCookie> &cookies)
{
QList<QNetworkCookie> 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

44
src/mirall/cookiejar.h Normal file
View file

@ -0,0 +1,44 @@
/*
* Copyright (C) by Daniel Molkentin <danimo@owncloud.com>
*
* 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 <QNetworkCookieJar>
namespace Mirall {
class CookieJar : public QNetworkCookieJar
{
Q_OBJECT
public:
explicit CookieJar(QObject *parent = 0);
~CookieJar();
bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url);
QList<QNetworkCookie> cookiesForUrl(const QUrl &url);
virtual bool deleteCookie(const QNetworkCookie & cookie);
signals:
void newCookiesForUrl(const QList<QNetworkCookie>& cookieList, const QUrl& url);
private:
void save();
void restore();
QList<QNetworkCookie> removeExpired(const QList<QNetworkCookie> &cookies);
QString storagePath() const;
};
} // namespace Mirall
#endif // MIRALL_COOKIEJAR_H

View file

@ -15,6 +15,7 @@
#include <QNetworkProxy>
#include <QAuthenticator>
#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*)));
}

View file

@ -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<OwncloudWizard*>(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<QNetworkCookie>, QUrl)),
this, SLOT(slotOtherCookiesReceived(QList<QNetworkCookie>, 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<QNetworkCookie>, QUrl)),
this, SLOT(slotOtherCookiesReceived(QList<QNetworkCookie>, 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<QNetworkCookie>& cookieList, const QUrl& url)
{
QList<QNetworkCookie>& cookies(_cookiesForUrl[url]);
QMap<QByteArray, QByteArray> 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();
}

View file

@ -18,6 +18,7 @@
#include <QMap>
#include <QNetworkCookie>
#include <QUrl>
#include <QPointer>
#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<QNetworkCookie>& cookieList, const QUrl& url);
void slotShibbolethCookieReceived();
void slotViewHidden();
private:
void setupBrowser();
void disposeBrowser();
ShibbolethWebView* _browser;
QNetworkCookie _cookie;
QPointer<ShibbolethWebView> _browser;
bool _afterInitialSetup;
QMap<QUrl, QList<QNetworkCookie> > _cookiesForUrl;
};
} // ns Mirall