Deprecate KQOauth

.. its dead (not updated for 5 years)

.. it introduces dependency issues with openssl/crypto/icu
   on Linux distros

.. we don't need it, since OAuthDialog does the heavy
   lifting we need (ok, its not pretty but it works).

.. old code moved into the deprecated folder

Fixes #2881
This commit is contained in:
Mark Liversedge
2018-06-02 11:01:43 +01:00
parent 9e21ececff
commit cdd99da9c9
29 changed files with 20 additions and 248 deletions

707
deprecated/OAuthManager.cpp Normal file
View File

@@ -0,0 +1,707 @@
/*
* Copyright (c) 2010 Justin Knotzke (jknotzke@shampoo.ca)
* Copyright (c) 2017 Mark Liversedge (liversedge@gmail.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; either version 2 of the License, or (at your option)
* any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "Secrets.h"
#include "OAuthManager.h"
#include "OAuthDialog.h"
#include "Athlete.h"
#include "Context.h"
#include "Settings.h"
#include "Colors.h"
#include "TimeUtils.h"
#if QT_VERSION > 0x050000
#include "GoogleDrive.h"
#include "PolarFlow.h"
#include <QJsonParseError>
#endif
OAuthManager::OAuthManager(Context *context, OAuthSite site, CloudService *service, QString baseURL, QString clientsecret) :
context(context), site(site), service(service), baseURL(baseURL), clientsecret(clientsecret)
{
if (service) { // ultimately this will be the only way this works
if (service->id() == "Strava") site = this->site = STRAVA;
if (service->id() == "Dropbox") site = this->site = DROPBOX;
if (service->id() == "Cycling Analytics") site = this->site = CYCLING_ANALYTICS;
if (service->id() == "Google Drive") site = this->site = GOOGLE_DRIVE;
if (service->id() == "University of Kent") site = this->site = KENTUNI;
if (service->id() == "Today's Plan") site = this->site = TODAYSPLAN;
if (service->id() == "Withings") site = this->site = WITHINGS;
if (service->id() == "PolarFlow") site = this->site = POLAR;
if (service->id() == "SportTracks.mobi") site = this->site = SPORTTRACKS;
if (service->id() == "Xert") site = this->site = XERT;
}
// check if SSL is available - if not - message and end
if (!QSslSocket::supportsSsl()) {
QString text = QString(tr("SSL Security Libraries required for 'Authorise' are missing in this installation."));
QMessageBox sslMissing(QMessageBox::Critical, tr("Authorization Error"), text);
sslMissing.exec();
noSSLlib = true;
return;
}
// ignore responses to false, used by POLARFLOW when binding the user
ignore = false;
// SSL is available - so authorisation can take place
noSSLlib = false;
}
void
OAuthManager::authorize()
{
//
// All services have some kind of initial authorisation URL where the user needs
// to login and confirm they are willing to authorise the particular app and
// provide a temporary token to get the real token with
//
QString urlstr = "";
if (site == STRAVA) {
urlstr = QString("https://www.strava.com/oauth/authorize?");
urlstr.append("client_id=").append(GC_STRAVA_CLIENT_ID).append("&");
urlstr.append("scope=view_private,write&");
urlstr.append("redirect_uri=http://www.goldencheetah.org/&");
urlstr.append("response_type=code&");
urlstr.append("approval_prompt=force");
} else if (site == DROPBOX) {
urlstr = QString("https://www.dropbox.com/oauth2/authorize?");
#ifdef GC_DROPBOX_CLIENT_ID
urlstr.append("client_id=").append(GC_DROPBOX_CLIENT_ID).append("&");
#endif
urlstr.append("redirect_uri=https://goldencheetah.github.io/blank.html&");
urlstr.append("response_type=code&");
urlstr.append("force_reapprove=true");
} else if (site == CYCLING_ANALYTICS) {
urlstr = QString("https://www.cyclinganalytics.com/api/auth?");
urlstr.append("client_id=").append(GC_CYCLINGANALYTICS_CLIENT_ID).append("&");
urlstr.append("scope=modify_rides&");
urlstr.append("redirect_uri=http://www.goldencheetah.org/&");
urlstr.append("response_type=code&");
urlstr.append("approval_prompt=force");
#if QT_VERSION >= 0x050000
} else if (site == GOOGLE_DRIVE) {
const QString scope = service->getSetting(GC_GOOGLE_DRIVE_AUTH_SCOPE, "drive.appdata").toString();
// OAUTH 2.0 - Google flow for installed applications
urlstr = QString("https://accounts.google.com/o/oauth2/auth?");
// We only request access to the application data folder, not all files.
urlstr.append("scope=https://www.googleapis.com/auth/" + scope + "&");
urlstr.append("redirect_uri=urn:ietf:wg:oauth:2.0:oob&");
urlstr.append("response_type=code&");
urlstr.append("client_id=").append(GC_GOOGLE_DRIVE_CLIENT_ID);
} else if (site == KENTUNI) {
const QString scope = service->getSetting(GC_UOK_GOOGLE_DRIVE_AUTH_SCOPE, "drive.appdata").toString();
// OAUTH 2.0 - Google flow for installed applications
urlstr = QString("https://accounts.google.com/o/oauth2/auth?");
// We only request access to the application data folder, not all files.
urlstr.append("scope=https://www.googleapis.com/auth/" + scope + "&");
urlstr.append("redirect_uri=urn:ietf:wg:oauth:2.0:oob&");
urlstr.append("response_type=code&");
urlstr.append("client_id=").append(GC_GOOGLE_DRIVE_CLIENT_ID);
#endif
} else if (site == TODAYSPLAN) {
//urlstr = QString("https://whats.todaysplan.com.au/en/authorize/"); //XXX fixup below when pages.cpp goes
if (baseURL=="") baseURL=service->getSetting(GC_TODAYSPLAN_URL, "https://whats.todaysplan.com.au").toString();
urlstr = QString("%1/authorize/").arg(baseURL);
urlstr.append(GC_TODAYSPLAN_CLIENT_ID);
} else if (site == POLAR) {
// OAUTH 2.0 - Google flow for installed applications
urlstr = QString("%1?").arg(GC_POLARFLOW_OAUTH_URL);
// We only request access to the application data folder, not all files.
urlstr.append("response_type=code&");
urlstr.append("client_id=").append(GC_POLARFLOW_CLIENT_ID);
} else if (site == SPORTTRACKS) {
// We only request access to the application data folder, not all files.
urlstr = QString("https://api.sporttracks.mobi/oauth2/authorize?");
urlstr.append("redirect_uri=http://www.goldencheetah.org&");
urlstr.append("state=xyzzy&");
urlstr.append("response_type=code&");
urlstr.append("client_id=").append(GC_SPORTTRACKS_CLIENT_ID);
} else if (site == WITHINGS) {
// Withings is the only service that uses KQOauth for now.
#ifdef GC_HAVE_KQOAUTH
oauthRequest = new KQOAuthRequest;
oauthManager = new KQOAuthManager(this);
connect(oauthManager, SIGNAL(temporaryTokenReceived(QString,QString)), this, SLOT(onTemporaryTokenReceived(QString, QString)));
connect(oauthManager, SIGNAL(authorizationReceived(QString,QString)), this, SLOT( onAuthorizationReceived(QString, QString)));
connect(oauthManager, SIGNAL(accessTokenReceived(QString,QString)), this, SLOT(onAccessTokenReceived(QString,QString)));
connect(oauthManager, SIGNAL(requestReady(QByteArray)), this, SLOT(onRequestReady(QByteArray)));
connect(oauthManager, SIGNAL(authorizationPageRequested(QUrl)), this, SLOT(onAuthorizationPageRequested(QUrl)));
oauthRequest->initRequest(KQOAuthRequest::TemporaryCredentials, QUrl("https://oauth.withings.com/account/request_token"));
//oauthRequest->setEnableDebugOutput(true);
oauthRequest->setHttpMethod(KQOAuthRequest::GET);
oauthRequest->setConsumerKey(GC_WITHINGS_CONSUMER_KEY);
oauthRequest->setConsumerSecretKey(GC_WITHINGS_CONSUMER_SECRET);
//oauthRequest->setCallbackUrl(QUrl("http://www.goldencheetah.org"));
oauthManager->setHandleUserAuthorization(true); // false to use callback
oauthManager->setHandleAuthorizationPageOpening(false);
oauthManager->executeRequest(oauthRequest);
#endif
} else if (site == XERT) {
getTokenWithCode("");
}
//
// STEP 1: LOGIN AND AUTHORISE THE APPLICATION
//
/*if (site == DROPBOX || site == STRAVA || site == CYCLING_ANALYTICS || site == POLAR || site == SPORTTRACKS || site == GOOGLE_DRIVE || site == KENTUNI || site == TODAYSPLAN) {
url = QUrl(urlstr);
view->setUrl(url);
// connects
connect(view, SIGNAL(urlChanged(const QUrl&)), this, SLOT(urlChanged(const QUrl&)));
connect(view, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool)));
}*/
}
// just ignore SSL handshake errors at all times
void
OAuthManager::onSslErrors(QNetworkReply *reply, const QList<QSslError>&)
{
reply->ignoreSslErrors();
}
#ifdef GC_HAVE_KQOAUTH
//
// KQOauth call backs
//
void
OAuthManager::onTemporaryTokenReceived(QString, QString)
{
//qDebug() << "onTemporaryTokenReceived";
QUrl userAuthURL;
if (site == WITHINGS) {
userAuthURL = "https://oauth.withings.com/account/authorize";
}
if(oauthManager->lastError() == KQOAuthManager::NoError) {
oauthManager->getUserAuthorization(userAuthURL);
} else
qDebug() << "error" << oauthManager->lastError();
}
void
OAuthManager::onAuthorizationReceived(QString, QString)
{
//qDebug() << "Authorization token received: " << token << verifier;
if (site == WITHINGS) {
oauthManager->getUserAccessTokens(QUrl("https://oauth.withings.com/account/access_token"));
}
if(oauthManager->lastError() != KQOAuthManager::NoError) {
QString error = QString(tr("Error fetching OAuth credentials - Endpoint: /oauth/access_token"));
QMessageBox oautherr(QMessageBox::Critical, tr("Authorization Error"), error);
oautherr.exec();
//accept();
}
}
void
OAuthManager::onAccessTokenReceived(QString token, QString tokenSecret)
{
//qDebug() << "Access token received: " << token << tokenSecret;
QString info;
if (site == WITHINGS) {
service->setSetting(GC_WITHINGS_TOKEN, token);
service->setSetting(GC_WITHINGS_SECRET, tokenSecret);
appsettings->setCValue(context->athlete->cyclist, GC_NOKIA_REFRESH_TOKEN, "");
info = QString(tr("Nokia Health (Withings) authorization was successful."));
}
QMessageBox information(QMessageBox::Information, tr("Information"), info);
information.exec();
//accept();
}
void
OAuthManager::onAuthorizedRequestDone() {} // request sent - do nothing
void
OAuthManager::onRequestReady(QByteArray response)
{
//qDebug() << "Response received: " << response;
QString r = response;
if (r.contains("\"errors\"", Qt::CaseInsensitive)) {
QMessageBox oautherr(QMessageBox::Critical, tr("Error in authorization"),
tr("There was an error during authorization. Please check the error description."));
oautherr.setDetailedText(r); // probably blank
oautherr.exec();
} else {
if (site == WITHINGS) {
QString userid;
#if QT_VERSION > 0x050000
QUrlQuery params;
params.setQuery(response);
#else
QUrl params;
params.setEncodedQuery(response);
#endif
userid = params.queryItemValue("userid");
if (userid.isEmpty() == false) {
service->setSetting(GC_WIUSER, userid);
}
}
}
}
void OAuthManager::onAuthorizationPageRequested(QUrl url) {
// open Authorization page in view
view->setUrl(url);
}
#endif // KQOAuth callbacks used by Withings only
//
// STEP 2: AUTHORISATION REDIRECT WITH TEMPORARY CODE
//
// When the URL changes, it will be the redirect with the temporary token after
// the initial authorisation. The URL will have some parameters to indicate this
// if they exist we should intercept the redirect to get the permanent token.
// If they don't get passed then we don't need to do anything.
//
void
OAuthManager::getTokenWithCode(QString code)
{
QString authheader;
// sites that use this scheme
if (site == DROPBOX || site == STRAVA || site == CYCLING_ANALYTICS || site == TODAYSPLAN || site == POLAR || site == SPORTTRACKS || site == XERT) {
// sporttracks insists on passing state
if (code.endsWith("&state=xyzzy")) code = code.mid(0,code.length()-12);
QByteArray data;
#if QT_VERSION > 0x050000
QUrlQuery params;
#else
QUrl params;
#endif
QString urlstr = "";
// now get the final token to store
if (site == DROPBOX) {
urlstr = QString("https://api.dropboxapi.com/oauth2/token?");
urlstr.append("redirect_uri=https://goldencheetah.github.io/blank.html&");
params.addQueryItem("grant_type", "authorization_code");
#ifdef GC_DROPBOX_CLIENT_ID
params.addQueryItem("client_id", GC_DROPBOX_CLIENT_ID);
#endif
#ifdef GC_DROPBOX_CLIENT_SECRET
params.addQueryItem("client_secret", GC_DROPBOX_CLIENT_SECRET);
#endif
} else if (site == POLAR) {
urlstr = QString("%1?").arg(GC_POLARFLOW_TOKEN_URL);
urlstr.append("redirect_uri=http://www.goldencheetah.org");
params.addQueryItem("grant_type", "authorization_code");
#if (defined GC_POLARFLOW_CLIENT_ID) && (defined GC_POLARFLOW_CLIENT_SECRET)
authheader = QString("%1:%2").arg(GC_POLARFLOW_CLIENT_ID).arg(GC_POLARFLOW_CLIENT_SECRET);
#endif
} else if (site == SPORTTRACKS) {
urlstr = QString("https://api.sporttracks.mobi/oauth2/token?");
params.addQueryItem("client_id", GC_SPORTTRACKS_CLIENT_ID);
params.addQueryItem("client_secret", GC_SPORTTRACKS_CLIENT_SECRET);
params.addQueryItem("redirect_uri","http://www.goldencheetah.org");
params.addQueryItem("grant_type", "authorization_code");
} else if (site == STRAVA) {
urlstr = QString("https://www.strava.com/oauth/token?");
params.addQueryItem("client_id", GC_STRAVA_CLIENT_ID);
#ifdef GC_STRAVA_CLIENT_SECRET
params.addQueryItem("client_secret", GC_STRAVA_CLIENT_SECRET);
#endif
} else if (site == CYCLING_ANALYTICS) {
urlstr = QString("https://www.cyclinganalytics.com/api/token?");
params.addQueryItem("client_id", GC_CYCLINGANALYTICS_CLIENT_ID);
#ifdef GC_CYCLINGANALYTICS_CLIENT_SECRET
params.addQueryItem("client_secret", GC_CYCLINGANALYTICS_CLIENT_SECRET);
#endif
params.addQueryItem("grant_type", "authorization_code");
} else if (site == TODAYSPLAN) {
if (baseURL=="") baseURL=service->getSetting(GC_TODAYSPLAN_URL, "https://whats.todaysplan.com.au").toString();
urlstr = QString("%1/rest/oauth/access_token?").arg(baseURL);
params.addQueryItem("client_id", GC_TODAYSPLAN_CLIENT_ID);
#ifdef GC_TODAYSPLAN_CLIENT_SECRET
if (clientsecret != "") //XXX get rid when pages.cpp goes
params.addQueryItem("client_secret", clientsecret);
else if (service->getSetting(GC_TODAYSPLAN_USERKEY, "").toString() != "")
params.addQueryItem("client_secret", service->getSetting(GC_TODAYSPLAN_USERKEY, "").toString());
else
params.addQueryItem("client_secret", GC_TODAYSPLAN_CLIENT_SECRET);
#endif
params.addQueryItem("grant_type", "authorization_code");
params.addQueryItem("redirect_uri", "https://goldencheetah.github.io/blank.html");
} else if (site == XERT) {
urlstr = QString("https://www.xertonline.com/oauth/token");
params.addQueryItem("username", service->getSetting(GC_XERTUSER, "").toString());
params.addQueryItem("password", service->getSetting(GC_XERTPASS, "").toString());
params.addQueryItem("grant_type", "password");
authheader = QString("%1:%1").arg("xert_public");
}
// all services will need us to send the temporary code received
params.addQueryItem("code", code);
#if QT_VERSION > 0x050000
data.append(params.query(QUrl::FullyEncoded));
#else
data=params.encodedQuery();
#endif
// trade-in the temporary access code retrieved by the Call-Back URL for the finale token
QUrl url = QUrl(urlstr);
QNetworkRequest request = QNetworkRequest(url);
request.setHeader(QNetworkRequest::ContentTypeHeader,"application/x-www-form-urlencoded");
// client id and secret are encoded and sent in the header for POLAR and XERT
if (site == POLAR || site == XERT) request.setRawHeader("Authorization", "Basic " + authheader.toLatin1().toBase64());
// now get the final token - but ignore errors
manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError> & )), this, SLOT(onSslErrors(QNetworkReply*, const QList<QSslError> & )));
//connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkRequestFinished(QNetworkReply*)));
QNetworkReply *reply = manager->post(request, data);
QEventLoop loop;
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
networkRequestFinished(reply);
}
}
//
// GOOGLE DRIVE gets the code in the HTML title field (different to other services)
//
void
OAuthManager::loadFinished(bool ok)
{
if (site == GOOGLE_DRIVE || site == KENTUNI) {
if (ok && url.toString().startsWith("https://accounts.google.com/o/oauth2/auth")) {
// retrieve the code from the HTML page title
QString title = view->title();
if (title.contains("code")) {
QString code = title.right(title.length()-title.indexOf("code=")-5);
QByteArray data;
#if QT_VERSION > 0x050000
QUrlQuery params;
#else
QUrl params;
#endif
QString urlstr = "https://www.googleapis.com/oauth2/v3/token?";
params.addQueryItem("client_id", GC_GOOGLE_DRIVE_CLIENT_ID);
if (site == GOOGLE_DRIVE || site == KENTUNI) {
params.addQueryItem("client_secret", GC_GOOGLE_DRIVE_CLIENT_SECRET);
}
params.addQueryItem("code", code);
params.addQueryItem("redirect_uri", "urn:ietf:wg:oauth:2.0:oob");
params.addQueryItem("grant_type", "authorization_code");
#if QT_VERSION > 0x050000
data.append(params.query(QUrl::FullyEncoded));
#else
data=params.encodedQuery();
#endif
// trade-in the temporary access code retrieved by the
// Call-Back URL for the finale token
QUrl url = QUrl(urlstr);
QNetworkRequest request = QNetworkRequest(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
// not get the final token - ignoring errors
manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError> & )), this, SLOT(onSslErrors(QNetworkReply*, const QList<QSslError> & )));
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkRequestFinished(QNetworkReply*)));
manager->post(request, data);
}
}
}
}
#if QT_VERSION < 0x050000
static QString RawJsonStringGrab(const QByteArray& payload,
const QString& needle) {
// A RegExp based JSON string parser. Not the best, but it does the job.
QString regex =
// This matches the key.
"(" + needle + "|\"" + needle + "\"|'" + needle + "')"
// Matches the separator.
"[\\s]*:[\\s]*"
// matches the value.
"(\"\\S+\"|'\\S+')";
QRegExp q(regex);
if (!q.isValid()) {
// Somehow failed to build the regex.
return "";
}
int start = q.indexIn(payload);
int pos = q.pos(2);
if (start == -1 || pos == -1) {
// Failed to find the key or the value.
return "";
}
QString ret = payload.mid(pos, q.matchedLength() + start - pos);
// Remove " or ' from the value.
ret.remove(0, 1);
ret.remove(ret.size() - 1, 1);
return ret;
}
#endif
//
// STEP 3: REFRESH AND ACCESS TOKEN RECEIVED
//
// this is when we get the refresh or access tokens after a redirect has been loaded
// so pretty much at the end of the process. Each service can have slightly special
// needs and certainly needs to set the right setting anyway.
//
void
OAuthManager::networkRequestFinished(QNetworkReply *reply)
{
// we've been told to ignore responses (used by POLAR, maybe others in future)
if (ignore) return;
// we can handle SSL handshake errors, if we got here then some kind of protocol was agreed
if (reply->error() == QNetworkReply::NoError || reply->error() == QNetworkReply::SslHandshakeFailedError) {
QByteArray payload = reply->readAll(); // JSON
QString refresh_token;
QString access_token;
double polar_userid=0;
// parse the response and extract the tokens, pretty much the same for all services
// although polar choose to also pass a user id, which is needed for future calls
#if QT_VERSION > 0x050000
QJsonParseError parseError;
QJsonDocument document = QJsonDocument::fromJson(payload, &parseError);
if (parseError.error == QJsonParseError::NoError) {
refresh_token = document.object()["refresh_token"].toString();
access_token = document.object()["access_token"].toString();
if (site == POLAR) polar_userid = document.object()["x_user_id"].toDouble();
}
#else
refresh_token = RawJsonStringGrab(payload, "refresh_token");
access_token = RawJsonStringGrab(payload, "access_token");
#endif
// if we failed to extract then we have a big problem
// google uses a refresh token so trap for them only
if (((site == GOOGLE_DRIVE || site == KENTUNI) && refresh_token == "") || access_token == "") {
// Something failed.
// Only Google uses both refresh and access tokens.
QString error = QString(tr("Error retrieving authoriation credentials"));
QMessageBox oautherr(QMessageBox::Critical, tr("Authorization Error"), error);
oautherr.setDetailedText(error);
oautherr.exec();
return;
}
// now set the tokens etc
if (site == DROPBOX) {
service->setSetting(GC_DROPBOX_TOKEN, access_token);
QString info = QString(tr("Dropbox authorization was successful."));
QMessageBox information(QMessageBox::Information, tr("Information"), info);
information.exec();
} else if (site == SPORTTRACKS) {
service->setSetting(GC_SPORTTRACKS_TOKEN, access_token);
service->setSetting(GC_SPORTTRACKS_REFRESH_TOKEN, refresh_token);
service->setSetting(GC_SPORTTRACKS_LAST_REFRESH, QDateTime::currentDateTime());
QString info = QString(tr("SportTracks authorization was successful."));
QMessageBox information(QMessageBox::Information, tr("Information"), info);
information.exec();
} else if (site == POLAR) {
service->setSetting(GC_POLARFLOW_TOKEN, access_token);
service->setSetting(GC_POLARFLOW_USER_ID, polar_userid);
// we now need to bind the user, this is a one time deal.
QString url = QString("%1/v3/users").arg(GC_POLARFLOW_URL);
// request using the bearer token
QNetworkRequest request(url);
request.setRawHeader("Authorization", (QString("Bearer %1").arg(access_token)).toLatin1());
request.setRawHeader("Accept", "application/json");
request.setRawHeader("Content-Type", "application/json");
// data to post
QByteArray data;
data.append(QString("{\"member-id\":\"%1\"}").arg(context->athlete->cyclist));
// the request will fallback to this method on networkRequestFinished
// but we are done, so set ignore= true to get this function to just
// return without doing anything
ignore=true;
QNetworkReply *bind = manager->post(request, data);
// blocking request
QEventLoop loop;
connect(bind, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
// Bind response lists athlete details, we ignore them for now
QByteArray r = bind->readAll();
//qDebug()<<bind->errorString()<< "bind response="<<r;
QString info = QString(tr("PolarFlow authorization was successful."));
QMessageBox information(QMessageBox::Information, tr("Information"), info);
information.exec();
} else if (site == STRAVA) {
service->setSetting(GC_STRAVA_TOKEN, access_token);
QString info = QString(tr("Strava authorization was successful."));
QMessageBox information(QMessageBox::Information, tr("Information"), info);
information.exec();
} else if (site == CYCLING_ANALYTICS) {
service->setSetting(GC_CYCLINGANALYTICS_TOKEN, access_token);
QString info = QString(tr("Cycling Analytics authorization was successful."));
QMessageBox information(QMessageBox::Information, tr("Information"), info);
information.exec();
} else if (site == KENTUNI) {
service->setSetting(GC_UOK_GOOGLE_DRIVE_REFRESH_TOKEN, refresh_token);
service->setSetting(GC_UOK_GOOGLE_DRIVE_ACCESS_TOKEN, access_token);
service->setSetting(GC_UOK_GOOGLE_DRIVE_LAST_ACCESS_TOKEN_REFRESH, QDateTime::currentDateTime());
QString info = QString(tr("Kent University Google Drive authorization was successful."));
QMessageBox information(QMessageBox::Information, tr("Information"), info);
information.exec();
} else if (site == GOOGLE_DRIVE) {
service->setSetting(GC_GOOGLE_DRIVE_REFRESH_TOKEN, refresh_token);
service->setSetting(GC_GOOGLE_DRIVE_ACCESS_TOKEN, access_token);
service->setSetting(GC_GOOGLE_DRIVE_LAST_ACCESS_TOKEN_REFRESH, QDateTime::currentDateTime());
QString info = QString(tr("Google Drive authorization was successful."));
QMessageBox information(QMessageBox::Information, tr("Information"), info);
information.exec();
} else if (site == TODAYSPLAN) {
service->setSetting(GC_TODAYSPLAN_TOKEN, access_token);
QString info = QString(tr("Today's Plan authorization was successful."));
QMessageBox information(QMessageBox::Information, tr("Information"), info);
information.exec();
} else if (site == XERT) {
service->setSetting(GC_XERT_TOKEN, access_token);
service->setSetting(GC_XERT_REFRESH_TOKEN, refresh_token);
// Try without Message Box
//QString info = QString(tr("Xert authorization was successful."));
//QMessageBox information(QMessageBox::Information, tr("Information"), info);
//information.exec();
service->message = "Xert authorization was successful.";
}
} else {
// general error getting response
QString error = QString(tr("Error retrieving access token, %1 (%2)")).arg(reply->errorString()).arg(reply->error());
QMessageBox oautherr(QMessageBox::Critical, tr("SSL Token Refresh Error"), error);
oautherr.setDetailedText(error);
oautherr.exec();
}
// job done, dialog can be closed
//accept();
}

128
deprecated/OAuthManager.h Normal file
View File

@@ -0,0 +1,128 @@
/*
* Copyright (c) 2009 Justin F. Knotzke (jknotzke@shampoo.ca)
*
* 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; either version 2 of the License, or (at your option)
* any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef OAUTHMANAGER_H
#define OAUTHMANAGER_H
#include "GoldenCheetah.h"
#include "Pages.h"
#include "CloudService.h"
#ifdef GC_HAVE_KQOAUTH
#include <kqoauthmanager.h>
#endif
#include <QObject>
#include <QtGui>
#include <QWidget>
#include <QStackedLayout>
#include <QUrl>
#include <QSslSocket>
#ifndef NOWEBKIT
#include <QtWebKit>
#include <QWebView>
#include <QWebFrame>
#endif
// QUrl split into QUrlQuerty in QT5
#if QT_VERSION > 0x050000
#include <QUrlQuery>
#endif
// QWebEngine if on Mac, -or- we don't have webkit
#if defined(NOWEBKIT) || ((QT_VERSION > 0x050000) && defined(Q_OS_MAC))
#include <QWebEngineHistory>
#include <QWebEngineHistoryItem>
#include <QWebEnginePage>
#include <QWebEngineView>
#include <QWebEngineProfile>
#if (QT_VERSION >= 0x050600)
#include <QWebEngineCookieStore>
#endif
#endif
class OAuthManager : QObject
{
Q_OBJECT
G_OBJECT
public:
typedef enum {
NONE=0,
STRAVA,
DROPBOX,
CYCLING_ANALYTICS,
GOOGLE_DRIVE,
SPORTTRACKS,
TODAYSPLAN,
WITHINGS,
POLAR,
KENTUNI,
XERT
} OAuthSite;
// will work with old config via site and new via cloudservice (which is null for calendar and withings for now)
OAuthManager(Context *context, OAuthSite site, CloudService *service, QString baseURL="", QString clientsecret="");
void authorize();
void getTokenWithCode(QString code);
bool sslLibMissing() { return noSSLlib; }
private slots:
// Strava/Cyclinganalytics/Google
void loadFinished(bool ok);
void networkRequestFinished(QNetworkReply *reply);
void onSslErrors(QNetworkReply *reply, const QList<QSslError>&error);
#ifdef GC_HAVE_KQOAUTH
void onTemporaryTokenReceived(QString, QString);
void onAuthorizationReceived(QString, QString);
void onAccessTokenReceived(QString token, QString tokenSecret);
void onAuthorizedRequestDone();
void onRequestReady(QByteArray response);
void onAuthorizationPageRequested (QUrl pageUrl);
#endif
private:
Context *context;
bool noSSLlib;
bool ignore;
OAuthSite site;
CloudService *service;
QString baseURL; // can be passed, but typically is blank (used by Todays Plan)
QString clientsecret; // can be passed, but typicall is blank (used by Todays Plan)
QVBoxLayout *layout;
// QUrl split into QUrlQuerty in QT5
#if defined(NOWEBKIT) || ((QT_VERSION > 0x050000) && defined(Q_OS_MAC))
QWebEngineView *view;
#else
QWebView *view;
#endif
QNetworkAccessManager* manager;
QUrl url;
#ifdef GC_HAVE_KQOAUTH
KQOAuthManager *oauthManager;
KQOAuthRequest *oauthRequest;
#endif
};
#endif // OAUTHMANAGER_H

View File

@@ -0,0 +1,11 @@
prefix=/usr
exec_prefix=${prefix}
libdir=${prefix}/lib
includedir=${prefix}/include/QtKOAuth
Name: KQOAuth
Description: Qt OAuth support library
Version: 0.97
Requires: QtCore QtNetwork
Libs: -L${libdir} -lkqoauth
Cflags: -I${includedir}

View File

@@ -0,0 +1,105 @@
TARGET = kqoauth
DESTDIR = .
win32:DLLDESTDIR = $${DESTDIR}
VERSION = 0.97
TEMPLATE = lib
QT += network
CONFIG += \
create_prl
unix:!macx: CONFIG += static
INC_DIR = .
LIBS += -lssl -lcrypto
INCLUDEPATH += .
PUBLIC_HEADERS += kqoauthmanager.h \
kqoauthrequest.h \
kqoauthrequest_1.h \
kqoauthrequest_xauth.h \
kqoauthglobals.h
PRIVATE_HEADERS += kqoauthrequest_p.h \
kqoauthmanager_p.h \
kqoauthauthreplyserver.h \
kqoauthauthreplyserver_p.h \
kqoauthutils.h \
kqoauthrequest_xauth_p.h
HEADERS = \
$$PUBLIC_HEADERS \
$$PRIVATE_HEADERS
SOURCES += \
kqoauthmanager.cpp \
kqoauthrequest.cpp \
kqoauthutils.cpp \
kqoauthauthreplyserver.cpp \
kqoauthrequest_1.cpp \
kqoauthrequest_xauth.cpp
DEFINES += KQOAUTH
headers.files = \
$${PUBLIC_HEADERS} \
$${INC_DIR}/QtKOAuth
features.path = $$[QMAKE_MKSPECS]/features
features.files = ../kqoauth.prf
docs.files = ../doc/html
macx {
CONFIG += lib_bundle
QMAKE_FRAMEWORK_BUNDLE_NAME = $$TARGET
CONFIG(debug, debug|release): CONFIG += build_all
FRAMEWORK_HEADERS.version = Versions
FRAMEWORK_HEADERS.files = $$headers.files
FRAMEWORK_HEADERS.path = Headers
QMAKE_BUNDLE_DATA += FRAMEWORK_HEADERS
target.path = $$[QT_INSTALL_LIBS]
INSTALLS += \
target \
features \
postinstall
postinstall.path = target.path
postinstall.extra = install_name_tool -id $${target.path}/$${QMAKE_FRAMEWORK_BUNDLE_NAME}.framework/Versions/0/$${TARGET} $${target.path}/$${QMAKE_FRAMEWORK_BUNDLE_NAME}.framework/Versions/0/$${TARGET}
}
else:unix {
isEmpty( PREFIX ):INSTALL_PREFIX = /usr
else:INSTALL_PREFIX = $${PREFIX}
# this creates a pkgconfig file
system( ./pcfile.sh $${INSTALL_PREFIX} $${VERSION} )
pkgconfig.files = kqoauth.pc
target.path = $$[QT_INSTALL_LIBS]
headers.path = $${INSTALL_PREFIX}/include/QtKOAuth
docs.path = $${INSTALL_PREFIX}/share/doc/$${TARGET}-$${VERSION}/html
pkgconfig.path = $${target.path}/pkgconfig
INSTALLS += \
target \
headers \
docs \
pkgconfig \
features
}
else:windows {
INSTALL_PREFIX = $$[QT_INSTALL_PREFIX]
target.path = $${INSTALL_PREFIX}/lib
headers.path = $${INSTALL_PREFIX}/include/QtKOAuth
INSTALLS += \
target \
headers \
features
}
CONFIG(debug_and_release) {
build_pass:CONFIG(debug, debug|release) {
unix: TARGET = $$join(TARGET,,,_debug)
else: TARGET = $$join(TARGET,,,d)
}
}

View File

@@ -0,0 +1,111 @@
/**
* KQOAuth - An OAuth authentication library for Qt.
*
* Author: Johan Paul (johan.paul@gmail.com)
* http://www.johanpaul.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* KQOAuth 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with KQOAuth. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QTcpSocket>
#include <QStringList>
#include <QUrl>
#if QT_VERSION >= 0x050000
#include <QUrlQuery>
#endif
#include "kqoauthauthreplyserver.h"
#include "kqoauthauthreplyserver_p.h"
KQOAuthAuthReplyServerPrivate::KQOAuthAuthReplyServerPrivate(KQOAuthAuthReplyServer *parent):
q_ptr(parent)
{
}
KQOAuthAuthReplyServerPrivate::~KQOAuthAuthReplyServerPrivate()
{
}
void KQOAuthAuthReplyServerPrivate::onIncomingConnection() {
Q_Q(KQOAuthAuthReplyServer);
socket = q->nextPendingConnection();
connect(socket, SIGNAL(readyRead()),
this, SLOT(onBytesReady()), Qt::UniqueConnection);
}
void KQOAuthAuthReplyServerPrivate::onBytesReady() {
Q_Q(KQOAuthAuthReplyServer);
QByteArray reply;
QByteArray content;
content.append("<HTML></HTML>");
reply.append("HTTP/1.0 200 OK \r\n");
reply.append("Content-Type: text/html; charset=\"utf-8\"\r\n");
reply.append(QString("Content-Length: %1\r\n").arg(content.size()));
reply.append("\r\n");
reply.append(content);
socket->write(reply);
QByteArray data = socket->readAll();
QMultiMap<QString, QString> queryParams = parseQueryParams(&data);
socket->disconnectFromHost();
q->close();
emit q->verificationReceived(queryParams);
}
QMultiMap<QString, QString> KQOAuthAuthReplyServerPrivate::parseQueryParams(QByteArray *data) {
QString splitGetLine = QString(*data).split("\r\n").first(); // Retrieve the first line with query params.
splitGetLine.remove("GET "); // Clean the line from GET
splitGetLine.remove("HTTP/1.1"); // From HTTP
splitGetLine.remove("\r\n"); // And from rest.
splitGetLine.prepend("http://localhost"); // Now, make it a URL
QUrl getTokenUrl(splitGetLine);
#if QT_VERSION < 0x050000
QList< QPair<QString, QString> > tokens = getTokenUrl.queryItems(); // Ask QUrl to do our work.
#else
QList< QPair<QString, QString> > tokens = QUrlQuery(getTokenUrl.query()).queryItems(); // Ask QUrl to do our work.
#endif
QMultiMap<QString, QString> queryParams;
QPair<QString, QString> tokenPair;
foreach (tokenPair, tokens) {
queryParams.insert(tokenPair.first.trimmed(), tokenPair.second.trimmed());
}
return queryParams;
}
KQOAuthAuthReplyServer::KQOAuthAuthReplyServer(QObject *parent) :
QTcpServer(parent),
d_ptr( new KQOAuthAuthReplyServerPrivate(this) )
{
Q_D(KQOAuthAuthReplyServer);
connect(this, SIGNAL(newConnection()),
d, SLOT(onIncomingConnection()));
}
KQOAuthAuthReplyServer::~KQOAuthAuthReplyServer()
{
delete d_ptr;
}

View File

@@ -0,0 +1,47 @@
/**
* KQOAuth - An OAuth authentication library for Qt.
*
* Author: Johan Paul (johan.paul@gmail.com)
* http://www.johanpaul.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* KQOAuth 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with KQOAuth. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KQOAUTHAUTHREPLYSERVER_H
#define KQOAUTHAUTHREPLYSERVER_H
#include <QTcpServer>
#include "kqoauthglobals.h"
class KQOAuthAuthReplyServerPrivate;
class KQOAUTH_EXPORT KQOAuthAuthReplyServer : public QTcpServer
{
Q_OBJECT
public:
explicit KQOAuthAuthReplyServer(QObject *parent);
~KQOAuthAuthReplyServer();
Q_SIGNALS:
void verificationReceived(QMultiMap<QString, QString>);
private:
KQOAuthAuthReplyServerPrivate * const d_ptr;
Q_DECLARE_PRIVATE(KQOAuthAuthReplyServer);
Q_DISABLE_COPY(KQOAuthAuthReplyServer);
};
#endif // KQOAUTHAUTHREPLYSERVER_H

View File

@@ -0,0 +1,48 @@
/**
* KQOAuth - An OAuth authentication library for Qt.
*
* Author: Johan Paul (johan.paul@gmail.com)
* http://www.johanpaul.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* KQOAuth 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with KQOAuth. If not, see <http://www.gnu.org/licenses/>.
*/
// Note this class shouldn't be copied or used and the implementation might change later.
#ifndef KQOAUTHAUTHREPLYSERVER_P_H
#define KQOAUTHAUTHREPLYSERVER_P_H
#include "kqoauthauthreplyserver.h"
#include <QMultiMap>
#include <QString>
class KQOAUTH_EXPORT KQOAuthAuthReplyServerPrivate: public QObject
{
Q_OBJECT
public:
KQOAuthAuthReplyServerPrivate( KQOAuthAuthReplyServer * parent );
~KQOAuthAuthReplyServerPrivate();
QMultiMap<QString, QString> parseQueryParams(QByteArray *sdata);
public Q_SLOTS:
void onIncomingConnection();
void onBytesReady();
public:
KQOAuthAuthReplyServer * q_ptr;
Q_DECLARE_PUBLIC(KQOAuthAuthReplyServer);
QTcpSocket *socket;
};
#endif // KQOAUTHAUTHREPLYSERVER_P_H

View File

@@ -0,0 +1,44 @@
/**
* KQOAuth - An OAuth authentication library for Qt.
*
* Author: Johan Paul (johan.paul@gmail.com)
* http://www.johanpaul.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* KQOAuth 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with KQOAuth. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KQOAUTHGLOBALS_H
#define KQOAUTHGLOBALS_H
#include <QtCore/qglobal.h>
#if defined(KQOAUTH)
# define KQOAUTH_EXPORT Q_DECL_EXPORT
#else
# define KQOAUTH_EXPORT Q_DECL_IMPORT
#endif
//////////// Static constant definitions ///////////
const QString OAUTH_KEY_CONSUMER("oauth_consumer");
const QString OAUTH_KEY_CONSUMER_KEY("oauth_consumer_key");
const QString OAUTH_KEY_TOKEN("oauth_token");
const QString OAUTH_KEY_TOKEN_SECRET("oauth_token_secret");
const QString OAUTH_KEY_SIGNATURE_METHOD("oauth_signature_method");
const QString OAUTH_KEY_TIMESTAMP("oauth_timestamp");
const QString OAUTH_KEY_NONCE("oauth_nonce");
const QString OAUTH_KEY_SIGNATURE("oauth_signature");
const QString OAUTH_KEY_CALLBACK("oauth_callback");
const QString OAUTH_KEY_VERIFIER("oauth_verifier");
const QString OAUTH_KEY_VERSION("oauth_version");
#endif // KQOAUTHGLOBALS_H

View File

@@ -0,0 +1,749 @@
/**
* KQOAuth - An OAuth authentication library for Qt.
*
* Author: Johan Paul (johan.paul@gmail.com)
* http://www.johanpaul.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* KQOAuth 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with KQOAuth. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QtCore>
#include <QDesktopServices>
#if QT_VERSION >= 0x050000
#include <QUrlQuery>
#endif
#include "kqoauthmanager.h"
#include "kqoauthmanager_p.h"
////////////// Private d_ptr implementation ////////////////
KQOAuthManagerPrivate::KQOAuthManagerPrivate(KQOAuthManager *parent) :
error(KQOAuthManager::NoError) ,
r(0) ,
opaqueRequest(new KQOAuthRequest) ,
q_ptr(parent) ,
callbackServer(new KQOAuthAuthReplyServer(parent)) ,
isVerified(false) ,
isAuthorized(false) ,
autoAuth(false),
handleAuthPageOpening(true),
networkManager(new QNetworkAccessManager),
managerUserSet(false)
{
}
KQOAuthManagerPrivate::~KQOAuthManagerPrivate() {
delete opaqueRequest;
opaqueRequest = 0;
if (!managerUserSet) {
delete networkManager;
networkManager = 0;
}
}
QList< QPair<QString, QString> > KQOAuthManagerPrivate::createQueryParams(const KQOAuthParameters &requestParams) {
QList<QString> requestKeys = requestParams.keys();
QList<QString> requestValues = requestParams.values();
QList< QPair<QString, QString> > result;
for(int i=0; i<requestKeys.size(); i++) {
result.append( qMakePair(requestKeys.at(i),
requestValues.at(i))
);
}
return result;
}
QMultiMap<QString, QString> KQOAuthManagerPrivate::createTokensFromResponse(QByteArray reply) {
QMultiMap<QString, QString> result;
QString replyString(reply);
QStringList parameterPairs = replyString.split('&', QString::SkipEmptyParts);
foreach (const QString &parameterPair, parameterPairs) {
QStringList parameter = parameterPair.split('=');
result.insert(parameter.value(0), parameter.value(1));
}
return result;
}
bool KQOAuthManagerPrivate::setSuccessfulRequestToken(const QMultiMap<QString, QString> &request) {
if (currentRequestType == KQOAuthRequest::TemporaryCredentials) {
hasTemporaryToken = (!QString(request.value("oauth_token")).isEmpty() && !QString(request.value("oauth_token_secret")).isEmpty());
} else {
return false;
}
if (hasTemporaryToken) {
requestToken = QUrl::fromPercentEncoding( QString(request.value("oauth_token")).toLocal8Bit() );
requestTokenSecret = QUrl::fromPercentEncoding( QString(request.value("oauth_token_secret")).toLocal8Bit() );
}
return hasTemporaryToken;
}
bool KQOAuthManagerPrivate::setSuccessfulAuthorized(const QMultiMap<QString, QString> &request ) {
if (currentRequestType == KQOAuthRequest::AccessToken) {
isAuthorized = (!QString(request.value("oauth_token")).isEmpty() && !QString(request.value("oauth_token_secret")).isEmpty());
} else {
return false;
}
if (isAuthorized) {
requestToken = QUrl::fromPercentEncoding( QString(request.value("oauth_token")).toLocal8Bit() );
requestTokenSecret = QUrl::fromPercentEncoding( QString(request.value("oauth_token_secret")).toLocal8Bit() );
}
return isAuthorized;
}
void KQOAuthManagerPrivate::emitTokens() {
Q_Q(KQOAuthManager);
if (this->requestToken.isEmpty() || this->requestTokenSecret.isEmpty()) {
error = KQOAuthManager::RequestUnauthorized;
}
if (currentRequestType == KQOAuthRequest::TemporaryCredentials) {
// Signal that we are ready to use the protected resources.
emit q->temporaryTokenReceived(this->requestToken, this->requestTokenSecret);
}
if (currentRequestType == KQOAuthRequest::AccessToken) {
// Signal that we are ready to use the protected resources.
emit q->accessTokenReceived(this->requestToken, this->requestTokenSecret);
}
emit q->receivedToken(this->requestToken, this->requestTokenSecret);
}
bool KQOAuthManagerPrivate::setupCallbackServer() {
return callbackServer->listen();
}
/////////////// Public implementation ////////////////
KQOAuthManager::KQOAuthManager(QObject *parent) :
QObject(parent) ,
d_ptr(new KQOAuthManagerPrivate(this))
{
qsrand(QTime::currentTime().msec()); // We need to seed the nonce random number with something.
// However, we cannot do this while generating the nonce since
// we might get the same seed. So initializing here should be fine.
}
KQOAuthManager::~KQOAuthManager()
{
delete d_ptr;
}
void KQOAuthManager::executeRequest(KQOAuthRequest *request) {
Q_D(KQOAuthManager);
d->r = request;
if (request == 0) {
qWarning() << "Request is NULL. Cannot proceed.";
d->error = KQOAuthManager::RequestError;
return;
}
if (!request->requestEndpoint().isValid()) {
qWarning() << "Request endpoint URL is not valid. Cannot proceed.";
d->error = KQOAuthManager::RequestEndpointError;
return;
}
if (!request->isValid()) {
qWarning() << "Request is not valid. Cannot proceed.";
d->error = KQOAuthManager::RequestValidationError;
return;
}
d->currentRequestType = request->requestType();
QNetworkRequest networkRequest;
networkRequest.setUrl( request->requestEndpoint() );
if (d->autoAuth && d->currentRequestType == KQOAuthRequest::TemporaryCredentials) {
d->setupCallbackServer();
connect(d->callbackServer, SIGNAL(verificationReceived(QMultiMap<QString, QString>)),
this, SLOT( onVerificationReceived(QMultiMap<QString, QString>)));
QString serverString = "http://localhost:";
serverString.append(QString::number(d->callbackServer->serverPort()));
request->setCallbackUrl(QUrl(serverString));
}
// And now fill the request with "Authorization" header data.
QList<QByteArray> requestHeaders = request->requestParameters();
QByteArray authHeader;
bool first = true;
foreach (const QByteArray header, requestHeaders) {
if (!first) {
authHeader.append(", ");
} else {
authHeader.append("OAuth ");
first = false;
}
authHeader.append(header);
}
networkRequest.setRawHeader("Authorization", authHeader);
connect(d->networkManager, SIGNAL(finished(QNetworkReply *)),
this, SLOT(onRequestReplyReceived(QNetworkReply *)), Qt::UniqueConnection);
disconnect(d->networkManager, SIGNAL(finished(QNetworkReply *)),
this, SLOT(onAuthorizedRequestReplyReceived(QNetworkReply *)));
if (request->httpMethod() == KQOAuthRequest::GET) {
// Get the requested additional params as a list of pairs we can give QUrl
QList< QPair<QString, QString> > urlParams = d->createQueryParams(request->additionalParameters());
// Take the original URL and append the query params to it.
QUrl urlWithParams = networkRequest.url();
#if QT_VERSION < 0x050000
urlWithParams.setQueryItems(urlParams);
#else
QUrlQuery query;
query.setQueryItems(urlParams);
urlWithParams.setQuery(query);
#endif
networkRequest.setUrl(urlWithParams);
qDebug() << "urlWithParams:" << urlWithParams;
// Submit the request including the params.
QNetworkReply *reply = d->networkManager->get(networkRequest);
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(slotError(QNetworkReply::NetworkError)));
d->requestMap.insert( request, reply );
} else if (request->httpMethod() == KQOAuthRequest::POST) {
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, request->contentType());
qDebug() << networkRequest.rawHeaderList();
qDebug() << networkRequest.rawHeader("Authorization");
qDebug() << networkRequest.rawHeader("Content-Type");
QNetworkReply *reply;
if (request->contentType() == "application/x-www-form-urlencoded") {
reply = d->networkManager->post(networkRequest, request->requestBody());
} else {
reply = d->networkManager->post(networkRequest, request->rawData());
}
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(slotError(QNetworkReply::NetworkError)));
d->requestMap.insert( request, reply );
}
d->r->requestTimerStart();
}
void KQOAuthManager::executeAuthorizedRequest(KQOAuthRequest *request, int id) {
Q_D(KQOAuthManager);
d->r = request;
if (request == 0) {
qWarning() << "Request is NULL. Cannot proceed.";
d->error = KQOAuthManager::RequestError;
return;
}
if (!request->requestEndpoint().isValid()) {
qWarning() << "Request endpoint URL is not valid. Cannot proceed.";
d->error = KQOAuthManager::RequestEndpointError;
return;
}
if (!request->isValid()) {
qWarning() << "Request is not valid. Cannot proceed.";
d->error = KQOAuthManager::RequestValidationError;
return;
}
d->currentRequestType = request->requestType();
QNetworkRequest networkRequest;
networkRequest.setUrl( request->requestEndpoint() );
if ( d->currentRequestType != KQOAuthRequest::AuthorizedRequest){
qWarning() << "Not Authorized Request. Cannot proceed";
d->error = KQOAuthManager::RequestError;
return;
}
// And now fill the request with "Authorization" header data.
QList<QByteArray> requestHeaders = request->requestParameters();
QByteArray authHeader;
bool first = true;
foreach (const QByteArray header, requestHeaders) {
if (!first) {
authHeader.append(", ");
} else {
authHeader.append("OAuth ");
first = false;
}
authHeader.append(header);
}
networkRequest.setRawHeader("Authorization", authHeader);
disconnect(d->networkManager, SIGNAL(finished(QNetworkReply *)),
this, SLOT(onRequestReplyReceived(QNetworkReply *)));
connect(d->networkManager, SIGNAL(finished(QNetworkReply *)),
this, SLOT(onAuthorizedRequestReplyReceived(QNetworkReply*)), Qt::UniqueConnection);
QNetworkReply *reply;
if (request->httpMethod() == KQOAuthRequest::POST) {
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, request->contentType());
/*
qDebug() << networkRequest.rawHeaderList();
qDebug() << networkRequest.rawHeader("Authorization");
qDebug() << networkRequest.rawHeader("Content-Type");
*/
if (request->contentType() == "application/x-www-form-urlencoded") {
reply = d->networkManager->post(networkRequest, request->requestBody());
} else {
reply = d->networkManager->post(networkRequest, request->rawData());
}
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(slotError(QNetworkReply::NetworkError)));
connect(request, SIGNAL(requestTimedout()),
this, SLOT(requestTimeout()));
d->requestMap.insert( request, reply );
} else {
// Get the requested additional params as a list of pairs we can give QUrl
QList< QPair<QString, QString> > urlParams = d->createQueryParams(request->additionalParameters());
// Take the original URL and append the query params to it.
QUrl urlWithParams = networkRequest.url();
#if QT_VERSION < 0x050000
urlWithParams.setQueryItems(urlParams);
#else
QUrlQuery query;
query.setQueryItems(urlParams);
urlWithParams.setQuery(query);
#endif
networkRequest.setUrl(urlWithParams);
// Submit the request including the params.
if (request->httpMethod() == KQOAuthRequest::GET)
reply = d->networkManager->get(networkRequest);
else if (request->httpMethod() == KQOAuthRequest::HEAD)
reply = d->networkManager->head(networkRequest);
else if (request->httpMethod() == KQOAuthRequest::DELETE)
reply = d->networkManager->deleteResource(networkRequest);
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(slotError(QNetworkReply::NetworkError)));
connect(request, SIGNAL(requestTimedout()),
this, SLOT(requestTimeout()));
d->requestMap.insert( request, reply );
}
d->requestIds.insert(reply, id);
d->r->requestTimerStart();
}
void KQOAuthManager::setHandleUserAuthorization(bool set) {
Q_D(KQOAuthManager);
d->autoAuth = set;
}
void KQOAuthManager::setHandleAuthorizationPageOpening(bool set) {
Q_D(KQOAuthManager);
d->handleAuthPageOpening = set;
}
bool KQOAuthManager::hasTemporaryToken() {
Q_D(KQOAuthManager);
return d->hasTemporaryToken;
}
bool KQOAuthManager::isVerified() {
Q_D(KQOAuthManager);
return d->isVerified;
}
bool KQOAuthManager::isAuthorized() {
Q_D(KQOAuthManager);
return d->isAuthorized;
}
KQOAuthManager::KQOAuthError KQOAuthManager::lastError() {
Q_D(KQOAuthManager);
return d->error;
}
void KQOAuthManager::setNetworkManager(QNetworkAccessManager *manager) {
Q_D(KQOAuthManager);
if (manager == 0) {
d->error = KQOAuthManager::ManagerError;
return;
}
if (!d->managerUserSet) {
delete d->networkManager;
}
d->managerUserSet = true;
d->networkManager = manager;
}
QNetworkAccessManager * KQOAuthManager::networkManager() const {
Q_D(const KQOAuthManager);
if (d->managerUserSet) {
return d->networkManager;
} else {
return NULL;
}
}
//////////// Public convenience API /////////////
void KQOAuthManager::getUserAuthorization(QUrl authorizationEndpoint) {
Q_D(KQOAuthManager);
if (!d->hasTemporaryToken) {
qWarning() << "No temporary tokens retreieved. Cannot get user authorization.";
d->error = KQOAuthManager::RequestUnauthorized;
return;
}
if (!authorizationEndpoint.isValid()) {
qWarning() << "Authorization endpoint not valid. Cannot proceed.";
d->error = KQOAuthManager::RequestEndpointError;
return;
}
d->error = KQOAuthManager::NoError;
QPair<QString, QString> tokenParam = qMakePair(QString("oauth_token"), QString(d->requestToken));
QUrl openWebPageUrl(authorizationEndpoint.toString(), QUrl::StrictMode);
#if QT_VERSION < 0x050000
openWebPageUrl.addQueryItem(tokenParam.first, tokenParam.second);
#else
QUrlQuery query(openWebPageUrl);
query.addQueryItem(tokenParam.first, tokenParam.second);
openWebPageUrl.setQuery(query);
#endif
if (d->handleAuthPageOpening) {
// Open the user's default browser to the resource authorization page provided
// by the service.
QDesktopServices::openUrl(openWebPageUrl);
} else {
emit authorizationPageRequested(openWebPageUrl);
}
}
void KQOAuthManager::getUserAccessTokens(QUrl accessTokenEndpoint) {
Q_D(KQOAuthManager);
if (!d->isVerified) {
qWarning() << "Not verified. Cannot get access tokens.";
d->error = KQOAuthManager::RequestUnauthorized;
return;
}
if (!accessTokenEndpoint.isValid()) {
qWarning() << "Endpoint for access token exchange is not valid. Cannot proceed.";
d->error = KQOAuthManager::RequestEndpointError;
return;
}
d->error = KQOAuthManager::NoError;
d->opaqueRequest->clearRequest();
d->opaqueRequest->initRequest(KQOAuthRequest::AccessToken, accessTokenEndpoint);
d->opaqueRequest->setToken(d->requestToken);
d->opaqueRequest->setTokenSecret(d->requestTokenSecret);
d->opaqueRequest->setVerifier(d->requestVerifier);
d->opaqueRequest->setConsumerKey(d->consumerKey);
d->opaqueRequest->setConsumerSecretKey(d->consumerKeySecret);
d->opaqueRequest->setSignatureMethod(d->signatureMethod);
executeRequest(d->opaqueRequest);
}
void KQOAuthManager::verifyToken(const QString &token, const QString &verifier) {
QMultiMap<QString, QString> params;
params.insert("oauth_token", token);
params.insert("oauth_verifier", verifier);
onVerificationReceived(params);
}
void KQOAuthManager::sendAuthorizedRequest(QUrl requestEndpoint, const KQOAuthParameters &requestParameters) {
Q_D(KQOAuthManager);
if (!d->isAuthorized) {
qWarning() << "No access tokens retrieved. Cannot send authorized requests.";
d->error = KQOAuthManager::RequestUnauthorized;
return;
}
if (!requestEndpoint.isValid()) {
qWarning() << "Endpoint for authorized request is not valid. Cannot proceed.";
d->error = KQOAuthManager::RequestEndpointError;
return;
}
d->error = KQOAuthManager::NoError;
d->opaqueRequest->clearRequest();
d->opaqueRequest->initRequest(KQOAuthRequest::AuthorizedRequest, requestEndpoint);
d->opaqueRequest->setAdditionalParameters(requestParameters);
d->opaqueRequest->setToken(d->requestToken);
d->opaqueRequest->setTokenSecret(d->requestTokenSecret);
d->opaqueRequest->setConsumerKey(d->consumerKey);
d->opaqueRequest->setConsumerSecretKey(d->consumerKeySecret);
executeRequest(d->opaqueRequest);
}
/////////////// Private slots //////////////////
void KQOAuthManager::onRequestReplyReceived( QNetworkReply *reply ) {
Q_D(KQOAuthManager);
QNetworkReply::NetworkError networkError = reply->error();
switch (networkError) {
case QNetworkReply::NoError:
d->error = KQOAuthManager::NoError;
break;
case QNetworkReply::ContentAccessDenied:
case QNetworkReply::AuthenticationRequiredError:
d->error = KQOAuthManager::RequestUnauthorized;
break;
default:
d->error = KQOAuthManager::NetworkError;
break;
}
// Let's disconnect this slot first
/*
disconnect(d->networkManager, SIGNAL(finished(QNetworkReply *)),
this, SLOT(onRequestReplyReceived(QNetworkReply *)));
*/
// Read the content of the reply from the network.
QByteArray networkReply = reply->readAll();
d->r = d->requestMap.key(reply);
if( d->r ) {
d->requestMap.remove(d->r);
disconnect(d->r, SIGNAL(requestTimedout()),
this, SLOT(requestTimeout()));
// Stop any timer we have set on the request.
d->r->requestTimerStop();
d->currentRequestType = d->r->requestType();
}
// Just don't do anything if we didn't get anything useful.
if(networkReply.isEmpty()) {
reply->deleteLater();
return;
}
QMultiMap<QString, QString> responseTokens;
// We need to emit the signal even if we got an error.
if (d->error != KQOAuthManager::NoError) {
reply->deleteLater();
emit requestReady(networkReply);
d->emitTokens();
return;
}
responseTokens = d->createTokensFromResponse(networkReply);
d->opaqueRequest->clearRequest();
d->opaqueRequest->setHttpMethod(KQOAuthRequest::POST); // XXX FIXME: Convenient API does not support GET
if (!d->isAuthorized || !d->isVerified) {
if (d->setSuccessfulRequestToken(responseTokens)) {
qDebug() << "Successfully got request tokens.";
d->consumerKey = d->r->consumerKeyForManager();
d->consumerKeySecret = d->r->consumerKeySecretForManager();
d->signatureMethod = d->r->requestSignatureMethodForManager();
d->opaqueRequest->setSignatureMethod(KQOAuthRequest::HMAC_SHA1);
d->opaqueRequest->setCallbackUrl(d->r->callbackUrlForManager());
d->emitTokens();
} else if (d->setSuccessfulAuthorized(responseTokens)) {
qDebug() << "Successfully got access tokens.";
d->opaqueRequest->setSignatureMethod(KQOAuthRequest::HMAC_SHA1);
d->emitTokens();
} else if (d->currentRequestType == KQOAuthRequest::AuthorizedRequest) {
emit authorizedRequestDone();
}
}
emit requestReady(networkReply);
reply->deleteLater(); // We need to clean this up, after the event processing is done.
}
void KQOAuthManager::onAuthorizedRequestReplyReceived( QNetworkReply *reply ) {
Q_D(KQOAuthManager);
QNetworkReply::NetworkError networkError = reply->error();
switch (networkError) {
case QNetworkReply::NoError:
d->error = KQOAuthManager::NoError;
break;
case QNetworkReply::ContentAccessDenied:
case QNetworkReply::AuthenticationRequiredError:
d->error = KQOAuthManager::RequestUnauthorized;
break;
default:
d->error = KQOAuthManager::NetworkError;
break;
}
/*
disconnect(d->networkManager, SIGNAL(finished(QNetworkReply *)),
this, SLOT(onAuthorizedRequestReplyReceived(QNetworkReply *)));
*/
// Read the content of the reply from the network.
QByteArray networkReply = reply->readAll();
int id = d->requestIds.take(reply);
d->r = d->requestMap.key(reply);
if( d->r ) {
d->requestMap.remove(d->r);
disconnect(d->r, SIGNAL(requestTimedout()),
this, SLOT(requestTimeout()));
// Stop any timer we have set on the request.
d->r->requestTimerStop();
d->currentRequestType = d->r->requestType();
}
// Just don't do anything if we didn't get anything useful.
if(networkReply.isEmpty()) {
reply->deleteLater();
return;
}
// We need to emit the signal even if we got an error.
if (d->error != KQOAuthManager::NoError) {
qWarning() << "Network reply error";
return;
}
d->opaqueRequest->clearRequest();
d->opaqueRequest->setHttpMethod(KQOAuthRequest::POST); // XXX FIXME: Convenient API does not support GET
if (d->currentRequestType == KQOAuthRequest::AuthorizedRequest) {
emit authorizedRequestDone();
}
emit authorizedRequestReady(networkReply, id);
reply->deleteLater();
}
void KQOAuthManager::onVerificationReceived(QMultiMap<QString, QString> response) {
Q_D(KQOAuthManager);
QString token = response.value("oauth_token");
QString verifier = response.value("oauth_verifier");
if (verifier.isEmpty()) {
d->error = KQOAuthManager::RequestUnauthorized;
}
verifier = QUrl::fromPercentEncoding(verifier.toUtf8()); // We get the raw URL response here so we need to convert it back
// to plain string so we can percent encode it again later in requests.
if (d->error == KQOAuthManager::NoError) {
d->requestVerifier = verifier;
d->isVerified = true;
}
emit authorizationReceived(token, verifier);
}
void KQOAuthManager::slotError(QNetworkReply::NetworkError error) {
Q_UNUSED(error)
Q_D(KQOAuthManager);
d->error = KQOAuthManager::NetworkError;
QByteArray emptyResponse;
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
d->r = d->requestMap.key(reply);
d->currentRequestType = d->r->requestType();
if( d->requestIds.contains(reply) ) {
int id = d->requestIds.value(reply);
emit authorizedRequestReady(emptyResponse, id);
}
else if ( d->currentRequestType == KQOAuthRequest::AuthorizedRequest) {
// does this signal always have to be emitted if there is an error
// or can is it only valid for KQOAuthRequest::AuthorizedRequest?
emit authorizedRequestDone();
}
else
emit requestReady(emptyResponse);
reply->deleteLater();
}
void KQOAuthManager::requestTimeout() {
Q_D(KQOAuthManager);
KQOAuthRequest *request = qobject_cast<KQOAuthRequest *>(sender());
if( d->requestMap.contains(request)) {
qWarning() << "KQOAuthManager::requestTimeout: Calling abort";
d->requestMap.value(request)->abort();
}
else
qWarning() << "KQOAuthManager::requestTimeout: The KQOAuthRequest was not found";
}

