2013-07-29 16:28:45 +04:00
|
|
|
/*
|
|
|
|
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
2013-07-30 12:48:26 +04:00
|
|
|
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
2013-07-29 16:28:45 +04:00
|
|
|
*
|
|
|
|
* 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
|
2013-07-30 12:48:26 +04:00
|
|
|
* the Free Software Foundation; version 2 of the License.
|
2013-07-29 16:28:45 +04:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2013-10-23 16:48:44 +04:00
|
|
|
#include <QDebug>
|
|
|
|
#include <QMutex>
|
|
|
|
|
2013-07-30 13:19:22 +04:00
|
|
|
#include "creds/shibbolethcredentials.h"
|
|
|
|
#include "creds/shibboleth/shibbolethaccessmanager.h"
|
|
|
|
#include "creds/shibboleth/shibbolethwebview.h"
|
2013-08-05 19:31:52 +04:00
|
|
|
#include "creds/shibboleth/shibbolethrefresher.h"
|
2013-08-06 18:17:55 +04:00
|
|
|
#include "creds/shibboleth/shibbolethconfigfile.h"
|
2013-08-06 13:50:08 +04:00
|
|
|
#include "creds/credentialscommon.h"
|
2013-10-23 16:48:44 +04:00
|
|
|
#include "mirall/account.h"
|
2013-07-29 16:28:45 +04:00
|
|
|
|
|
|
|
namespace Mirall
|
|
|
|
{
|
|
|
|
|
2013-08-05 19:31:52 +04:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
|
|
|
|
int shibboleth_redirect_callback(CSYNC* csync_ctx,
|
|
|
|
const char* uri)
|
|
|
|
{
|
|
|
|
if (!csync_ctx || !uri) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QString qurl(QString::fromLatin1(uri));
|
|
|
|
QRegExp shibbolethyWords ("SAML|wayf");
|
|
|
|
|
|
|
|
shibbolethyWords.setCaseSensitivity (Qt::CaseInsensitive);
|
|
|
|
if (!qurl.contains(shibbolethyWords)) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
QMutex mutex;
|
|
|
|
QMutexLocker locker(&mutex);
|
2013-10-30 19:31:47 +04:00
|
|
|
ShibbolethCredentials* creds = qobject_cast<ShibbolethCredentials*>(AccountManager::instance()->account()->credentials());
|
|
|
|
|
2013-08-05 19:31:52 +04:00
|
|
|
|
|
|
|
if (!creds) {
|
|
|
|
qDebug() << "Not a Shibboleth creds instance!";
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ShibbolethRefresher refresher(creds, csync_ctx);
|
|
|
|
|
|
|
|
// blocks
|
|
|
|
refresher.refresh();
|
|
|
|
|
2013-08-07 16:00:47 +04:00
|
|
|
return creds->ready() ? 0 : 1;
|
2013-08-05 19:31:52 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
} // ns
|
|
|
|
|
2013-07-29 16:28:45 +04:00
|
|
|
ShibbolethCredentials::ShibbolethCredentials()
|
2013-11-04 19:36:23 +04:00
|
|
|
: AbstractCredentials(),
|
|
|
|
_url(),
|
|
|
|
_shibCookie(),
|
2013-07-29 16:28:45 +04:00
|
|
|
_ready(false),
|
2013-08-06 18:17:55 +04:00
|
|
|
_browser(0),
|
|
|
|
_otherCookies()
|
2013-07-29 16:28:45 +04:00
|
|
|
{}
|
|
|
|
|
2013-08-06 18:17:55 +04:00
|
|
|
ShibbolethCredentials::ShibbolethCredentials(const QNetworkCookie& cookie, const QMap<QUrl, QList<QNetworkCookie> >& otherCookies)
|
2013-07-29 16:28:45 +04:00
|
|
|
: _shibCookie(cookie),
|
|
|
|
_ready(true),
|
2013-08-06 18:17:55 +04:00
|
|
|
_browser(0),
|
|
|
|
_otherCookies(otherCookies)
|
2013-07-29 16:28:45 +04:00
|
|
|
{}
|
|
|
|
|
2013-08-06 13:50:08 +04:00
|
|
|
void ShibbolethCredentials::syncContextPreInit(CSYNC* ctx)
|
|
|
|
{
|
|
|
|
csync_set_auth_callback (ctx, handleNeonSSLProblems);
|
|
|
|
}
|
2013-07-29 16:44:54 +04:00
|
|
|
|
2013-08-05 19:31:52 +04:00
|
|
|
QByteArray ShibbolethCredentials::prepareCookieData() const
|
|
|
|
{
|
|
|
|
QString cookiesAsString;
|
|
|
|
// TODO: This should not be a part of this method, but we don't
|
|
|
|
// 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.
|
2013-10-23 16:48:44 +04:00
|
|
|
QList<QNetworkCookie> cookies(AccountManager::instance()->account()->lastAuthCookies());
|
2013-08-05 20:39:26 +04:00
|
|
|
QMap<QString, QString> uniqueCookies;
|
2013-08-05 19:31:52 +04:00
|
|
|
|
|
|
|
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) {
|
2013-08-05 20:39:26 +04:00
|
|
|
const QString cookieName(c.name());
|
|
|
|
|
|
|
|
if (cookieName.startsWith("_shibsession_")) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
uniqueCookies.insert(cookieName, c.value());
|
|
|
|
}
|
|
|
|
|
2013-08-07 16:00:47 +04:00
|
|
|
if (!_shibCookie.name().isEmpty()) {
|
|
|
|
uniqueCookies.insert(_shibCookie.name(), _shibCookie.value());
|
|
|
|
}
|
2013-08-05 20:39:26 +04:00
|
|
|
foreach(const QString& cookieName, uniqueCookies.keys()) {
|
|
|
|
cookiesAsString += cookieName;
|
2013-08-05 19:31:52 +04:00
|
|
|
cookiesAsString += '=';
|
2013-08-05 20:39:26 +04:00
|
|
|
cookiesAsString += uniqueCookies[cookieName];
|
2013-08-05 19:31:52 +04:00
|
|
|
cookiesAsString += "; ";
|
|
|
|
}
|
|
|
|
|
|
|
|
return cookiesAsString.toLatin1();
|
|
|
|
}
|
|
|
|
|
2013-07-29 16:44:54 +04:00
|
|
|
void ShibbolethCredentials::syncContextPreStart (CSYNC* ctx)
|
2013-07-29 16:28:45 +04:00
|
|
|
{
|
2013-08-05 19:31:52 +04:00
|
|
|
typedef int (*csync_owncloud_redirect_callback_t)(CSYNC* ctx, const char* uri);
|
|
|
|
|
|
|
|
csync_owncloud_redirect_callback_t cb = shibboleth_redirect_callback;
|
|
|
|
|
|
|
|
csync_set_module_property(ctx, "session_key", prepareCookieData().data());
|
|
|
|
csync_set_module_property(ctx, "redirect_callback", &cb);
|
2013-07-29 16:28:45 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ShibbolethCredentials::changed(AbstractCredentials* credentials) const
|
|
|
|
{
|
|
|
|
ShibbolethCredentials* other(dynamic_cast< ShibbolethCredentials* >(credentials));
|
|
|
|
|
|
|
|
if (!other || other->cookie() != this->cookie()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString ShibbolethCredentials::authType() const
|
|
|
|
{
|
|
|
|
return QString::fromLatin1("shibboleth");
|
|
|
|
}
|
|
|
|
|
2013-11-13 16:59:35 +04:00
|
|
|
QString ShibbolethCredentials::user() const
|
|
|
|
{
|
|
|
|
// ### TODO: If we had a way to extract the currently authenticated user
|
|
|
|
// somehow, we could return its id token (email) here (stored in REMOTE_USER)
|
|
|
|
// The server doesn't return it by default
|
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
2013-07-29 16:28:45 +04:00
|
|
|
QNetworkCookie ShibbolethCredentials::cookie() const
|
|
|
|
{
|
|
|
|
return _shibCookie;
|
|
|
|
}
|
|
|
|
|
|
|
|
QNetworkAccessManager* ShibbolethCredentials::getQNAM() const
|
|
|
|
{
|
2013-07-30 14:56:27 +04:00
|
|
|
ShibbolethAccessManager* qnam(new ShibbolethAccessManager(_shibCookie));
|
|
|
|
|
|
|
|
connect(this, SIGNAL(newCookie(QNetworkCookie)),
|
|
|
|
qnam, SLOT(setCookie(QNetworkCookie)));
|
|
|
|
return qnam;
|
2013-07-29 16:28:45 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ShibbolethCredentials::ready() const
|
|
|
|
{
|
|
|
|
return _ready;
|
|
|
|
}
|
|
|
|
|
2013-11-04 19:36:23 +04:00
|
|
|
void ShibbolethCredentials::fetch(Account *account)
|
2013-07-29 16:28:45 +04:00
|
|
|
{
|
|
|
|
if (_ready) {
|
|
|
|
Q_EMIT fetched();
|
|
|
|
} else {
|
2013-08-06 18:17:55 +04:00
|
|
|
ShibbolethConfigFile cfg;
|
2013-11-04 19:36:23 +04:00
|
|
|
if (account) {
|
|
|
|
_url = account->url();
|
|
|
|
}
|
2013-11-07 21:47:38 +04:00
|
|
|
_browser = new ShibbolethWebView(account, cfg.createCookieJar());
|
2013-07-29 16:28:45 +04:00
|
|
|
connect(_browser, SIGNAL(shibbolethCookieReceived(QNetworkCookie)),
|
|
|
|
this, SLOT(onShibbolethCookieReceived(QNetworkCookie)));
|
2013-08-07 16:00:47 +04:00
|
|
|
connect(_browser, SIGNAL(viewHidden()),
|
|
|
|
this, SLOT(slotBrowserHidden()));
|
2013-07-29 16:28:45 +04:00
|
|
|
_browser->show ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-04 19:36:23 +04:00
|
|
|
void ShibbolethCredentials::persist(Account* /*account*/)
|
2013-07-29 16:28:45 +04:00
|
|
|
{
|
2013-08-06 18:17:55 +04:00
|
|
|
ShibbolethConfigFile cfg;
|
|
|
|
|
|
|
|
cfg.storeCookies(_otherCookies);
|
2013-07-29 16:28:45 +04:00
|
|
|
}
|
|
|
|
|
2013-08-07 16:00:47 +04:00
|
|
|
void ShibbolethCredentials::disposeBrowser()
|
2013-07-29 16:28:45 +04:00
|
|
|
{
|
2013-08-07 16:00:47 +04:00
|
|
|
disconnect(_browser, SIGNAL(viewHidden()),
|
|
|
|
this, SLOT(slotBrowserHidden()));
|
2013-07-29 16:28:45 +04:00
|
|
|
disconnect(_browser, SIGNAL(shibbolethCookieReceived(QNetworkCookie)),
|
|
|
|
this, SLOT(onShibbolethCookieReceived(QNetworkCookie)));
|
2013-08-07 16:00:47 +04:00
|
|
|
_browser->hide();
|
2013-07-29 16:28:45 +04:00
|
|
|
_browser->deleteLater();
|
|
|
|
_browser = 0;
|
2013-08-07 16:00:47 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void ShibbolethCredentials::onShibbolethCookieReceived(const QNetworkCookie& cookie)
|
|
|
|
{
|
|
|
|
disposeBrowser();
|
2013-07-29 16:28:45 +04:00
|
|
|
_ready = true;
|
|
|
|
_shibCookie = cookie;
|
2013-07-30 14:56:27 +04:00
|
|
|
Q_EMIT newCookie(_shibCookie);
|
2013-07-29 16:28:45 +04:00
|
|
|
Q_EMIT fetched();
|
|
|
|
}
|
|
|
|
|
2013-08-07 16:00:47 +04:00
|
|
|
void ShibbolethCredentials::slotBrowserHidden()
|
|
|
|
{
|
|
|
|
disposeBrowser();
|
|
|
|
_ready = false;
|
|
|
|
_shibCookie = QNetworkCookie();
|
|
|
|
Q_EMIT fetched();
|
|
|
|
}
|
|
|
|
|
2013-08-05 19:31:52 +04:00
|
|
|
void ShibbolethCredentials::invalidateAndFetch()
|
|
|
|
{
|
|
|
|
_ready = false;
|
|
|
|
connect (this, SIGNAL(fetched()),
|
|
|
|
this, SLOT(onFetched()));
|
2013-11-04 19:36:23 +04:00
|
|
|
// small hack to support the ShibbolethRefresher hack
|
|
|
|
// we already rand fetch() with a valid account object,
|
|
|
|
// and hence know the url on refresh
|
|
|
|
fetch(0);
|
2013-08-05 19:31:52 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void ShibbolethCredentials::onFetched()
|
|
|
|
{
|
|
|
|
disconnect (this, SIGNAL(fetched()),
|
|
|
|
this, SLOT(onFetched()));
|
|
|
|
|
|
|
|
Q_EMIT invalidatedAndFetched(prepareCookieData());
|
|
|
|
}
|
|
|
|
|
2013-07-29 16:28:45 +04:00
|
|
|
} // ns Mirall
|