View File

@@ -0,0 +1,201 @@
/**
* KQOAuth - An OAuth authentication library for Qt.
*
* Author: Johan Paul (johan.paul@gmail.com)
* http://www.johanpaul.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* KQOAuth 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with KQOAuth. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KQOAUTHMANAGER_H
#define KQOAUTHMANAGER_H
#include <QObject>
#include <QMultiMap>
#include <QNetworkReply>
#include "kqoauthrequest.h"
class KQOAuthRequest;
class KQOAuthManagerThread;
class KQOAuthManagerPrivate;
class QNetworkAccessManager;
class QUrl;
class QByteArray;
class KQOAUTH_EXPORT KQOAuthManager : public QObject
{
Q_OBJECT
public:
enum KQOAuthError {
NoError, // No error
NetworkError, // Network error: timeout, cannot connect.
RequestEndpointError, // Request endpoint is not valid.
RequestValidationError, // Request is not valid: some parameter missing?
RequestUnauthorized, // Authorization error: trying to access a resource without tokens.
RequestError, // The given request to KQOAuthManager is invalid: NULL?,
ManagerError // Manager error, cannot use for sending requests.
};
explicit KQOAuthManager(QObject *parent = 0);
~KQOAuthManager();
KQOAuthError lastError();
/**
* The manager executes the given request. It takes the HTTP parameters from the
* request and uses QNetworkAccessManager to submit the HTTP request to the net.
* When the request is done it will emit signal requestReady(QByteArray networkReply).
* NOTE: At the moment there is no timeout for the request.
*/
void executeRequest(KQOAuthRequest *request);
void executeAuthorizedRequest(KQOAuthRequest *request, int id);
/**
* Indicates to the user that KQOAuthManager should handle user authorization by
* opening the user's default browser and parsing the reply from the service.
* By setting the parameter to true, KQOAuthManager will store intermediate results
* of the OAuth 1.0 process in its own opaque request. This information is used in
* the user authorization process and also when calling sendAuthorizedRequest().
* NOTE: You need to set this to true if you want to use getUserAccessTokens() or
* sendAuthorizedRequest().
*/
void setHandleUserAuthorization(bool set);
/** Indicates whether the KQOAuthManager should launch the browser with the user
* authorization page itself.
*
* If set to true (the default), the KQOAuthManager uses QDesktopServices::openUrl()
* for opening the browser. Otherwise it emits the authorizationPageRequested()
* signal which must then be handled by the calling code.
*/
void setHandleAuthorizationPageOpening(bool set);
/**
* Returns true if the KQOAuthManager has retrieved the oauth_token value. Otherwise
* return false.
*/
bool hasTemporaryToken();
/**
* Returns true if the user has authorized us to use the protected resources. Otherwise
* returns false.
* NOTE: In order for KQOAuthManager to know if the user has authorized us to use the
* protected resources, KQOAuthManager must be in control of the user authorization
* process. Hence, this returns true if setHandleUserAuthorization() is set to true
* and the user is authorized with getUserAuthorization().
*/
bool isVerified();
/**
* Returns true if KQOAuthManager has the access token and hence can access the protected
* resources. Otherwise returns false.
* NOTE: In order for KQOAuthManager to know if we have access to protected resource
* KQOAuthManager must be in control of the user authorization process and requesting
* the acess token. Hence, this returns true if setHandleUserAuthorization() is set to true
* and the user is authorized with getUserAuthorization() and the access token must be retrieved
* with getUserAccessTokens.
*/
bool isAuthorized();
/**
* This is a convenience API for authorizing the user.
* The call will open the user's default browser, setup a local HTTP server and parse the reply from the
* service after the user has authorized us to access protected resources. If the user authorizes
* us to access protected resources, the verifier token is stored in KQOAuthManager for further use.
* In order to use this method, you must set setHandleUserAuthorization() to true.
*/
void getUserAuthorization(QUrl authorizationEndpoint);
/**
* This is a convenience API for retrieving the access token in exchange for the temporary token and the
* verifier.
* This call will create a KQOAuthRequest and use the previously stored temporary token and verifier to
* exchange for the access token, which will be used to access the protected resources.
* Note that in order to use this method, KQOAuthManager must be in control of the user authorization process.
* Set setHandleUserAuthorization() to true and retrieve user authorization with void getUserAuthorization.
*/
void getUserAccessTokens(QUrl accessTokenEndpoint);
/**
* This is a conveience API for setting the token verifier.
* If setHandleUserAuthorization() is set to false you need to call this function before calling
* getUserAccessTokens()
*/
void verifyToken(const QString &token, const QString &verifier);
/**
* Sends a request to the protected resources. Parameters for the request are service specific and
* are given to the 'requestParameters' as parameters.
* Note that in order to use this method, KQOAuthManager must be in control of the user authorization process.
* Set setHandleUserAuthorization() to true and retrieve user authorization with void getUserAuthorization.
*/
void sendAuthorizedRequest(QUrl requestEndpoint, const KQOAuthParameters &requestParameters);
/**
* Sets a custom QNetworkAccessManager to handle network requests. This method can be useful if the
* application is using some proxy settings for example.
* The application is responsible for deleting this manager. KQOAuthManager will not delete any
* previously given manager.
* If the manager is NULL, the manager will not be set and the KQOAuthManager::Error.
* If no manager is given, KQOAuthManager will use the default one it will create by itself.
*/
void setNetworkManager(QNetworkAccessManager *manager);
/**
* Returns the given QNetworkAccessManager. Returns NULL if none is given.
*/
QNetworkAccessManager* networkManager() const;
Q_SIGNALS:
// This signal will be emitted after each request has got a reply.
// Parameter is the raw response from the service.
void requestReady(QByteArray networkReply);
void authorizedRequestReady(QByteArray networkReply, int id);
// This signal will be emitted when the authorization page should be opened if
// setHandleAuthorizationPageOpening() is set to false.
void authorizationPageRequested(QUrl pageUrl);
// This signal will be emited when we have an request tokens available
// (either temporary resource tokens, or authorization tokens).
void receivedToken(QString oauth_token, QString oauth_token_secret); // oauth_token, oauth_token_secret
// This signal is emited when temporary tokens are returned from the service.
// Note that this signal is also emited in case temporary tokens are not available.
void temporaryTokenReceived(QString oauth_token, QString oauth_token_secret); // oauth_token, oauth_token_secret
// This signal is emited when the user has authenticated the application to
// communicate with the protected resources. Next we need to exchange the
// temporary tokens for access tokens.
// Note that this signal is also emited if user denies access.
void authorizationReceived(QString oauth_token, QString oauth_verifier); // oauth_token, oauth_verifier
// This signal is emited when access tokens are received from the service. We are
// ready to start communicating with the protected resources.
void accessTokenReceived(QString oauth_token, QString oauth_token_secret); // oauth_token, oauth_token_secret
// This signal is emited when the authorized request is done.
// This ends the kQOAuth interactions.
void authorizedRequestDone();
private Q_SLOTS:
void onRequestReplyReceived( QNetworkReply *reply );
void onAuthorizedRequestReplyReceived( QNetworkReply *reply );
void onVerificationReceived(QMultiMap<QString, QString> response);
void slotError(QNetworkReply::NetworkError error);
void requestTimeout();
private:
KQOAuthManagerPrivate *d_ptr;
Q_DECLARE_PRIVATE(KQOAuthManager);
Q_DISABLE_COPY(KQOAuthManager);
};
#endif // KQOAUTHMANAGER_H

View File

@@ -0,0 +1,76 @@
/**
* KQOAuth - An OAuth authentication library for Qt.
*
* Author: Johan Paul (johan.paul@gmail.com)
* http://www.johanpaul.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* KQOAuth 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with KQOAuth. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KQOAUTHMANAGER_P_H
#define KQOAUTHMANAGER_P_H
#include "kqoauthauthreplyserver.h"
#include "kqoauthrequest.h"
class KQOAUTH_EXPORT KQOAuthManagerPrivate {
public:
KQOAuthManagerPrivate(KQOAuthManager *parent);
~KQOAuthManagerPrivate();
QList< QPair<QString, QString> > createQueryParams(const KQOAuthParameters &requestParams);
QMultiMap<QString, QString> createTokensFromResponse(QByteArray reply);
bool setSuccessfulRequestToken(const QMultiMap<QString, QString> &request);
bool setSuccessfulAuthorized(const QMultiMap<QString, QString> &request);
void emitTokens();
bool setupCallbackServer();
KQOAuthManager::KQOAuthError error;
KQOAuthRequest *r; // This request is used to cache the user sent request.
KQOAuthRequest *opaqueRequest; // This request is used to creating opaque convenience requests for the user.
KQOAuthManager * const q_ptr;
/**
* The items below are needed in order to store the state of the manager and
* by that be able to do convenience operations for the user.
*/
KQOAuthRequest::RequestType currentRequestType;
// Variables we store here for opaque request handling.
// NOTE: The variables are labeled the same for both access token request
// and protected resource access.
QString requestToken;
QString requestTokenSecret;
QString consumerKey;
QString consumerKeySecret;
QString requestVerifier;
KQOAuthRequest::RequestSignatureMethod signatureMethod;
KQOAuthAuthReplyServer *callbackServer;
bool hasTemporaryToken;
bool isVerified;
bool isAuthorized;
bool autoAuth;
bool handleAuthPageOpening;
QNetworkAccessManager *networkManager;
bool managerUserSet;
QMap<QNetworkReply*, int> requestIds;
QMap<KQOAuthRequest*, QNetworkReply*> requestMap;
Q_DECLARE_PUBLIC(KQOAuthManager);
};
#endif // KQOAUTHMANAGER_P_H

View File

@@ -0,0 +1,609 @@
/**
* KQOAuth - An OAuth authentication library for Qt.
*
* Author: Johan Paul (johan.paul@gmail.com)
* http://www.johanpaul.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* KQOAuth 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with KQOAuth. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QByteArray>
#include <QDateTime>
#include <QCryptographicHash>
#include <QPair>
#include <QStringList>
#include <QtDebug>
#include <QtAlgorithms>
#include "kqoauthrequest.h"
#include "kqoauthrequest_p.h"
#include "kqoauthutils.h"
#include "kqoauthglobals.h"
//////////// Private d_ptr implementation /////////
KQOAuthRequestPrivate::KQOAuthRequestPrivate() :
timeout(0)
{
}
KQOAuthRequestPrivate::~KQOAuthRequestPrivate()
{
}
// This method will not include the "oauthSignature" paramater, since it is calculated from these parameters.
void KQOAuthRequestPrivate::prepareRequest() {
// If parameter list is not empty, we don't want to insert these values by
// accident a second time. So giving up.
if( !requestParameters.isEmpty() ) {
return;
}
switch ( requestType ) {
case KQOAuthRequest::TemporaryCredentials:
requestParameters.append( qMakePair( OAUTH_KEY_CALLBACK, oauthCallbackUrl.toString()) ); // This is so ugly that it is almost beautiful.
requestParameters.append( qMakePair( OAUTH_KEY_SIGNATURE_METHOD, oauthSignatureMethod) );
requestParameters.append( qMakePair( OAUTH_KEY_CONSUMER_KEY, oauthConsumerKey ));
requestParameters.append( qMakePair( OAUTH_KEY_VERSION, oauthVersion ));
requestParameters.append( qMakePair( OAUTH_KEY_TIMESTAMP, this->oauthTimestamp() ));
requestParameters.append( qMakePair( OAUTH_KEY_NONCE, this->oauthNonce() ));
break;
case KQOAuthRequest::AccessToken:
requestParameters.append( qMakePair( OAUTH_KEY_SIGNATURE_METHOD, oauthSignatureMethod ));
requestParameters.append( qMakePair( OAUTH_KEY_CONSUMER_KEY, oauthConsumerKey ));
requestParameters.append( qMakePair( OAUTH_KEY_VERSION, oauthVersion ));
requestParameters.append( qMakePair( OAUTH_KEY_TIMESTAMP, this->oauthTimestamp() ));
requestParameters.append( qMakePair( OAUTH_KEY_NONCE, this->oauthNonce() ));
requestParameters.append( qMakePair( OAUTH_KEY_VERIFIER, oauthVerifier ));
requestParameters.append( qMakePair( OAUTH_KEY_TOKEN, oauthToken ));
break;
case KQOAuthRequest::AuthorizedRequest:
requestParameters.append( qMakePair( OAUTH_KEY_SIGNATURE_METHOD, oauthSignatureMethod ));
requestParameters.append( qMakePair( OAUTH_KEY_CONSUMER_KEY, oauthConsumerKey ));
requestParameters.append( qMakePair( OAUTH_KEY_VERSION, oauthVersion ));
requestParameters.append( qMakePair( OAUTH_KEY_TIMESTAMP, this->oauthTimestamp() ));
requestParameters.append( qMakePair( OAUTH_KEY_NONCE, this->oauthNonce() ));
requestParameters.append( qMakePair( OAUTH_KEY_TOKEN, oauthToken ));
break;
default:
break;
}
}
void KQOAuthRequestPrivate::signRequest() {
QString signature = this->oauthSignature();
requestParameters.append( qMakePair( OAUTH_KEY_SIGNATURE, signature) );
}
QString KQOAuthRequestPrivate::oauthSignature() {
/**
* http://oauth.net/core/1.0/#anchor16
* The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104] where the
* Signature Base String is the text and the key is the concatenated values (each first encoded per Parameter
* Encoding) of the Consumer Secret and Token Secret, separated by an & character (ASCII code 38) even if empty.
*
* RSA-SHA1 uses RSA to to generate the signature
**/
QByteArray baseString = this->requestBaseString();
QString signature;
if (this->oauthSignatureMethod == "RSA-SHA1") {
signature = KQOAuthUtils::rsa_sha1(baseString, oauthConsumerSecretKey);
} else { // Default: Use HMAC-SHA1
QString secret = QString(QUrl::toPercentEncoding(oauthConsumerSecretKey)) + "&" + QString(QUrl::toPercentEncoding(oauthTokenSecret));
signature = KQOAuthUtils::hmac_sha1(baseString, secret);
}
if (debugOutput) {
qDebug() << "========== KQOAuthRequest has the following signature:";
qDebug() << " * Signature : " << QUrl::toPercentEncoding(signature) << "\n";
}
return QString( QUrl::toPercentEncoding(signature) );
}
bool normalizedParameterSort(const QPair<QString, QString> &left, const QPair<QString, QString> &right) {
QString keyLeft = left.first;
QString valueLeft = left.second;
QString keyRight = right.first;
QString valueRight = right.second;
if(keyLeft == keyRight) {
return (valueLeft < valueRight);
} else {
return (keyLeft < keyRight);
}
}
QByteArray KQOAuthRequestPrivate::requestBaseString() {
QByteArray baseString;
// Every request has these as the commont parameters.
baseString.append( oauthHttpMethodString.toUtf8() + "&"); // HTTP method
baseString.append( QUrl::toPercentEncoding( oauthRequestEndpoint.toString(QUrl::RemoveQuery) ) + "&" ); // The path and query components
QList< QPair<QString, QString> > baseStringParameters;
baseStringParameters.append(requestParameters);
baseStringParameters.append(additionalParameters);
// Sort the request parameters. These parameters have been
// initialized earlier.
qSort(baseStringParameters.begin(),
baseStringParameters.end(),
normalizedParameterSort
);
// Last append the request parameters correctly encoded.
baseString.append( encodedParamaterList(baseStringParameters) );
if (debugOutput) {
qDebug() << "========== KQOAuthRequest has the following base string:";
qDebug() << baseString << "\n";
}
return baseString;
}
QByteArray KQOAuthRequestPrivate::encodedParamaterList(const QList< QPair<QString, QString> > &parameters) {
QByteArray resultList;
bool first = true;
QPair<QString, QString> parameter;
// Do the debug output.
if (debugOutput) {
qDebug() << "========== KQOAuthRequest has the following parameters:";
}
foreach (parameter, parameters) {
if(!first) {
resultList.append( "&" );
} else {
first = false;
}
// Here we don't need to explicitely encode the strings to UTF-8 since
// QUrl::toPercentEncoding() takes care of that for us.
resultList.append( QUrl::toPercentEncoding(parameter.first) // Parameter key
+ "="
+ QUrl::toPercentEncoding(parameter.second) // Parameter value
);
if (debugOutput) {
qDebug() << " * "
<< parameter.first
<< " : "
<< parameter.second;
}
}
if (debugOutput) {
qDebug() << "\n";
}
return QUrl::toPercentEncoding(resultList);
}
QString KQOAuthRequestPrivate::oauthTimestamp() const {
// This is basically for unit tests only. In most cases we don't set the nonce beforehand.
if (!oauthTimestamp_.isEmpty()) {
return oauthTimestamp_;
}
#if QT_VERSION >= 0x040700
return QString::number(QDateTime::currentDateTimeUtc().toTime_t());
#else
return QString::number(QDateTime::currentDateTime().toUTC().toTime_t());
#endif
}
QString KQOAuthRequestPrivate::oauthNonce() const {
// This is basically for unit tests only. In most cases we don't set the nonce beforehand.
if (!oauthNonce_.isEmpty()) {
return oauthNonce_;
}
return QString::number(qrand());
}
bool KQOAuthRequestPrivate::validateRequest() const {
switch ( requestType ) {
case KQOAuthRequest::TemporaryCredentials:
if (oauthRequestEndpoint.isEmpty()
|| oauthConsumerKey.isEmpty()
|| oauthNonce_.isEmpty()
|| oauthSignatureMethod.isEmpty()
|| oauthTimestamp_.isEmpty()
|| oauthVersion.isEmpty())
{
return false;
}
return true;
case KQOAuthRequest::AccessToken:
if (oauthRequestEndpoint.isEmpty()
|| oauthVerifier.isEmpty()
|| oauthConsumerKey.isEmpty()
|| oauthNonce_.isEmpty()
|| oauthSignatureMethod.isEmpty()
|| oauthTimestamp_.isEmpty()
|| oauthToken.isEmpty()
|| oauthTokenSecret.isEmpty()
|| oauthVersion.isEmpty())
{
return false;
}
return true;
case KQOAuthRequest::AuthorizedRequest:
if (oauthRequestEndpoint.isEmpty()
|| oauthConsumerKey.isEmpty()
|| oauthNonce_.isEmpty()
|| oauthSignatureMethod.isEmpty()
|| oauthTimestamp_.isEmpty()
|| oauthToken.isEmpty()
|| oauthTokenSecret.isEmpty()
|| oauthVersion.isEmpty())
{
return false;
}
return true;
default:
return false;
}
// We should not come here.
return false;
}
//////////// Public implementation ////////////////
KQOAuthRequest::KQOAuthRequest(QObject *parent) :
QObject(parent),
d_ptr(new KQOAuthRequestPrivate)
{
Q_D(KQOAuthRequest);
d_ptr->debugOutput = false; // No debug output by default.
connect(&(d->timer), SIGNAL(timeout()), this, SIGNAL(requestTimedout()));
}
KQOAuthRequest::~KQOAuthRequest()
{
delete d_ptr;
}
void KQOAuthRequest::initRequest(KQOAuthRequest::RequestType type, const QUrl &requestEndpoint) {
Q_D(KQOAuthRequest);
if (!requestEndpoint.isValid()) {
qWarning() << "Endpoint URL is not valid. Ignoring. This request might not work.";
return;
}
if (type < 0 || type > KQOAuthRequest::AuthorizedRequest) {
qWarning() << "Invalid request type. Ignoring. This request might not work.";
return;
}
// Clear the request
clearRequest();
// Set smart defaults.
d->requestType = type;
d->oauthRequestEndpoint = requestEndpoint;
d->oauthTimestamp_ = d->oauthTimestamp();
d->oauthNonce_ = d->oauthNonce();
this->setSignatureMethod(KQOAuthRequest::HMAC_SHA1);
this->setHttpMethod(KQOAuthRequest::POST);
d->oauthVersion = "1.0"; // Currently supports only version 1.0
d->contentType = "application/x-www-form-urlencoded";
}
void KQOAuthRequest::setConsumerKey(const QString &consumerKey) {
Q_D(KQOAuthRequest);
d->oauthConsumerKey = consumerKey;
}
void KQOAuthRequest::setConsumerSecretKey(const QString &consumerSecretKey) {
Q_D(KQOAuthRequest);
d->oauthConsumerSecretKey = consumerSecretKey;
}
void KQOAuthRequest::setCallbackUrl(const QUrl &callbackUrl) {
Q_D(KQOAuthRequest);
d->oauthCallbackUrl = callbackUrl;
}
void KQOAuthRequest::setSignatureMethod(KQOAuthRequest::RequestSignatureMethod requestMethod) {
Q_D(KQOAuthRequest);
QString requestMethodString;
switch (requestMethod) {
case KQOAuthRequest::PLAINTEXT:
requestMethodString = "PLAINTEXT";
break;
case KQOAuthRequest::HMAC_SHA1:
requestMethodString = "HMAC-SHA1";
break;
case KQOAuthRequest::RSA_SHA1:
requestMethodString = "RSA-SHA1";
break;
default:
// We should not come here
qWarning() << "Invalid signature method set.";
break;
}
d->oauthSignatureMethod = requestMethodString;
d->requestSignatureMethod = requestMethod;
}
void KQOAuthRequest::setTokenSecret(const QString &tokenSecret) {
Q_D(KQOAuthRequest);
d->oauthTokenSecret = tokenSecret;
}
void KQOAuthRequest::setToken(const QString &token) {
Q_D(KQOAuthRequest);
d->oauthToken = token;
}
void KQOAuthRequest::setVerifier(const QString &verifier) {
Q_D(KQOAuthRequest);
d->oauthVerifier = verifier;
}
void KQOAuthRequest::setHttpMethod(KQOAuthRequest::RequestHttpMethod httpMethod) {
Q_D(KQOAuthRequest);
QString requestHttpMethodString;
switch (httpMethod) {
case KQOAuthRequest::GET:
requestHttpMethodString = "GET";
break;
case KQOAuthRequest::POST:
requestHttpMethodString = "POST";
break;
case KQOAuthRequest::HEAD:
requestHttpMethodString = "HEAD";
break;
case KQOAuthRequest::DELETE:
requestHttpMethodString = "DELETE";
break;
default:
qWarning() << "Invalid HTTP method set.";
break;
}
d->oauthHttpMethod = httpMethod;
d->oauthHttpMethodString = requestHttpMethodString;
}
KQOAuthRequest::RequestHttpMethod KQOAuthRequest::httpMethod() const {
Q_D(const KQOAuthRequest);
return d->oauthHttpMethod;
}
void KQOAuthRequest::setAdditionalParameters(const KQOAuthParameters &additionalParams) {
Q_D(KQOAuthRequest);
QList<QString> additionalKeys = additionalParams.keys();
QList<QString> additionalValues = additionalParams.values();
int i=0;
foreach(QString key, additionalKeys) {
QString value = additionalValues.at(i);
d->additionalParameters.append( qMakePair(key, value) );
i++;
}
}
KQOAuthParameters KQOAuthRequest::additionalParameters() const {
Q_D(const KQOAuthRequest);
QMultiMap<QString, QString> additionalParams;
for(int i=0; i<d->additionalParameters.size(); i++) {
additionalParams.insert(d->additionalParameters.at(i).first,
d->additionalParameters.at(i).second);
}
return additionalParams;
}
KQOAuthRequest::RequestType KQOAuthRequest::requestType() const {
Q_D(const KQOAuthRequest);
return d->requestType;
}
QUrl KQOAuthRequest::requestEndpoint() const {
Q_D(const KQOAuthRequest);
return d->oauthRequestEndpoint;
}
QList<QByteArray> KQOAuthRequest::requestParameters() {
Q_D(KQOAuthRequest);
QList<QByteArray> requestParamList;
d->prepareRequest();
if (!isValid() ) {
qWarning() << "Request is not valid! I will still sign it, but it will probably not work.";
}
d->signRequest();
QPair<QString, QString> requestParam;
QString param;
QString value;
foreach (requestParam, d->requestParameters) {
param = requestParam.first;
value = requestParam.second;
if (param != OAUTH_KEY_SIGNATURE) {
value = QUrl::toPercentEncoding(value);
}
requestParamList.append(QString(param + "=\"" + value +"\"").toUtf8());
}
return requestParamList;
}
QString KQOAuthRequest::contentType()
{
Q_D(const KQOAuthRequest);
return d->contentType;
}
void KQOAuthRequest::setContentType(const QString &contentType)
{
Q_D(KQOAuthRequest);
d->contentType = contentType;
}
QByteArray KQOAuthRequest::rawData()
{
Q_D(const KQOAuthRequest);
return d->postRawData;
}
void KQOAuthRequest::setRawData(const QByteArray &rawData)
{
Q_D(KQOAuthRequest);
d->postRawData = rawData;
}
QByteArray KQOAuthRequest::requestBody() const {
Q_D(const KQOAuthRequest);
QByteArray postBodyContent;
bool first = true;
for(int i=0; i < d->additionalParameters.size(); i++) {
if(!first) {
postBodyContent.append("&");
} else {
first = false;
}
QString key = d->additionalParameters.at(i).first;
QString value = d->additionalParameters.at(i).second;
postBodyContent.append(QUrl::toPercentEncoding(key) + QString("=").toUtf8() +
QUrl::toPercentEncoding(value));
}
return postBodyContent;
}
bool KQOAuthRequest::isValid() const {
Q_D(const KQOAuthRequest);
return d->validateRequest();
}
void KQOAuthRequest::setTimeout(int timeoutMilliseconds) {
Q_D(KQOAuthRequest);
d->timeout = timeoutMilliseconds;
}
void KQOAuthRequest::clearRequest() {
Q_D(KQOAuthRequest);
d->oauthRequestEndpoint = "";
d->oauthHttpMethodString = "";
d->oauthConsumerKey = "";
d->oauthConsumerSecretKey = "";
d->oauthToken = "";
d->oauthTokenSecret = "";
d->oauthSignatureMethod = "";
d->oauthCallbackUrl = "";
d->oauthVerifier = "";
d->oauthTimestamp_ = "";
d->oauthNonce_ = "";
d->requestParameters.clear();
d->additionalParameters.clear();
d->timeout = 0;
}
void KQOAuthRequest::setEnableDebugOutput(bool enabled) {
Q_D(KQOAuthRequest);
d->debugOutput = enabled;
}
/**
* Protected implementations for inherited classes
*/
bool KQOAuthRequest::validateXAuthRequest() const {
Q_D(const KQOAuthRequest);
if (d->oauthRequestEndpoint.isEmpty()
|| d->oauthConsumerKey.isEmpty()
|| d->oauthNonce_.isEmpty()
|| d->oauthSignatureMethod.isEmpty()
|| d->oauthTimestamp_.isEmpty()
|| d->oauthVersion.isEmpty())
{
return false;
}
return true;
}
/**
* Private implementations for friend classes
*/
QString KQOAuthRequest::consumerKeyForManager() const {
Q_D(const KQOAuthRequest);
return d->oauthConsumerKey;
}
QString KQOAuthRequest::consumerKeySecretForManager() const {
Q_D(const KQOAuthRequest);
return d->oauthConsumerSecretKey;
}
KQOAuthRequest::RequestSignatureMethod KQOAuthRequest::requestSignatureMethodForManager() const {
Q_D(const KQOAuthRequest);
return d->requestSignatureMethod;
}
QUrl KQOAuthRequest::callbackUrlForManager() const {
Q_D(const KQOAuthRequest);
return d->oauthCallbackUrl;
}
void KQOAuthRequest::requestTimerStart()
{
Q_D(KQOAuthRequest);
if (d->timeout > 0) {
d->timer.start(d->timeout);
}
}
void KQOAuthRequest::requestTimerStop()
{
Q_D(KQOAuthRequest);
if( d->timer.isActive() )
d->timer.stop();
}

View File

@@ -0,0 +1,148 @@
/**
* KQOAuth - An OAuth authentication library for Qt.
*
* Author: Johan Paul (johan.paul@gmail.com)
* http://www.johanpaul.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* KQOAuth 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with KQOAuth. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KQOAUTHREQUEST_H
#define KQOAUTHREQUEST_H
#include <QObject>
#include <QUrl>
#include <QMultiMap>
#include "kqoauthglobals.h"
typedef QMultiMap<QString, QString> KQOAuthParameters;
class KQOAuthRequestPrivate;
class KQOAUTH_EXPORT KQOAuthRequest : public QObject
{
Q_OBJECT
public:
explicit KQOAuthRequest(QObject *parent = 0);
~KQOAuthRequest();
enum RequestType {
TemporaryCredentials = 0,
AccessToken,
AuthorizedRequest
};
enum RequestSignatureMethod {
PLAINTEXT = 0,
HMAC_SHA1,
RSA_SHA1
};
enum RequestHttpMethod {
GET = 0,
POST,
HEAD,
DELETE
};
/**
* These methods can be overridden in child classes which are different types of
* OAuth requests.
*/
// Validate the request of this type.
virtual bool isValid() const;
/**
* These methods are OAuth request type specific and not overridden in child
* classes.
* NOTE: Refactorting still a TODO
*/
// Initialize the request of this type.
void initRequest(KQOAuthRequest::RequestType type, const QUrl &requestEndpoint);
void setConsumerKey(const QString &consumerKey);
void setConsumerSecretKey(const QString &consumerSecretKey);
// Mandatory methods for acquiring a request token
void setCallbackUrl(const QUrl &callbackUrl);
// Mandator methods for acquiring a access token
void setTokenSecret(const QString &tokenSecret);
void setToken(const QString &token);
void setVerifier(const QString &verifier);
// Request signature method to use - HMAC_SHA1 currently only supported
void setSignatureMethod(KQOAuthRequest::RequestSignatureMethod = KQOAuthRequest::HMAC_SHA1);
// Request's HTTP method.
void setHttpMethod(KQOAuthRequest::RequestHttpMethod = KQOAuthRequest::POST);
KQOAuthRequest::RequestHttpMethod httpMethod() const;
// Sets the timeout for this request. If the timeout expires, the signal "requestTimedout" will be
// emitted. The KQOAuthManager will then call the abort() function from QNetworkReply associated with this request
// 0 = If set to zero, timeout is disabled.
// TODO: Do we need some request ID now?
void setTimeout(int timeoutMilliseconds);
// Additional optional parameters to the request.
void setAdditionalParameters(const KQOAuthParameters &additionalParams);
KQOAuthParameters additionalParameters() const;
QList<QByteArray> requestParameters(); // This will return all request's parameters in the raw format given
// to the QNetworkRequest.
QByteArray requestBody() const; // This will return the POST body as given to the QNetworkRequest.
KQOAuthRequest::RequestType requestType() const;
QUrl requestEndpoint() const;
void setContentType(const QString &contentType);
QString contentType();
void setRawData(const QByteArray &rawData);
QByteArray rawData();
void clearRequest();
// Enable verbose debug output for request content.
void setEnableDebugOutput(bool enabled);
Q_SIGNALS:
// This signal is emited if the request is not completed before the request's timeout
// value has expired.
void requestTimedout();
protected:
bool validateXAuthRequest() const;
private:
KQOAuthRequestPrivate * const d_ptr;
Q_DECLARE_PRIVATE(KQOAuthRequest);
Q_DISABLE_COPY(KQOAuthRequest);
// These classes are only for the internal use of KQOAuthManager so it can
// work with the opaque request.
QString consumerKeyForManager() const;
QString consumerKeySecretForManager() const;
KQOAuthRequest::RequestSignatureMethod requestSignatureMethodForManager() const;
QUrl callbackUrlForManager() const;
// This method is for timeout handling by the KQOAuthManager.
void requestTimerStart();
void requestTimerStop();
friend class KQOAuthManager;
#ifdef UNIT_TEST
friend class Ut_KQOAuth;
#endif
};
#endif // KQOAUTHREQUEST_H

View File

@@ -0,0 +1,25 @@
/**
* KQOAuth - An OAuth authentication library for Qt.
*
* Author: Johan Paul (johan.paul@gmail.com)
* http://www.johanpaul.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* KQOAuth 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with KQOAuth. If not, see <http://www.gnu.org/licenses/>.
*/
#include "kqoauthrequest_1.h"
KQOAuthRequest_1::KQOAuthRequest_1()
{
}

View File

@@ -0,0 +1,32 @@
/**
* KQOAuth - An OAuth authentication library for Qt.
*
* Author: Johan Paul (johan.paul@gmail.com)
* http://www.johanpaul.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* KQOAuth 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with KQOAuth. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KQOAUTHREQUEST_1_H
#define KQOAUTHREQUEST_1_H
#include "kqoauthrequest.h"
class KQOAUTH_EXPORT KQOAuthRequest_1 : public KQOAuthRequest
{
public:
KQOAuthRequest_1();
};
#endif // KQOAUTHREQUEST_1_H

View File

@@ -0,0 +1,94 @@
/**
* KQOAuth - An OAuth authentication library for Qt.
*
* Author: Johan Paul (johan.paul@gmail.com)
* http://www.johanpaul.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* KQOAuth 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with KQOAuth. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KQOAUTHREQUEST_P_H
#define KQOAUTHREQUEST_P_H
#include "kqoauthglobals.h"
#include "kqoauthrequest.h"
#include <QString>
#include <QUrl>
#include <QMap>
#include <QPair>
#include <QMultiMap>
#include <QTimer>
class KQOAUTH_EXPORT KQOAuthRequestPrivate {
public:
KQOAuthRequestPrivate();
~KQOAuthRequestPrivate();
// Helper methods to get the values for the OAuth request parameters.
QString oauthTimestamp() const;
QString oauthNonce() const;
QString oauthSignature();
// Utility methods for making the request happen.
void prepareRequest();
void signRequest();
bool validateRequest() const;
QByteArray requestBaseString();
QByteArray encodedParamaterList(const QList< QPair<QString, QString> > &requestParameters);
void insertAdditionalParams();
void insertPostBody();
QUrl oauthRequestEndpoint;
KQOAuthRequest::RequestHttpMethod oauthHttpMethod;
QString oauthHttpMethodString;
QString oauthConsumerKey;
QString oauthConsumerSecretKey;
QString oauthToken;
QString oauthTokenSecret;
QString oauthSignatureMethod;
KQOAuthRequest::RequestSignatureMethod requestSignatureMethod;
QUrl oauthCallbackUrl;
QString oauthVersion;
QString oauthVerifier;
// These will be generated by the helper methods
QString oauthTimestamp_;
QString oauthNonce_;
// User specified additional parameters needed for the request.
QList< QPair<QString, QString> > additionalParameters;
// The raw POST body content as given to the HTTP request.
QByteArray postBodyContent;
// Protocol parameters.
// These parameters are used in the "Authorized" header of the HTTP request.
QList< QPair<QString, QString> > requestParameters;
KQOAuthRequest::RequestType requestType;
//The Content-Type HTTP header
QString contentType;
//Raw data to post if type is not url-encoded
QByteArray postRawData;
// Timeout for this request in milliseconds.
int timeout;
QTimer timer;
bool debugOutput;
};
#endif // KQOAUTHREQUEST_P_H

View File

@@ -0,0 +1,89 @@
/**
* KQOAuth - An OAuth authentication library for Qt.
*
* Author: Johan Paul (johan.paul@gmail.com)
* http://www.johanpaul.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* KQOAuth 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with KQOAuth. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QtDebug>
#include "kqoauthrequest_xauth_p.h"
#include "kqoauthrequest_xauth.h"
/**
* Private d_ptr implementations.
*/
KQOAuthRequest_XAuthPrivate::KQOAuthRequest_XAuthPrivate()
{
}
KQOAuthRequest_XAuthPrivate::~KQOAuthRequest_XAuthPrivate()
{
}
/**
* Public implementations.
*/
KQOAuthRequest_XAuth::KQOAuthRequest_XAuth(QObject *parent) :
KQOAuthRequest(parent),
d_ptr(new KQOAuthRequest_XAuthPrivate)
{
}
bool KQOAuthRequest_XAuth::isValid() const {
// An xAuth can never request temporary credentials.
if (requestType() == KQOAuthRequest::TemporaryCredentials) {
qWarning() << "XAuth request cannot be of type KQOAuthRequest::TemporaryCredentials. Aborting.";
return false;
}
// Access token must always be retrieved using the POST HTTP method.
if (requestType() == KQOAuthRequest::AccessToken
&& httpMethod() != KQOAuthRequest::POST) {
qWarning() << "Access tokens must be fetched using the POST HTTP method. Aborting.";
return false;
}
if (!xauth_parameters_set) {
qWarning() << "No XAuth parameters set. Aborting.";
return false;
}
// And then check the validity of the XAuth request.
// Provided by the base class as a protected method for us.
return validateXAuthRequest();
}
void KQOAuthRequest_XAuth::setXAuthLogin(const QString &username,
const QString &password) {
if (username.isEmpty() || password.isEmpty()) {
qWarning() << "Username or password cannot be empty. Aborting.";
return;
}
xauth_parameters_set = true;
KQOAuthParameters xauthParams;
xauthParams.insert("x_auth_username", username);
xauthParams.insert("x_auth_password", password);
xauthParams.insert("x_auth_mode", "client_auth");
setAdditionalParameters(xauthParams);
}

View File

@@ -0,0 +1,49 @@
/**
* KQOAuth - An OAuth authentication library for Qt.
*
* Author: Johan Paul (johan.paul@gmail.com)
* http://www.johanpaul.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* KQOAuth 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with KQOAuth. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KQOAUTHREQUEST_XAUTH_H
#define KQOAUTHREQUEST_XAUTH_H
#include "kqoauthrequest.h"
#include "kqoauthrequest_1.h"
class KQOAuthRequest_XAuthPrivate;
class KQOAUTH_EXPORT KQOAuthRequest_XAuth : public KQOAuthRequest
{
Q_OBJECT
public:
KQOAuthRequest_XAuth(QObject *parent = 0);
/**
* These methods can be overridden in child classes which are different types of
* OAuth requests.
*/
// Validate the request of this type.
bool isValid() const;
// Give the xAuth specific parameters.
void setXAuthLogin(const QString &username = "",
const QString &password = "");
private:
KQOAuthRequest_XAuthPrivate * const d_ptr;
bool xauth_parameters_set;
};
#endif // KQOAUTHREQUEST_XAUTH_H

View File

@@ -0,0 +1,33 @@
/**
* KQOAuth - An OAuth authentication library for Qt.
*
* Author: Johan Paul (johan.paul@gmail.com)
* http://www.johanpaul.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* KQOAuth 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with KQOAuth. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KQOAUTHREQUEST_XAUTH_P_H
#define KQOAUTHREQUEST_XAUTH_P_H
#include "kqoauthglobals.h"
class KQOAuthRequest;
class KQOAUTH_EXPORT KQOAuthRequest_XAuthPrivate
{
public:
KQOAuthRequest_XAuthPrivate();
~KQOAuthRequest_XAuthPrivate();
};
#endif // KQOAUTHREQUEST_XAUTH_P_H

View File

@@ -0,0 +1,142 @@
/**
* KQOAuth - An OAuth authentication library for Qt.
*
* Author: Johan Paul (johan.paul@gmail.com)
* http://www.johanpaul.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* KQOAuth 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with KQOAuth. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QString>
#include <QCryptographicHash>
#include <QByteArray>
#include <QtDebug>
#include "kqoauthutils.h"
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <openssl/evp.h>
QString KQOAuthUtils::hmac_sha1(const QString &message, const QString &key)
{
QByteArray keyBytes = key.toLatin1();
int keyLength; // Lenght of key word
const int blockSize = 64; // Both MD5 and SHA-1 have a block size of 64.
keyLength = keyBytes.size();
// If key is longer than block size, we need to hash the key
if (keyLength > blockSize) {
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(keyBytes);
keyBytes = hash.result();
}
/* http://tools.ietf.org/html/rfc2104 - (1) */
// Create the opad and ipad for the hash function.
QByteArray ipad;
QByteArray opad;
ipad.fill( 0, blockSize);
opad.fill( 0, blockSize);
ipad.replace(0, keyBytes.length(), keyBytes);
opad.replace(0, keyBytes.length(), keyBytes);
/* http://tools.ietf.org/html/rfc2104 - (2) & (5) */
for (int i=0; i<64; i++) {
ipad[i] = ipad[i] ^ 0x36;
opad[i] = opad[i] ^ 0x5c;
}
QByteArray workArray;
workArray.clear();
workArray.append(ipad, 64);
/* http://tools.ietf.org/html/rfc2104 - (3) */
workArray.append(message.toLatin1());
/* http://tools.ietf.org/html/rfc2104 - (4) */
QByteArray sha1 = QCryptographicHash::hash(workArray, QCryptographicHash::Sha1);
/* http://tools.ietf.org/html/rfc2104 - (6) */
workArray.clear();
workArray.append(opad, 64);
workArray.append(sha1);
sha1.clear();
/* http://tools.ietf.org/html/rfc2104 - (7) */
sha1 = QCryptographicHash::hash(workArray, QCryptographicHash::Sha1);
return QString(sha1.toBase64());
}
QString KQOAuthUtils::rsa_sha1(const QString &message, const QString &key)
{
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
OpenSSL_add_all_ciphers();
OpenSSL_add_all_digests();
EVP_PKEY *evpKey = 0;
evpKey = EVP_PKEY_new();
RSA *rsa = 0;
rsa = getRsaFromKey(key);
EVP_PKEY_set1_RSA(evpKey, rsa);
EVP_MD_CTX* ctx = 0;
ctx = EVP_MD_CTX_create();
EVP_SignInit_ex(ctx, EVP_sha1(), 0);
QByteArray latinMessage = message.toLatin1();
EVP_SignUpdate(ctx, latinMessage.data(), strlen(latinMessage.data()));
const int MAX_LEN = 1024;
unsigned char *sig = new unsigned char[MAX_LEN];
unsigned int sigLen;
EVP_SignFinal(ctx, sig, &sigLen, evpKey);
sig[sigLen] = '\0';
EVP_MD_CTX_destroy(ctx);
RSA_free(rsa);
EVP_PKEY_free(evpKey);
ERR_free_strings();
QByteArray ret((char *)sig, sigLen);
return QString(ret.toBase64());
}
RSA* KQOAuthUtils::getRsaFromKey(const QString &key)
{
BIO *bufio;
QByteArray data = key.toLocal8Bit();
char *pem_key_buffer = data.data();
int pem_key_buffer_len = strlen(pem_key_buffer);
bufio = BIO_new_mem_buf((void*)pem_key_buffer, pem_key_buffer_len);
RSA *rsa = 0;
rsa = RSA_new();
rsa = PEM_read_bio_RSAPrivateKey(bufio, 0, 0, NULL);
if (rsa == 0) {
printf("Can not parse RSA data. Errors:\n");
ERR_print_errors_fp(stderr);
exit(1);
}
return rsa;
}

View File

@@ -0,0 +1,39 @@
/**
* KQOAuth - An OAuth authentication library for Qt.
*
* Author: Johan Paul (johan.paul@gmail.com)
* http://www.johanpaul.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* KQOAuth 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with KQOAuth. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KQOAUTHUTILS_H
#define KQOAUTHUTILS_H
#include "kqoauthglobals.h"
#include <openssl/rsa.h>
class QString;
class KQOAUTH_EXPORT KQOAuthUtils
{
public:
static QString hmac_sha1(const QString &message, const QString &key);
static QString rsa_sha1(const QString &message, const QString &key);
private:
static RSA* getRsaFromKey(const QString &key);
};
#endif // KQOAUTHUTILS_H

15
deprecated/kqoauth/pcfile.sh Executable file
View File

@@ -0,0 +1,15 @@
#!/bin/sh
# This "script" creates a pkg-config file basing on values set
# in project file
echo "prefix=$1
exec_prefix=\${prefix}
libdir=\${prefix}/lib
includedir=\${prefix}/include/QtKOAuth
Name: KQOAuth
Description: Qt OAuth support library
Version: $2
Requires: QtCore QtNetwork
Libs: -L\${libdir} -lkqoauth
Cflags: -I\${includedir}" > kqoauth.pc