diff options
Diffstat (limited to 'src')
25 files changed, 3989 insertions, 0 deletions
diff --git a/src/oauth/oauth.pro b/src/oauth/oauth.pro new file mode 100644 index 0000000..3060605 --- /dev/null +++ b/src/oauth/oauth.pro @@ -0,0 +1,37 @@ +TARGET = QtNetworkAuth +MODULE = networkauth + +QT += core core-private network + +PUBLIC_HEADERS += \ + qoauth1.h \ + qoauthglobal.h \ + qabstractoauth.h \ + qabstractoauth2.h \ + qoauth1signature.h \ + qoauthoobreplyhandler.h \ + qabstractoauthreplyhandler.h \ + qoauth2authorizationcodeflow.h \ + qoauthhttpserverreplyhandler.h + +PRIVATE_HEADERS += \ + qoauth1_p.h \ + qabstractoauth_p.h \ + qabstractoauth2_p.h \ + qoauth1signature_p.h \ + qoauth2authorizationcodeflow_p.h \ + qoauthhttpserverreplyhandler_p.h + +SOURCES += \ + qoauth1.cpp \ + qabstractoauth.cpp \ + qabstractoauth2.cpp \ + qoauth1signature.cpp \ + qoauthoobreplyhandler.cpp \ + qabstractoauthreplyhandler.cpp \ + qoauth2authorizationcodeflow.cpp \ + qoauthhttpserverreplyhandler.cpp + +HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS + +load(qt_module) diff --git a/src/oauth/qabstractoauth.cpp b/src/oauth/qabstractoauth.cpp new file mode 100644 index 0000000..b970d80 --- /dev/null +++ b/src/oauth/qabstractoauth.cpp @@ -0,0 +1,222 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT_NO_HTTP + +#include <qabstractoauth.h> +#include <qabstractoauthreplyhandler.h> + +#include <private/qabstractoauth_p.h> + +#include <QtCore/qurl.h> +#include <QtCore/qpair.h> +#include <QtCore/qstring.h> +#include <QtCore/qdatetime.h> +#include <QtCore/qurlquery.h> +#include <QtCore/qmessageauthenticationcode.h> + +#include <QtNetwork/qnetworkrequest.h> +#include <QtNetwork/qnetworkaccessmanager.h> + +#include <random> + +Q_DECLARE_METATYPE(QAbstractOAuth::Error) + +QT_BEGIN_NAMESPACE + +QAbstractOAuthPrivate::QAbstractOAuthPrivate(QNetworkAccessManager *manager) : + QAbstractOAuthPrivate(QUrl(), manager) +{} + +QAbstractOAuthPrivate::QAbstractOAuthPrivate(const QUrl &authorizationUrl, + QNetworkAccessManager *manager) : + authorizationUrl(authorizationUrl), defaultReplyHandler(new QOAuthOobReplyHandler), + networkAccessManagerPointer(manager) +{} + +QAbstractOAuthPrivate::~QAbstractOAuthPrivate() +{} + +QNetworkAccessManager *QAbstractOAuthPrivate::networkAccessManager() +{ + Q_Q(QAbstractOAuth); + if (!networkAccessManagerPointer) + networkAccessManagerPointer = new QNetworkAccessManager(q); + return networkAccessManagerPointer.data(); +} + +void QAbstractOAuthPrivate::setStatus(QAbstractOAuth::Status newStatus) +{ + Q_Q(QAbstractOAuth); + if (status != newStatus) { + status = newStatus; + Q_EMIT q->statusChanged(status); + if (status == QAbstractOAuth::Status::Granted) + Q_EMIT q->granted(); + } +} + +QByteArray QAbstractOAuthPrivate::generateRandomString(quint8 length) +{ + const char characters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + std::mt19937 randomEngine; + std::uniform_int_distribution<int> distribution(0, sizeof(characters) - 2); + QByteArray data; + data.reserve(length); + for (quint8 i = 0; i < length; ++i) + data.append(characters[distribution(randomEngine)]); + return data; +} + +QUrlQuery QAbstractOAuthPrivate::createQuery(const QVariantMap ¶meters) +{ + QUrlQuery query; + for (auto it = parameters.begin(), end = parameters.end(); it != end; ++it) + query.addQueryItem(it.key(), it.value().toString()); + return query; +} + +QAbstractOAuth::QAbstractOAuth(QAbstractOAuthPrivate &dd, QObject *parent) + : QObject(dd, parent) +{ + qRegisterMetaType<QAbstractOAuth::Error>(); +} + +QAbstractOAuth::~QAbstractOAuth() +{} + +QNetworkAccessManager *QAbstractOAuth::networkAccessManager() const +{ + Q_D(const QAbstractOAuth); + return d->networkAccessManagerPointer.data(); +} + +void QAbstractOAuth::setNetworkAccessManager(QNetworkAccessManager *networkAccessManager) +{ + Q_D(QAbstractOAuth); + if (networkAccessManager != d->networkAccessManagerPointer) { + if (d->networkAccessManagerPointer && d->networkAccessManagerPointer->parent() == this) + delete d->networkAccessManagerPointer.data(); + d->networkAccessManagerPointer = networkAccessManager; + } +} + +QAbstractOAuth::Status QAbstractOAuth::status() const +{ + Q_D(const QAbstractOAuth); + return d->status; +} + +QUrl QAbstractOAuth::authorizationUrl() const +{ + Q_D(const QAbstractOAuth); + return d->authorizationUrl; +} + +void QAbstractOAuth::setAuthorizationUrl(const QUrl &url) +{ + Q_D(QAbstractOAuth); + if (d->authorizationUrl != url) { + d->authorizationUrl = url; + Q_EMIT authorizationUrlChanged(url); + } +} + +void QAbstractOAuth::setStatus(QAbstractOAuth::Status status) +{ + Q_D(QAbstractOAuth); + if (status != d->status) { + d->status = status; + Q_EMIT statusChanged(status); + } +} + +QAbstractOAuthReplyHandler *QAbstractOAuth::replyHandler() const +{ + Q_D(const QAbstractOAuth); + return d->replyHandler ? d->replyHandler.data() : d->defaultReplyHandler.data(); +} + +void QAbstractOAuth::setReplyHandler(QAbstractOAuthReplyHandler *handler) +{ + Q_D(QAbstractOAuth); + d->replyHandler = handler; +} + +QAbstractOAuth::ModifyParametersFunction QAbstractOAuth::modifyParametersFunction() const +{ + Q_D(const QAbstractOAuth); + return d->modifyParametersFunction; +} + +void QAbstractOAuth::setModifyParametersFunction( + const QAbstractOAuth::ModifyParametersFunction &modifyParametersFunction) +{ + Q_D(QAbstractOAuth); + d->modifyParametersFunction = modifyParametersFunction; +} + +QVariantMap QAbstractOAuth::extraTokens() const +{ + Q_D(const QAbstractOAuth); + return d->extraTokens; +} + +QString QAbstractOAuth::callback() const +{ + Q_D(const QAbstractOAuth); + return d->replyHandler ? d->replyHandler->callback() + : d->defaultReplyHandler->callback(); +} + +void QAbstractOAuth::resourceOwnerAuthorization(const QUrl &url, const QVariantMap ¶meters) +{ + QUrl u = url; + u.setQuery(QAbstractOAuthPrivate::createQuery(parameters)); + Q_EMIT authorizeWithBrowser(u); +} + +QByteArray QAbstractOAuth::generateRandomString(quint8 length) +{ + return QAbstractOAuthPrivate::generateRandomString(length); +} + +QT_END_NAMESPACE + +#endif // QT_NO_HTTP diff --git a/src/oauth/qabstractoauth.h b/src/oauth/qabstractoauth.h new file mode 100644 index 0000000..d9c5aeb --- /dev/null +++ b/src/oauth/qabstractoauth.h @@ -0,0 +1,172 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QABSTRACTOAUTH_H +#define QABSTRACTOAUTH_H + +#ifndef QT_NO_HTTP + +#include <QtNetworkAuth/qoauthglobal.h> + +#include <QtCore/qurl.h> +#include <QtCore/qobject.h> +#include <QtCore/qstring.h> +#include <QtCore/qvariant.h> + +#include <functional> + +QT_BEGIN_NAMESPACE + +class QString; +class QByteArray; +class QNetworkReply; +class QNetworkRequest; +class QNetworkAccessManager; +class QAbstractOAuthReplyHandler; + +class QAbstractOAuthPrivate; +class Q_OAUTH_EXPORT QAbstractOAuth : public QObject +{ + Q_OBJECT + + Q_ENUMS(Status) + Q_ENUMS(Stage) + Q_ENUMS(Error) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(QVariantMap extraTokens READ extraTokens NOTIFY extraTokensChanged) + Q_PROPERTY(QUrl authorizationUrl + READ authorizationUrl + WRITE setAuthorizationUrl + NOTIFY authorizationUrlChanged) + +public: + enum class Status { + NotAuthenticated, + TemporaryCredentialsReceived, + Granted, + RefreshingToken + }; + + enum class Stage { + RequestingTemporaryCredentials, + RequestingAuthorization, + RequestingAccessToken + }; + + enum class Error { + NoError, + NetworkError, + ServerError, + + OAuthTokenNotFoundError, + OAuthTokenSecretNotFoundError, + OAuthCallbackNotVerified + }; + + typedef std::function<void(Stage, QVariantMap*)> ModifyParametersFunction; + + virtual ~QAbstractOAuth(); + + virtual QString clientIdentifier() const = 0; + virtual void setClientIdentifier(const QString &clientIdentifier) = 0; + + virtual QString token() const = 0; + virtual void setToken(const QString &token) = 0; + + QNetworkAccessManager *networkAccessManager() const; + void setNetworkAccessManager(QNetworkAccessManager *networkAccessManager); + + Status status() const; + + QUrl authorizationUrl() const; + void setAuthorizationUrl(const QUrl &url); + + QVariantMap extraTokens() const; + + QAbstractOAuthReplyHandler *replyHandler() const; + void setReplyHandler(QAbstractOAuthReplyHandler *handler); + + Q_INVOKABLE virtual QNetworkReply *head(const QUrl &url, + const QVariantMap ¶meters = QVariantMap()) = 0; + Q_INVOKABLE virtual QNetworkReply *get(const QUrl &url, + const QVariantMap ¶meters = QVariantMap()) = 0; + Q_INVOKABLE virtual QNetworkReply *post(const QUrl &url, + const QVariantMap ¶meters = QVariantMap()) = 0; + Q_INVOKABLE virtual QNetworkReply *deleteResource( + const QUrl &url, const QVariantMap ¶meters = QVariantMap()) = 0; + + ModifyParametersFunction modifyParametersFunction() const; + void setModifyParametersFunction(const ModifyParametersFunction &modifyParametersFunction); + +public Q_SLOTS: + virtual void grant() = 0; + +Q_SIGNALS: + void clientIdentifierChanged(const QString &clientIdentifier); + void tokenChanged(const QString &token); + void statusChanged(Status status); + void authorizationUrlChanged(const QUrl &url); + void extraTokensChanged(const QVariantMap &tokens); + + void requestFailed(const Error error); + void authorizeWithBrowser(const QUrl &url); + void granted(); + void finished(QNetworkReply *reply); + void replyDataReceived(const QByteArray &data); + +protected: + explicit QAbstractOAuth(QAbstractOAuthPrivate &, QObject *parent = nullptr); + + void setStatus(Status status); + + QString callback() const; + + virtual void resourceOwnerAuthorization(const QUrl &url, const QVariantMap ¶meters); + static QByteArray generateRandomString(quint8 length); + +private: + Q_DISABLE_COPY(QAbstractOAuth) + Q_DECLARE_PRIVATE(QAbstractOAuth) +}; + +QT_END_NAMESPACE + +#endif // QT_NO_HTTP + +#endif // QABSTRACTOAUTH_H diff --git a/src/oauth/qabstractoauth2.cpp b/src/oauth/qabstractoauth2.cpp new file mode 100644 index 0000000..5808b97 --- /dev/null +++ b/src/oauth/qabstractoauth2.cpp @@ -0,0 +1,273 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT_NO_HTTP + +#include <qabstractoauth2.h> +#include <private/qabstractoauth2_p.h> + +#include <QtCore/qurl.h> +#include <QtCore/qurlquery.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qmessageauthenticationcode.h> + +#include <QtNetwork/qnetworkreply.h> +#include <QtNetwork/qnetworkrequest.h> +#include <QtNetwork/qnetworkaccessmanager.h> + +QT_BEGIN_NAMESPACE + +using Key = QAbstractOAuth2Private::OAuth2KeyString; +const QString Key::accessToken = QStringLiteral("access_token"); +const QString Key::apiKey = QStringLiteral("api_key"); +const QString Key::clientIdentifier = QStringLiteral("client_id"); +const QString Key::clientSharedSecret = QStringLiteral("client_secret"); +const QString Key::code = QStringLiteral("code"); +const QString Key::error = QStringLiteral("error"); +const QString Key::errorDescription = QStringLiteral("error_description"); +const QString Key::errorUri = QStringLiteral("error_uri"); +const QString Key::expiresIn = QStringLiteral("expires_in"); +const QString Key::grantType = QStringLiteral("grant_type"); +const QString Key::redirectUri = QStringLiteral("redirect_uri"); +const QString Key::refreshToken = QStringLiteral("refresh_token"); +const QString Key::responseType = QStringLiteral("response_type"); +const QString Key::scope = QStringLiteral("scope"); +const QString Key::state = QStringLiteral("state"); +const QString Key::tokenType = QStringLiteral("token_type"); + +QAbstractOAuth2Private::QAbstractOAuth2Private(const QPair<QString, QString> &clientCredentials, + const QUrl &authorizationUrl, + QNetworkAccessManager *manager) : + QAbstractOAuthPrivate(authorizationUrl, manager), clientCredentials(clientCredentials) +{} + +QAbstractOAuth2Private::QAbstractOAuth2Private(QNetworkAccessManager *manager) : + QAbstractOAuthPrivate(authorizationUrl, manager) +{} + +QAbstractOAuth2Private::~QAbstractOAuth2Private() +{} + +QString QAbstractOAuth2Private::generateRandomState() +{ + return QString::fromUtf8(QAbstractOAuthPrivate::generateRandomString(8)); +} + +QNetworkRequest QAbstractOAuth2Private::createRequest(const QUrl &url, const QVariantMap ¶meters) +{ + QUrlQuery query(url.query()); + + for (auto it = parameters.begin(), end = parameters.end(); it != end; ++it) + query.addQueryItem(it.key(), it.value().toString()); + + QUrl u(url); + u.setQuery(query); + + QNetworkRequest request(u); + request.setHeader(QNetworkRequest::UserAgentHeader, userAgent); + const QString bearer = bearerFormat.arg(token); + request.setRawHeader("Authorization", bearer.toUtf8()); + return request; +} + +QAbstractOAuth2::QAbstractOAuth2(QObject *parent) : + QAbstractOAuth2(nullptr, parent) +{} + +QAbstractOAuth2::QAbstractOAuth2(QNetworkAccessManager *manager, QObject *parent) : + QAbstractOAuth(*new QAbstractOAuth2Private(manager), parent) +{} + +QAbstractOAuth2::QAbstractOAuth2(QAbstractOAuth2Private &dd, QObject *parent) : + QAbstractOAuth(dd, parent) +{} + +QAbstractOAuth2::~QAbstractOAuth2() +{} + +QUrl QAbstractOAuth2::createAuthenticatedUrl(const QUrl &url, const QVariantMap ¶meters) +{ + Q_D(const QAbstractOAuth2); + if (Q_UNLIKELY(d->token.isEmpty())) { + qWarning("QAbstractOAuth2::createAuthenticatedUrl: Empty access token"); + return QUrl(); + } + QUrl ret = url; + QUrlQuery query(ret.query()); + query.addQueryItem(Key::accessToken, d->token); + for (auto it = parameters.begin(), end = parameters.end(); it != end ;++it) + query.addQueryItem(it.key(), it.value().toString()); + ret.setQuery(query); + return ret; +} + +QNetworkReply *QAbstractOAuth2::head(const QUrl &url, const QVariantMap ¶meters) +{ + Q_D(QAbstractOAuth2); + QNetworkReply *reply = d->networkAccessManager()->head(d->createRequest(url, parameters)); + connect(reply, &QNetworkReply::finished, std::bind(&QAbstractOAuth::finished, this, reply)); + return reply; +} + +QNetworkReply *QAbstractOAuth2::get(const QUrl &url, const QVariantMap ¶meters) +{ + Q_D(QAbstractOAuth2); + QNetworkReply *reply = d->networkAccessManager()->get( + d->createRequest(url, parameters)); + connect(reply, &QNetworkReply::finished, std::bind(&QAbstractOAuth::finished, this, reply)); + return reply; +} + +QNetworkReply *QAbstractOAuth2::post(const QUrl &url, const QVariantMap ¶meters) +{ + Q_D(QAbstractOAuth2); + QNetworkReply *reply = d->networkAccessManager()->post( + d->createRequest(url, parameters), QByteArray()); + connect(reply, &QNetworkReply::finished, std::bind(&QAbstractOAuth::finished, this, reply)); + return reply; +} + +QNetworkReply *QAbstractOAuth2::deleteResource(const QUrl &url, const QVariantMap ¶meters) +{ + Q_D(QAbstractOAuth2); + QNetworkReply *reply = d->networkAccessManager()->deleteResource( + d->createRequest(url, parameters)); + connect(reply, &QNetworkReply::finished, std::bind(&QAbstractOAuth::finished, this, reply)); + return reply; +} + +QString QAbstractOAuth2::scope() const +{ + Q_D(const QAbstractOAuth2); + return d->scope; +} + +void QAbstractOAuth2::setScope(const QString &scope) +{ + Q_D(QAbstractOAuth2); + if (d->scope != scope) { + d->scope = scope; + Q_EMIT scopeChanged(scope); + } +} + +QString QAbstractOAuth2::userAgent() const +{ + Q_D(const QAbstractOAuth2); + return d->userAgent; +} + +void QAbstractOAuth2::setUserAgent(const QString &userAgent) +{ + Q_D(QAbstractOAuth2); + if (d->userAgent != userAgent) { + d->userAgent = userAgent; + Q_EMIT userAgentChanged(userAgent); + } +} + +QString QAbstractOAuth2::clientIdentifier() const +{ + Q_D(const QAbstractOAuth2); + return d->clientCredentials.first; +} + +void QAbstractOAuth2::setClientIdentifier(const QString &clientIdentifier) +{ + Q_D(QAbstractOAuth2); + if (d->clientCredentials.first != clientIdentifier) { + d->clientCredentials.first = clientIdentifier; + Q_EMIT clientIdentifierChanged(clientIdentifier); + } +} + +QString QAbstractOAuth2::clientIdentifierSharedKey() const +{ + Q_D(const QAbstractOAuth2); + return d->clientCredentials.second; +} + +void QAbstractOAuth2::setClientIdentifierSharedKey(const QString &clientIdentifierSharedKey) +{ + Q_D(QAbstractOAuth2); + if (d->clientCredentials.second != clientIdentifierSharedKey) { + d->clientCredentials.second = clientIdentifierSharedKey; + Q_EMIT clientIdentifierSharedKeyChanged(clientIdentifierSharedKey); + } +} + +QString QAbstractOAuth2::token() const +{ + Q_D(const QAbstractOAuth2); + return d->token; +} + +void QAbstractOAuth2::setToken(const QString &token) +{ + Q_D(QAbstractOAuth2); + if (d->token != token) { + d->token = token; + Q_EMIT tokenChanged(token); + } +} + +QString QAbstractOAuth2::state() const +{ + Q_D(const QAbstractOAuth2); + return d->state; +} + +void QAbstractOAuth2::setState(const QString &state) +{ + Q_D(QAbstractOAuth2); + if (state != d->state) { + d->state = state; + Q_EMIT stateChanged(state); + } +} + +QDateTime QAbstractOAuth2::expirationAt() const +{ + Q_D(const QAbstractOAuth2); + return d->expiresAt; +} + +QT_END_NAMESPACE + +#endif // QT_NO_HTTP diff --git a/src/oauth/qabstractoauth2.h b/src/oauth/qabstractoauth2.h new file mode 100644 index 0000000..06f9b3d --- /dev/null +++ b/src/oauth/qabstractoauth2.h @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QABSTRACTOAUTH2_H +#define QABSTRACTOAUTH2_H + +#ifndef QT_NO_HTTP + +#include <QtCore/qdatetime.h> + +#include <QtNetworkAuth/qoauthglobal.h> +#include <QtNetworkAuth/qabstractoauth.h> + +QT_BEGIN_NAMESPACE + +class QAbstractOAuth2Private; +class Q_OAUTH_EXPORT QAbstractOAuth2 : public QAbstractOAuth +{ + Q_OBJECT + Q_PROPERTY(QString scope READ scope WRITE setScope NOTIFY scopeChanged) + Q_PROPERTY(QString userAgent READ userAgent WRITE setUserAgent NOTIFY userAgentChanged) + Q_PROPERTY(QString clientIdentifier + READ clientIdentifier + WRITE setClientIdentifier + NOTIFY clientIdentifierChanged) + Q_PROPERTY(QString clientIdentifierSharedKey + READ clientIdentifierSharedKey + WRITE setClientIdentifierSharedKey + NOTIFY clientIdentifierSharedKeyChanged) + Q_PROPERTY(QString state READ state WRITE setState NOTIFY stateChanged) + Q_PROPERTY(QDateTime expiration READ expirationAt NOTIFY expirationAtChanged) + +public: + explicit QAbstractOAuth2(QObject *parent = nullptr); + explicit QAbstractOAuth2(QNetworkAccessManager *manager, QObject *parent = nullptr); + ~QAbstractOAuth2(); + + Q_INVOKABLE virtual QUrl createAuthenticatedUrl(const QUrl &url, + const QVariantMap ¶meters = QVariantMap()); + Q_INVOKABLE virtual QNetworkReply *head(const QUrl &url, + const QVariantMap ¶meters = QVariantMap()) override; + Q_INVOKABLE virtual QNetworkReply *get(const QUrl &url, + const QVariantMap ¶meters = QVariantMap()) override; + Q_INVOKABLE virtual QNetworkReply *post(const QUrl &url, + const QVariantMap ¶meters = QVariantMap()) override; + Q_INVOKABLE virtual QNetworkReply *deleteResource(const QUrl &url, + const QVariantMap ¶meters = QVariantMap()) override; + + QString scope() const; + void setScope(const QString &scope); + + QString userAgent() const; + void setUserAgent(const QString &userAgent); + + virtual QString responseType() const = 0; + + QString clientIdentifier() const override; + void setClientIdentifier(const QString &clientIdentifier) override; + QString clientIdentifierSharedKey() const; + void setClientIdentifierSharedKey(const QString &clientIdentifierSharedKey); + + QString token() const override; + void setToken(const QString &token) override; + + QString state() const; + void setState(const QString &state); + + QDateTime expirationAt() const; + +Q_SIGNALS: + void scopeChanged(const QString &scope); + void userAgentChanged(const QString &userAgent); + void clientIdentifierChanged(const QString &clientIdentifier); + void clientIdentifierSharedKeyChanged(const QString &clientIdentifierSharedKey); + void stateChanged(const QString &state); + void expirationAtChanged(const QDateTime &expiration); + + void error(const QString &error, const QString &errorDescription, const QUrl &uri); + void authorizationCallbackReceived(const QVariantMap &data); + +protected: + explicit QAbstractOAuth2(QAbstractOAuth2Private &, QObject *parent = nullptr); + +private: + Q_DECLARE_PRIVATE(QAbstractOAuth2) +}; + +QT_END_NAMESPACE + +#endif // QT_NO_HTTP + +#endif // QABSTRACTOAUTH2_H diff --git a/src/oauth/qabstractoauth2_p.h b/src/oauth/qabstractoauth2_p.h new file mode 100644 index 0000000..c9a8ec1 --- /dev/null +++ b/src/oauth/qabstractoauth2_p.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the Network Access API. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QABSTRACTOAUTH2_P_H +#define QABSTRACTOAUTH2_P_H + +#ifndef QT_NO_HTTP + +#include <private/qabstractoauth_p.h> + +#include <QtNetworkAuth/qoauthglobal.h> +#include <QtNetworkAuth/qabstractoauth2.h> + +#include <QtCore/qurl.h> +#include <QtCore/qstring.h> +#include <QtCore/qpointer.h> + +#include <QtNetwork/qnetworkreply.h> + +QT_BEGIN_NAMESPACE + +class QNetworkAccessManager; + +class QAbstractOAuth2Private : public QAbstractOAuthPrivate +{ + Q_DECLARE_PUBLIC(QAbstractOAuth2) + +public: + QAbstractOAuth2Private(const QPair<QString, QString> &clientCredentials, + const QUrl &authorizationUrl, QNetworkAccessManager *manager = nullptr); + QAbstractOAuth2Private(QNetworkAccessManager *manager = nullptr); + ~QAbstractOAuth2Private(); + + static QString generateRandomState(); + QNetworkRequest createRequest(const QUrl &url, const QVariantMap ¶meters); + + QPair<QString, QString> clientCredentials; + QString token; + QString scope; + QString state = generateRandomState(); + QString userAgent = QStringLiteral("QtOAuth/1.0 (+https://www.qt.io)"); + const QString bearerFormat = QStringLiteral("Bearer %1"); // Case sensitive + QDateTime expiresAt; + QString refreshToken; + + struct OAuth2KeyString + { + static const QString accessToken; + static const QString apiKey; + static const QString clientIdentifier; + static const QString clientSharedSecret; + static const QString code; + static const QString error; + static const QString errorDescription; + static const QString errorUri; + static const QString expiresIn; + static const QString grantType; + static const QString redirectUri; + static const QString refreshToken; + static const QString responseType; + static const QString scope; + static const QString state; + static const QString tokenType; + }; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_HTTP + +#endif // QABSTRACTOAUTH2_P_H diff --git a/src/oauth/qabstractoauth_p.h b/src/oauth/qabstractoauth_p.h new file mode 100644 index 0000000..214108c --- /dev/null +++ b/src/oauth/qabstractoauth_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the Network Access API. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QABSTRACTQOAUTH_P_H +#define QABSTRACTQOAUTH_P_H + +#ifndef QT_NO_HTTP + +#include <private/qobject_p.h> + +#include <QtNetworkAuth/qoauthglobal.h> +#include <QtNetworkAuth/qabstractoauth.h> +#include <QtNetworkAuth/qoauthoobreplyhandler.h> + +#include <QtCore/qurl.h> +#include <QtCore/qglobal.h> +#include <QtCore/qvariant.h> +#include <QtCore/qscopedpointer.h> + +#include <QtNetwork/qtcpserver.h> +#include <QtNetwork/qnetworkaccessmanager.h> + +QT_BEGIN_NAMESPACE + +class QUrlQuery; + +class QAbstractOAuthPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QAbstractOAuth) + +public: + QAbstractOAuthPrivate(QNetworkAccessManager *manager); + QAbstractOAuthPrivate(const QUrl &authorizationUrl, QNetworkAccessManager *manager); + ~QAbstractOAuthPrivate(); + + QNetworkAccessManager *networkAccessManager(); + void setStatus(QAbstractOAuth::Status status); + static QByteArray generateRandomString(quint8 length); + + // Resource Owner Authorization: https://tools.ietf.org/html/rfc5849#section-2.2 + QUrl authorizationUrl; + QVariantMap extraTokens; + QAbstractOAuth::Status status = QAbstractOAuth::Status::NotAuthenticated; + QNetworkAccessManager::Operation operation; + QPointer<QAbstractOAuthReplyHandler> replyHandler; + QScopedPointer<QOAuthOobReplyHandler> defaultReplyHandler; + QPointer<QNetworkAccessManager> networkAccessManagerPointer; + QAbstractOAuth::ModifyParametersFunction modifyParametersFunction; + + static QUrlQuery createQuery(const QVariantMap ¶meters); +}; + +QT_END_NAMESPACE + +#endif // QT_NO_HTTP + +#endif // QABSTRACTQOAUTH_H diff --git a/src/oauth/qabstractoauthreplyhandler.cpp b/src/oauth/qabstractoauthreplyhandler.cpp new file mode 100644 index 0000000..6237732 --- /dev/null +++ b/src/oauth/qabstractoauthreplyhandler.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT_NO_HTTP + +#include "qabstractoauthreplyhandler.h" + +QT_BEGIN_NAMESPACE + +QAbstractOAuthReplyHandler::QAbstractOAuthReplyHandler(QObject *parent) + : QObject(parent) +{} + +QAbstractOAuthReplyHandler::~QAbstractOAuthReplyHandler() +{} + +QAbstractOAuthReplyHandler::QAbstractOAuthReplyHandler(QObjectPrivate &d, QObject *parent) + : QObject(d, parent) +{} + +QT_END_NAMESPACE + +#endif // QT_NO_HTTP diff --git a/src/oauth/qabstractoauthreplyhandler.h b/src/oauth/qabstractoauthreplyhandler.h new file mode 100644 index 0000000..e5cddb1 --- /dev/null +++ b/src/oauth/qabstractoauthreplyhandler.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QABSTRACTOAUTHREPLYHANDLER_H +#define QABSTRACTOAUTHREPLYHANDLER_H + +#ifndef QT_NO_HTTP + +#include <QtNetworkAuth/qoauthglobal.h> +#include <QtNetworkAuth/qabstractoauth.h> + +#include <QtCore/qobject.h> + +QT_BEGIN_NAMESPACE + +class Q_OAUTH_EXPORT QAbstractOAuthReplyHandler : public QObject +{ + Q_OBJECT + +public: + explicit QAbstractOAuthReplyHandler(QObject *parent = nullptr); + virtual ~QAbstractOAuthReplyHandler(); + + virtual QString callback() const = 0; + +public Q_SLOTS: + virtual void networkReplyFinished(QNetworkReply *reply) = 0; + +Q_SIGNALS: + void callbackReceived(const QVariantMap &values); + void tokensReceived(const QVariantMap &tokens); + + void replyDataReceived(const QByteArray &data); + void callbackDataReceived(const QByteArray &data); + +protected: + QAbstractOAuthReplyHandler(QObjectPrivate &d, QObject *parent = nullptr); + +private: + Q_DISABLE_COPY(QAbstractOAuthReplyHandler) +}; + +QT_END_NAMESPACE + +#endif // QT_NO_HTTP + +#endif // QABSTRACTOAUTHREPLYHANDLER_H diff --git a/src/oauth/qoauth1.cpp b/src/oauth/qoauth1.cpp new file mode 100644 index 0000000..cd6133e --- /dev/null +++ b/src/oauth/qoauth1.cpp @@ -0,0 +1,654 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT_NO_HTTP + +#include "qoauth1.h" +#include "qoauth1_p.h" +#include "qoauth1signature.h" +#include "qoauthoobreplyhandler.h" +#include "qoauthhttpserverreplyhandler.h" + +#include <QtCore/qmap.h> +#include <QtCore/qlist.h> +#include <QtCore/qvariant.h> +#include <QtCore/qurlquery.h> +#include <QtCore/qdatetime.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qmessageauthenticationcode.h> + +#include <QtNetwork/qnetworkreply.h> +#include <QtNetwork/qnetworkrequest.h> +#include <QtNetwork/qnetworkaccessmanager.h> + +QT_BEGIN_NAMESPACE + +using Key = QOAuth1Private::OAuth1KeyString; +const QString Key::oauthCallback = QStringLiteral("oauth_callback"); +const QString Key::oauthCallbackConfirmed = QStringLiteral("oauth_callback_confirmed"); +const QString Key::oauthConsumerKey = QStringLiteral("oauth_consumer_key"); +const QString Key::oauthNonce = QStringLiteral("oauth_nonce"); +const QString Key::oauthSignature = QStringLiteral("oauth_signature"); +const QString Key::oauthSignatureMethod = QStringLiteral("oauth_signature_method"); +const QString Key::oauthTimestamp = QStringLiteral("oauth_timestamp"); +const QString Key::oauthToken = QStringLiteral("oauth_token"); +const QString Key::oauthTokenSecret = QStringLiteral("oauth_token_secret"); +const QString Key::oauthVerifier = QStringLiteral("oauth_verifier"); +const QString Key::oauthVersion = QStringLiteral("oauth_version"); + +static auto networkReplyErrorFunctionPointer = static_cast<void(QNetworkReply::*)( + QNetworkReply::NetworkError)>(&QNetworkReply::error); + +QOAuth1Private::QOAuth1Private(QNetworkAccessManager *networkAccessManager) + : QAbstractOAuthPrivate(networkAccessManager) +{ + qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError"); +} + +void QOAuth1Private::appendCommonHeaders(QVariantMap *headers) +{ + const auto currentDateTime = QDateTime::currentDateTimeUtc(); + + headers->insert(Key::oauthNonce, QOAuth1::nonce()); + headers->insert(Key::oauthConsumerKey, clientCredentials.first); + headers->insert(Key::oauthTimestamp, QString::number(currentDateTime.toTime_t())); + headers->insert(Key::oauthVersion, oauthVersion); + headers->insert(Key::oauthSignatureMethod, signatureMethodString().toUtf8()); +} + +void QOAuth1Private::appendSignature(QAbstractOAuth::Stage stage, + QVariantMap *headers, + const QUrl &url, + QNetworkAccessManager::Operation operation, + const QVariantMap parameters) +{ + QByteArray signature; + { + QVariantMap allParameters = QVariantMap(*headers).unite(parameters); + if (modifyParametersFunction) + modifyParametersFunction(stage, &allParameters); + signature = generateSignature(allParameters, url, operation); + } + headers->insert(Key::oauthSignature, signature); +} + +QNetworkReply *QOAuth1Private::requestToken(QNetworkAccessManager::Operation operation, + const QUrl &url, + const QPair<QString, QString> &token, + const QVariantMap ¶meters) +{ + Q_Q(QOAuth1); + if (Q_UNLIKELY(!networkAccessManager())) { + qWarning("QOAuth1Private::requestToken: QNetworkAccessManager not available"); + return nullptr; + } + if (Q_UNLIKELY(url.isEmpty())) { + qWarning("QOAuth1Private::requestToken: Request Url not set"); + return nullptr; + } + if (Q_UNLIKELY(operation != QNetworkAccessManager::GetOperation && + operation != QNetworkAccessManager::PostOperation)) { + qWarning("QOAuth1Private::requestToken: Operation not supported"); + return nullptr; + } + + QNetworkRequest request(url); + request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + + QAbstractOAuth::Stage stage = QAbstractOAuth::Stage::RequestingTemporaryCredentials; + QVariantMap headers; + appendCommonHeaders(&headers); + headers.insert(Key::oauthCallback, q->callback()); + if (!token.first.isEmpty()) { + headers.insert(Key::oauthToken, token.first); + stage = QAbstractOAuth::Stage::RequestingAccessToken; + } + appendSignature(stage, &headers, url, operation, parameters); + + request.setRawHeader("Authorization", q->generateAuthorizationHeader(headers)); + + QNetworkReply *reply = nullptr; + if (operation == QNetworkAccessManager::GetOperation) { + if (parameters.size() > 0) { + QUrl url = request.url(); + url.setQuery(QOAuth1Private::createQuery(parameters)); + request.setUrl(url); + } + reply = networkAccessManager()->get(request); + } + else if (operation == QNetworkAccessManager::PostOperation) { + QUrlQuery query = QOAuth1Private::createQuery(parameters); + const QByteArray data = query.toString(QUrl::FullyEncoded).toUtf8(); + request.setHeader(QNetworkRequest::ContentTypeHeader, + QStringLiteral("application/x-www-form-urlencoded")); + reply = networkAccessManager()->post(request, data); + } + + connect(reply, networkReplyErrorFunctionPointer, this, &QOAuth1Private::_q_onTokenRequestError); + + QAbstractOAuthReplyHandler *handler = replyHandler ? replyHandler.data() + : defaultReplyHandler.data(); + QObject::connect(reply, &QNetworkReply::finished, + std::bind(&QAbstractOAuthReplyHandler::networkReplyFinished, handler, reply)); + connect(handler, &QAbstractOAuthReplyHandler::tokensReceived, this, + &QOAuth1Private::_q_tokensReceived); + + return reply; +} + +QString QOAuth1Private::signatureMethodString() const +{ + switch (signatureMethod) { // No default: intended + case QOAuth1::SignatureMethod::PlainText: + return QStringLiteral("PLAINTEXT"); + case QOAuth1::SignatureMethod::Hmac_Sha1: + return QStringLiteral("HMAC-SHA1"); + case QOAuth1::SignatureMethod::Rsa_Sha1: + qFatal("RSA-SHA1 signature method not supported"); + return QStringLiteral("RSA-SHA1"); + } + qFatal("Invalid signature method"); + return QString(); +} + +QByteArray QOAuth1Private::generateSignature(const QVariantMap ¶meters, + const QUrl &url, + QNetworkAccessManager::Operation operation) const +{ + const QOAuth1Signature signature(url, + clientCredentials.second, + tokenCredentials.second, + static_cast<QOAuth1Signature::HttpRequestMethod>(operation), + parameters); + + switch (signatureMethod) { + case QOAuth1::SignatureMethod::Hmac_Sha1: + return signature.hmacSha1().toBase64(); + case QOAuth1::SignatureMethod::PlainText: + return signature.plainText(clientCredentials.first); + default: + qFatal("QOAuth1Private::generateSignature: Signature method not supported"); + return QByteArray(); + } +} + +void QOAuth1Private::_q_onTokenRequestError(QNetworkReply::NetworkError error) +{ + Q_Q(QOAuth1); + Q_UNUSED(error); + Q_EMIT q->requestFailed(QAbstractOAuth::Error::NetworkError); +} + +void QOAuth1Private::_q_tokensReceived(const QVariantMap &tokens) +{ + Q_Q(QOAuth1); + + QPair<QString, QString> credential(tokens.value(Key::oauthToken).toString(), + tokens.value(Key::oauthTokenSecret).toString()); + switch (status) { + case QAbstractOAuth::Status::NotAuthenticated: + if (tokens.value(Key::oauthCallbackConfirmed, true).toBool()) { + q->setTokenCredentials(credential); + setStatus(QAbstractOAuth::Status::TemporaryCredentialsReceived); + } else { + Q_EMIT q->requestFailed(QAbstractOAuth::Error::OAuthCallbackNotVerified); + } + break; + case QAbstractOAuth::Status::TemporaryCredentialsReceived: + q->setTokenCredentials(credential); + setStatus(QAbstractOAuth::Status::Granted); + break; + case QAbstractOAuth::Status::Granted: + case QAbstractOAuth::Status::RefreshingToken: + break; + } + +} + +QOAuth1::QOAuth1(QObject *parent) + : QAbstractOAuth(*new QOAuth1Private, parent) +{} + +QOAuth1::QOAuth1(QNetworkAccessManager *manager, QObject *parent) + : QAbstractOAuth(*new QOAuth1Private(manager), parent) +{} + +QOAuth1::QOAuth1(const QString &clientIdentifier, + const QString &clientSharedSecret, + QNetworkAccessManager *manager, + QObject *parent) + : QAbstractOAuth(*new QOAuth1Private(manager), parent) +{ + Q_D(QOAuth1); + d->clientCredentials.first = clientIdentifier; + d->clientCredentials.second = clientSharedSecret; +} + +QOAuth1::~QOAuth1() +{} + +QString QOAuth1::clientIdentifier() const +{ + Q_D(const QOAuth1); + return d->clientCredentials.first; +} + +void QOAuth1::setClientIdentifier(const QString &clientIdentifier) +{ + Q_D(QOAuth1); + if (d->clientCredentials.first != clientIdentifier) { + d->clientCredentials.first = clientIdentifier; + Q_EMIT clientIdentifierChanged(clientIdentifier); + } +} + +QString QOAuth1::clientSharedSecret() const +{ + Q_D(const QOAuth1); + return d->clientCredentials.second; +} + +void QOAuth1::setClientSharedSecret(const QString &clientSharedSecret) +{ + Q_D(QOAuth1); + if (d->clientCredentials.second != clientSharedSecret) { + d->clientCredentials.second = clientSharedSecret; + Q_EMIT clientSharedSecretChanged(clientSharedSecret); + } +} + +QPair<QString, QString> QOAuth1::clientCredentials() const +{ + Q_D(const QOAuth1); + return d->clientCredentials; +} + +void QOAuth1::setClientCredentials(const QPair<QString, QString> &clientCredentials) +{ + setClientCredentials(clientCredentials.first, clientCredentials.second); +} + +void QOAuth1::setClientCredentials(const QString &clientIdentifier, + const QString &clientSharedSecret) +{ + setClientIdentifier(clientIdentifier); + setClientSharedSecret(clientSharedSecret); +} + +QString QOAuth1::token() const +{ + Q_D(const QOAuth1); + return d->tokenCredentials.first; +} + +void QOAuth1::setToken(const QString &token) +{ + Q_D(QOAuth1); + if (d->tokenCredentials.first != token) { + d->tokenCredentials.first = token; + Q_EMIT tokenChanged(token); + } +} + +QString QOAuth1::tokenSecret() const +{ + Q_D(const QOAuth1); + return d->tokenCredentials.second; +} + +void QOAuth1::setTokenSecret(const QString &tokenSecret) +{ + Q_D(QOAuth1); + if (d->tokenCredentials.second != tokenSecret) { + d->tokenCredentials.second = tokenSecret; + Q_EMIT tokenSecretChanged(tokenSecret); + } +} + +QPair<QString, QString> QOAuth1::tokenCredentials() const +{ + Q_D(const QOAuth1); + return d->tokenCredentials; +} + +void QOAuth1::setTokenCredentials(const QPair<QString, QString> &tokenCredentials) +{ + setTokenCredentials(tokenCredentials.first, tokenCredentials.second); +} + +void QOAuth1::setTokenCredentials(const QString &token, const QString &tokenSecret) +{ + setToken(token); + setTokenSecret(tokenSecret); +} + +QUrl QOAuth1::temporaryCredentialsUrl() const +{ + Q_D(const QOAuth1); + return d->temporaryCredentialsUrl; +} + +void QOAuth1::setTemporaryCredentialsUrl(const QUrl &url) +{ + Q_D(QOAuth1); + if (d->temporaryCredentialsUrl != url) { + d->temporaryCredentialsUrl = url; + Q_EMIT temporaryCredentialsUrlChanged(url); + } +} + +QUrl QOAuth1::tokenCredentialsUrl() const +{ + Q_D(const QOAuth1); + return d->tokenCredentialsUrl; +} + +void QOAuth1::setTokenCredentialsUrl(const QUrl &url) +{ + Q_D(QOAuth1); + if (d->tokenCredentialsUrl != url) { + d->tokenCredentialsUrl = url; + Q_EMIT tokenCredentialsUrlChanged(url); + } +} + +QOAuth1::SignatureMethod QOAuth1::signatureMethod() const +{ + Q_D(const QOAuth1); + return d->signatureMethod; +} + +void QOAuth1::setSignatureMethod(QOAuth1::SignatureMethod value) +{ + Q_D(QOAuth1); + if (d->signatureMethod != value) { + d->signatureMethod = value; + Q_EMIT signatureMethodChanged(value); + } +} + +QNetworkReply *QOAuth1::head(const QUrl &url, const QVariantMap ¶meters) +{ + Q_D(QOAuth1); + if (!d->networkAccessManager()) { + qWarning("QOAuth1::head: QNetworkAccessManager not available"); + return nullptr; + } + QNetworkRequest request(url); + setup(&request, parameters, QNetworkAccessManager::HeadOperation); + return d->networkAccessManager()->head(request); +} + +QNetworkReply *QOAuth1::get(const QUrl &url, const QVariantMap ¶meters) +{ + Q_D(QOAuth1); + if (!d->networkAccessManager()) { + qWarning("QOAuth1::get: QNetworkAccessManager not available"); + return nullptr; + } + QNetworkRequest request(url); + setup(&request, parameters, QNetworkAccessManager::GetOperation); + QNetworkReply *reply = d->networkAccessManager()->get(request); + connect(reply, &QNetworkReply::finished, std::bind(&QAbstractOAuth::finished, this, reply)); + return reply; +} + +QNetworkReply *QOAuth1::post(const QUrl &url, const QVariantMap ¶meters) +{ + Q_D(QOAuth1); + if (!d->networkAccessManager()) { + qWarning("QOAuth1::post: QNetworkAccessManager not available"); + return nullptr; + } + QNetworkRequest request(url); + setup(&request, parameters, QNetworkAccessManager::PostOperation); + + QUrlQuery query; + for (auto it = parameters.begin(), end = parameters.end(); it != end; ++it) + query.addQueryItem(it.key(), it.value().toString()); + QString data = query.toString(QUrl::FullyEncoded); + QNetworkReply *reply = d->networkAccessManager()->post(request, data.toUtf8()); + connect(reply, &QNetworkReply::finished, std::bind(&QAbstractOAuth::finished, this, reply)); + return reply; +} + +QNetworkReply *QOAuth1::deleteResource(const QUrl &url, const QVariantMap ¶meters) +{ + Q_D(QOAuth1); + if (!d->networkAccessManager()) { + qWarning("QOAuth1::deleteResource: QNetworkAccessManager not available"); + return nullptr; + } + QNetworkRequest request(url); + setup(&request, parameters, QNetworkAccessManager::DeleteOperation); + QNetworkReply *reply = d->networkAccessManager()->deleteResource(request); + connect(reply, &QNetworkReply::finished, std::bind(&QAbstractOAuth::finished, this, reply)); + return reply; +} + +QNetworkReply *QOAuth1::requestTemporaryCredentials(QNetworkAccessManager::Operation operation, + const QUrl &url, + const QVariantMap ¶meters) +{ + // https://tools.ietf.org/html/rfc5849#section-2.1 + Q_D(QOAuth1); + d->tokenCredentials = QPair<QString, QString>(); + return d->requestToken(operation, url, d->tokenCredentials, parameters); +} + +QNetworkReply *QOAuth1::requestTokenCredentials(QNetworkAccessManager::Operation operation, + const QUrl &url, + const QPair<QString, QString> &temporaryToken, + const QVariantMap ¶meters) +{ + Q_D(QOAuth1); + return d->requestToken(operation, url, temporaryToken, parameters); +} + +void QOAuth1::setup(QNetworkRequest *request, + const QVariantMap &signingParameters, + QNetworkAccessManager::Operation operation) +{ + Q_D(const QOAuth1); + + QVariantMap oauthParams; + QVariantMap otherParams = signingParameters; + // Adding parameters located in the query + { + auto queryItems = QUrlQuery(request->url().query()).queryItems(); + for (auto it = queryItems.begin(), end = queryItems.end(); it != end; ++it) + otherParams.insert(it->first, it->second); + } + + const auto currentDateTime = QDateTime::currentDateTimeUtc(); + + oauthParams.insert(Key::oauthConsumerKey, d->clientCredentials.first); + oauthParams.insert(Key::oauthVersion, QStringLiteral("1.0")); + oauthParams.insert(Key::oauthToken, d->tokenCredentials.first); + oauthParams.insert(Key::oauthSignatureMethod, d->signatureMethodString()); + oauthParams.insert(Key::oauthNonce, QOAuth1::nonce()); + oauthParams.insert(Key::oauthTimestamp, QString::number(currentDateTime.toTime_t())); + + // Add signature parameter + { + const auto parameters = QVariantMap(oauthParams).unite(signingParameters); + const auto signature = d->generateSignature(parameters, request->url(), operation); + oauthParams.insert(Key::oauthSignature, signature); + } + + if (operation == QNetworkAccessManager::GetOperation) { + if (signingParameters.size()) { + QUrl url = request->url(); + QUrlQuery query; + for (auto it = signingParameters.begin(), end = signingParameters.end(); it != end; + ++it) + query.addQueryItem(it.key(), it.value().toString()); + url.setQuery(query); + request->setUrl(url); + } + } + + request->setRawHeader("Authorization", generateAuthorizationHeader(oauthParams)); + + if (operation == QNetworkAccessManager::PostOperation) + request->setHeader(QNetworkRequest::ContentTypeHeader, + QStringLiteral("application/x-www-form-urlencoded")); +} + +QByteArray QOAuth1::nonce() +{ + // https://tools.ietf.org/html/rfc5849#section-3.3 + return QAbstractOAuth::generateRandomString(8); +} + +QByteArray QOAuth1::signature(const QVariantMap ¶meters, + const QUrl &url, + QNetworkAccessManager::Operation op, + const QString &clientSharedSecret, + const QString &tokenSecret) +{ + auto method = static_cast<QOAuth1Signature::HttpRequestMethod>(op); + QOAuth1Signature signature(url, clientSharedSecret, tokenSecret, method, parameters); + return signature.hmacSha1().toBase64(); +} + +QByteArray QOAuth1::generateAuthorizationHeader(const QVariantMap &oauthParams) +{ + // https://tools.ietf.org/html/rfc5849#section-3.5.1 + // TODO Add realm parameter support + bool first = true; + QString ret(QStringLiteral("OAuth ")); + QVariantMap headers(oauthParams); + for (auto it = headers.begin(), end = headers.end(); it != end; ++it) { + if (first) + first = false; + else + ret += QLatin1String(","); + ret += it.key() + + QLatin1String("=\"") + + QString::fromUtf8(QUrl::toPercentEncoding(it.value().toString())) + + QLatin1Char('\"'); + } + return ret.toUtf8(); +} + +void QOAuth1::grant() +{ + // https://tools.ietf.org/html/rfc5849#section-2 + Q_D(QOAuth1); + using Key = QOAuth1Private::OAuth1KeyString; + + if (d->temporaryCredentialsUrl.isEmpty()) { + qWarning("QOAuth1::grant: requestTokenUrl is empty"); + return; + } + if (d->tokenCredentialsUrl.isEmpty()) { + qWarning("QOAuth1::grant: authorizationGrantUrl is empty"); + return; + } + if (!d->tokenCredentials.first.isEmpty()) { + qWarning("QOAuth1::grant: Already authenticated"); + return; + } + + QMetaObject::Connection connection; + connection = connect(this, &QAbstractOAuth::statusChanged, [&](Status status) { + Q_D(QOAuth1); + + if (status == Status::TemporaryCredentialsReceived) { + if (d->authorizationUrl.isEmpty()) { + // try upgrading token without verifier + auto reply = requestTokenCredentials(QNetworkAccessManager::PostOperation, + d->tokenCredentialsUrl, + d->tokenCredentials); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); + } else { + QVariantMap parameters; + parameters.insert(Key::oauthToken, d->tokenCredentials.first); + if (d->modifyParametersFunction) + d->modifyParametersFunction(Stage::RequestingAuthorization, ¶meters); + + // https://tools.ietf.org/html/rfc5849#section-2.2 + resourceOwnerAuthorization(d->authorizationUrl, parameters); + } + } else if (status == Status::Granted) { + Q_EMIT granted(); + } else { + // Inherit class called QAbstractOAuth::setStatus(Status::NotAuthenticated); + setTokenCredentials(QString(), QString()); + disconnect(connection); + } + }); + + auto httpReplyHandler = qobject_cast<QOAuthHttpServerReplyHandler*>(replyHandler()); + if (httpReplyHandler) { + connect(httpReplyHandler, &QOAuthHttpServerReplyHandler::callbackReceived, [&]( + const QVariantMap &values) { + QString verifier = values.value(Key::oauthVerifier).toString(); + if (verifier.isEmpty()) { + qWarning("%s not found in the callback", qPrintable(Key::oauthVerifier)); + return; + } + continueGrantWithVerifier(verifier); + }); + } + + // requesting temporary credentials + auto reply = requestTemporaryCredentials(QNetworkAccessManager::PostOperation, + d->temporaryCredentialsUrl); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); +} + +void QOAuth1::continueGrantWithVerifier(const QString &verifier) +{ + // https://tools.ietf.org/html/rfc5849#section-2.3 + Q_D(QOAuth1); + + QVariantMap parameters; + parameters.insert(Key::oauthVerifier, verifier); + auto reply = requestTokenCredentials(QNetworkAccessManager::PostOperation, + d->tokenCredentialsUrl, + d->tokenCredentials, + parameters); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); +} + +QT_END_NAMESPACE + +#endif // QT_NO_HTTP diff --git a/src/oauth/qoauth1.h b/src/oauth/qoauth1.h new file mode 100644 index 0000000..3d13645 --- /dev/null +++ b/src/oauth/qoauth1.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOAUTH1_H +#define QOAUTH1_H + +#ifndef QT_NO_HTTP + +#include <QtNetworkAuth/qoauthglobal.h> +#include <QtNetworkAuth/qabstractoauth.h> + +#include <QtNetwork/qnetworkaccessmanager.h> + +QT_BEGIN_NAMESPACE + +class QOAuth1Private; +class Q_OAUTH_EXPORT QOAuth1: public QAbstractOAuth +{ + Q_OBJECT + +public: + enum class SignatureMethod { + Hmac_Sha1, + Rsa_Sha1, + PlainText + }; + + Q_ENUM(SignatureMethod) + + explicit QOAuth1(QObject *parent = nullptr); + explicit QOAuth1(QNetworkAccessManager *manager, + QObject *parent = nullptr); + + QOAuth1(const QString &clientIdentifier, + const QString &clientSharedSecret, + QNetworkAccessManager *manager, + QObject *parent = nullptr); + + ~QOAuth1(); + + // Client credentials + QString clientIdentifier() const override; + void setClientIdentifier(const QString &clientIdentifier) override; + QString clientSharedSecret() const; + void setClientSharedSecret(const QString &clientSharedSecret); + QPair<QString, QString> clientCredentials() const; + void setClientCredentials(const QPair<QString, QString> &clientCredentials); + void setClientCredentials(const QString &clientIdentifier, const QString &clientSharedSecret); + + // Token credentials: https://tools.ietf.org/html/rfc5849#section-2.3 + QString token() const override; + void setToken(const QString &token) override; + QString tokenSecret() const; + void setTokenSecret(const QString &tokenSecret); + QPair<QString, QString> tokenCredentials() const; + void setTokenCredentials(const QPair<QString, QString> &tokenCredentials); + void setTokenCredentials(const QString &token, const QString &tokenSecret); + + // Temporary Credentials: https://tools.ietf.org/html/rfc5849#section-2.1 + QUrl temporaryCredentialsUrl() const; + void setTemporaryCredentialsUrl(const QUrl &url); + + // Token Credentials: https://tools.ietf.org/html/rfc5849#section-2.3 + QUrl tokenCredentialsUrl() const; + void setTokenCredentialsUrl(const QUrl &url); + + // Signature method: https://tools.ietf.org/html/rfc5849#section-3.4 + SignatureMethod signatureMethod() const; + void setSignatureMethod(SignatureMethod value); + +public: + QNetworkReply *head(const QUrl &url, const QVariantMap ¶meters = QVariantMap()) override; + QNetworkReply *get(const QUrl &url, const QVariantMap ¶meters = QVariantMap()) override; + + QNetworkReply *post(const QUrl &url, const QVariantMap ¶meters = QVariantMap()) override; + QNetworkReply *deleteResource(const QUrl &url, + const QVariantMap ¶meters = QVariantMap()) override; + +public Q_SLOTS: + void grant() override; + void continueGrantWithVerifier(const QString &verifier); + +Q_SIGNALS: + void signatureMethodChanged(SignatureMethod method); + void clientSharedSecretChanged(const QString &credential); + void tokenSecretChanged(const QString &token); + void temporaryCredentialsUrlChanged(const QUrl &url); + void tokenCredentialsUrlChanged(const QUrl &url); + +protected: + QNetworkReply *requestTemporaryCredentials(QNetworkAccessManager::Operation operation, + const QUrl &url, + const QVariantMap ¶meters = QVariantMap()); + + QNetworkReply *requestTokenCredentials(QNetworkAccessManager::Operation operation, + const QUrl &url, + const QPair<QString, QString> &temporaryToken, + const QVariantMap ¶meters = QVariantMap()); + + void setup(QNetworkRequest *request, + const QVariantMap &signingParameters, + QNetworkAccessManager::Operation operation); + + static QByteArray nonce(); + static QByteArray generateAuthorizationHeader(const QVariantMap &oauthParams); + + // Signature: https://tools.ietf.org/html/rfc5849#section-3.4 + static QByteArray signature(const QVariantMap ¶meters, + const QUrl &url, + QNetworkAccessManager::Operation op, + const QString &clientSharedSecret, + const QString &tokenSecret); + +private: + Q_DISABLE_COPY(QOAuth1) + Q_DECLARE_PRIVATE(QOAuth1) +}; + +QT_END_NAMESPACE + +#endif // QT_NO_HTTP + +#endif // QOAUTH1_H diff --git a/src/oauth/qoauth1_p.h b/src/oauth/qoauth1_p.h new file mode 100644 index 0000000..167aed2 --- /dev/null +++ b/src/oauth/qoauth1_p.h @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the Network Access API. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QOAUTH1_P_H +#define QOAUTH1_P_H + +#ifndef QT_NO_HTTP + +#include <private/qabstractoauth_p.h> + +#include <QtNetworkAuth/qoauth1.h> +#include <QtNetworkAuth/qoauthglobal.h> + +#include <QtCore/qurl.h> +#include <QtCore/qobject.h> + +#include <QtNetwork/qnetworkreply.h> +#include <QtNetwork/qnetworkaccessmanager.h> + +QT_BEGIN_NAMESPACE + +class QOAuth1Private : public QAbstractOAuthPrivate +{ + Q_DECLARE_PUBLIC(QOAuth1) + +public: + QOAuth1Private(QNetworkAccessManager *networkAccessManager = nullptr); + + void appendCommonHeaders(QVariantMap *headers); + void appendSignature(QAbstractOAuth::Stage stage, + QVariantMap *headers, + const QUrl &url, + QNetworkAccessManager::Operation operation, + const QVariantMap parameters); + + QNetworkReply *requestToken(QNetworkAccessManager::Operation operation, + const QUrl &url, + const QPair<QString, QString> &token, + const QVariantMap &additionalParameters); + + QString signatureMethodString() const; + QByteArray generateSignature(const QVariantMap ¶meters, + const QUrl &url, + QNetworkAccessManager::Operation operation) const; + + void _q_onTokenRequestError(QNetworkReply::NetworkError error); + void _q_tokensReceived(const QVariantMap &tokens); + + QPair<QString, QString> clientCredentials; + QPair<QString, QString> tokenCredentials; + QString verifier; + QUrl temporaryCredentialsUrl; + QUrl tokenCredentialsUrl; + QOAuth1::SignatureMethod signatureMethod = QOAuth1::SignatureMethod::Hmac_Sha1; + const QString oauthVersion = QStringLiteral("1.0"); + + struct OAuth1KeyString + { + static const QString oauthCallback; + static const QString oauthCallbackConfirmed; + static const QString oauthConsumerKey; + static const QString oauthNonce; + static const QString oauthSignature; + static const QString oauthSignatureMethod; + static const QString oauthTimestamp; + static const QString oauthToken; + static const QString oauthTokenSecret; + static const QString oauthVerifier; + static const QString oauthVersion; + }; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_HTTP + +#endif // QOAUTH1_P_H diff --git a/src/oauth/qoauth1signature.cpp b/src/oauth/qoauth1signature.cpp new file mode 100644 index 0000000..238cf20 --- /dev/null +++ b/src/oauth/qoauth1signature.cpp @@ -0,0 +1,300 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qoauth1signature.h" +#include "qoauth1signature_p.h" + +#include <QtCore/qdebug.h> +#include <QtCore/qurlquery.h> +#include <QtCore/qmessageauthenticationcode.h> + +#include <QtNetwork/qnetworkaccessmanager.h> + +#include <functional> +#include <type_traits> + +QT_BEGIN_NAMESPACE + +static_assert(static_cast<int>(QOAuth1Signature::HttpRequestMethod::Head) == + static_cast<int>(QNetworkAccessManager::HeadOperation) && + static_cast<int>(QOAuth1Signature::HttpRequestMethod::Get) == + static_cast<int>(QNetworkAccessManager::GetOperation) && + static_cast<int>(QOAuth1Signature::HttpRequestMethod::Put) == + static_cast<int>(QNetworkAccessManager::PutOperation) && + static_cast<int>(QOAuth1Signature::HttpRequestMethod::Post) == + static_cast<int>(QNetworkAccessManager::PostOperation) && + static_cast<int>(QOAuth1Signature::HttpRequestMethod::Delete) == + static_cast<int>(QNetworkAccessManager::DeleteOperation), + "Invalid QOAuth1Signature::HttpRequestMethod enumeration values"); + +QOAuth1SignaturePrivate QOAuth1SignaturePrivate::shared_null; + +QOAuth1SignaturePrivate::QOAuth1SignaturePrivate() +{} + +QOAuth1SignaturePrivate::QOAuth1SignaturePrivate(const QUrl &url, + QOAuth1Signature::HttpRequestMethod method, + const QVariantMap ¶meters, + const QString &clientSharedKey, + const QString &tokenSecret) : + method(method), url(url), clientSharedKey(clientSharedKey), tokenSecret(tokenSecret), + parameters(parameters) +{} + +QByteArray QOAuth1SignaturePrivate::signatureBaseString() const +{ + // https://tools.ietf.org/html/rfc5849#section-3.4.1 + QByteArray base; + + switch (method) { + case QOAuth1Signature::HttpRequestMethod::Head: + base.append("HEAD"); + break; + case QOAuth1Signature::HttpRequestMethod::Get: + base.append("GET"); + break; + case QOAuth1Signature::HttpRequestMethod::Put: + base.append("PUT"); + break; + case QOAuth1Signature::HttpRequestMethod::Post: + base.append("POST"); + break; + case QOAuth1Signature::HttpRequestMethod::Delete: + base.append("DELETE"); + break; + default: + qCritical("QOAuth1Signature: HttpRequestMethod not supported"); + } + base.append('&'); + base.append(QUrl::toPercentEncoding(url.toString(QUrl::RemoveQuery)) + "&"); + + QVariantMap p = parameters; + { + const auto queryItems = QUrlQuery(url.query()).queryItems(); + for (auto it = queryItems.begin(), end = queryItems.end(); it != end; ++it) + p.insert(it->first, it->second); + } + base.append(encodeHeaders(p)); + return base; +} + +QByteArray QOAuth1SignaturePrivate::secret() const +{ + QByteArray secret; + secret.append(QUrl::toPercentEncoding(clientSharedKey)); + secret.append('&'); + secret.append(QUrl::toPercentEncoding(tokenSecret)); + return secret; +} + +QByteArray QOAuth1SignaturePrivate::parameterString(const QVariantMap ¶meters) +{ + QByteArray ret; + auto previous = parameters.end(); + for (auto it = parameters.begin(), end = parameters.end(); it != end; previous = it++) { + if (previous != parameters.end()) { + if (Q_UNLIKELY(previous.key() == it.key())) + qWarning("QOAuth: duplicated key %s", qPrintable(it.key())); + ret.append("&"); + } + ret.append(QUrl::toPercentEncoding(it.key())); + ret.append("="); + ret.append(QUrl::toPercentEncoding(it.value().toString())); + } + return ret; +} + +QByteArray QOAuth1SignaturePrivate::encodeHeaders(const QVariantMap &headers) +{ + return QUrl::toPercentEncoding(QString::fromLatin1(parameterString(headers))); +} + +QOAuth1Signature::QOAuth1Signature(const QUrl &url, QOAuth1Signature::HttpRequestMethod method, + const QVariantMap ¶meters) : + d(new QOAuth1SignaturePrivate(url, method, parameters)) +{} + +QOAuth1Signature::QOAuth1Signature(const QUrl &url, const QString &clientSharedKey, + const QString &tokenSecret, HttpRequestMethod method, + const QVariantMap ¶meters) : + d(new QOAuth1SignaturePrivate(url, method, parameters, clientSharedKey, tokenSecret)) +{} + +QOAuth1Signature::QOAuth1Signature(const QOAuth1Signature &other) : d(other.d) +{} + +QOAuth1Signature::QOAuth1Signature(QOAuth1Signature &&other) : d(other.d) +{ + other.d = &QOAuth1SignaturePrivate::shared_null; +} + +QOAuth1Signature::~QOAuth1Signature() +{} + +QOAuth1Signature::HttpRequestMethod QOAuth1Signature::httpRequestMethod() const +{ + return d->method; +} + +void QOAuth1Signature::setHttpRequestMethod(QOAuth1Signature::HttpRequestMethod method) +{ + d->method = method; +} + +QUrl QOAuth1Signature::url() const +{ + return d->url; +} + +void QOAuth1Signature::setUrl(const QUrl &url) +{ + d->url = url; +} + +QVariantMap QOAuth1Signature::parameters() const +{ + return d->parameters; +} + +void QOAuth1Signature::setParameters(const QVariantMap ¶meters) +{ + d->parameters = parameters; +} + +void QOAuth1Signature::addRequestBody(const QUrlQuery &body) +{ + const auto list = body.queryItems(); + for (auto it = list.begin(), end = list.end(); it != end; ++it) + d->parameters.insert(it->first, it->second); +} + +void QOAuth1Signature::insert(const QString &key, const QVariant &value) +{ + d->parameters.insert(key, value); +} + +QList<QString> QOAuth1Signature::keys() const +{ + return d->parameters.uniqueKeys(); +} + +QVariant QOAuth1Signature::take(const QString &key) +{ + return d->parameters.take(key); +} + +QVariant QOAuth1Signature::value(const QString &key, const QVariant &defaultValue) const +{ + return d->parameters.value(key, defaultValue); +} + +QString QOAuth1Signature::clientSharedKey() const +{ + return d->clientSharedKey; +} + +void QOAuth1Signature::setClientSharedKey(const QString &secret) +{ + d->clientSharedKey = secret; +} + +QString QOAuth1Signature::tokenSecret() const +{ + return d->tokenSecret; +} + +void QOAuth1Signature::setTokenSecret(const QString &secret) +{ + d->tokenSecret = secret; +} + +QByteArray QOAuth1Signature::hmacSha1() const +{ + QMessageAuthenticationCode code(QCryptographicHash::Sha1); + code.setKey(d->secret()); + code.addData(d->signatureBaseString()); + return code.result(); +} + +QByteArray QOAuth1Signature::rsaSha1() const +{ + qCritical("QOAuth1Signature::rsaSha1: RSA-SHA1 signing method not supported"); + return QByteArray(); +} + +QByteArray QOAuth1Signature::plainText(const QString &clientIdentifier) const +{ + return plainText(clientIdentifier, d->clientSharedKey); +} + +QByteArray QOAuth1Signature::plainText(const QString &clientIdentifier, + const QString clientSharedKey) +{ + QByteArray ret; + ret += clientIdentifier.toUtf8() + '&' + clientSharedKey.toUtf8(); + return ret; +} + +QByteArray QOAuth1Signature::plainText(const QPair<QString, QString> &clientCredentials) +{ + return plainText(clientCredentials.first, clientCredentials.second); +} + +void QOAuth1Signature::swap(QOAuth1Signature &other) +{ + qSwap(d, other.d); +} + +QOAuth1Signature &QOAuth1Signature::operator=(const QOAuth1Signature &other) +{ + if (d != other.d) { + QOAuth1Signature tmp(other); + tmp.swap(*this); + } + return *this; +} + +QOAuth1Signature &QOAuth1Signature::operator=(QOAuth1Signature &&other) +{ + QOAuth1Signature moved(std::move(other)); + swap(moved); + return *this; +} + +QT_END_NAMESPACE diff --git a/src/oauth/qoauth1signature.h b/src/oauth/qoauth1signature.h new file mode 100644 index 0000000..c0d5719 --- /dev/null +++ b/src/oauth/qoauth1signature.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOAUTH1SIGNATURE_H +#define QOAUTH1SIGNATURE_H + +#ifndef QT_NO_HTTP + +#include <QtNetworkAuth/qoauthglobal.h> + +#include <QtCore/qurl.h> +#include <QtCore/qvariant.h> +#include <QtCore/qshareddata.h> + +QT_BEGIN_NAMESPACE + +class QUrlQuery; + +class QOAuth1SignaturePrivate; +class Q_OAUTH_EXPORT QOAuth1Signature +{ +public: + enum class HttpRequestMethod { + Head = 1, + Get, + Put, + Post, + Delete, + Custom, + + Unknown = 0 + }; + + explicit QOAuth1Signature(const QUrl &url = QUrl(), + HttpRequestMethod method = HttpRequestMethod::Post, + const QVariantMap ¶meters = QVariantMap()); + QOAuth1Signature(const QUrl &url, const QString &clientSharedKey, const QString &tokenSecret, + HttpRequestMethod method = HttpRequestMethod::Post, + const QVariantMap ¶meters = QVariantMap()); + QOAuth1Signature(const QOAuth1Signature &other); + QOAuth1Signature(QOAuth1Signature &&other); + ~QOAuth1Signature(); + + HttpRequestMethod httpRequestMethod() const; + void setHttpRequestMethod(HttpRequestMethod method); + + QUrl url() const; + void setUrl(const QUrl &url); + + QVariantMap parameters() const; + void setParameters(const QVariantMap ¶meters); + void addRequestBody(const QUrlQuery &body); + + void insert(const QString &key, const QVariant &value); + QList<QString> keys() const; + QVariant take(const QString &key); + QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; + + QString clientSharedKey() const; + void setClientSharedKey(const QString &secret); + + QString tokenSecret() const; + void setTokenSecret(const QString &secret); + + QByteArray hmacSha1() const; + QByteArray rsaSha1() const; + QByteArray plainText(const QString &clientIdentifier) const; + + static QByteArray plainText(const QString &clientIdentifier, const QString clientSharedKey); + static QByteArray plainText(const QPair<QString, QString> &clientCredentials); + + void swap(QOAuth1Signature &other); + QOAuth1Signature &operator=(const QOAuth1Signature &other); + QOAuth1Signature &operator=(QOAuth1Signature &&other); + +private: + QSharedDataPointer<QOAuth1SignaturePrivate> d; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_HTTP + +#endif // QOAUTH1SIGNATURE_H diff --git a/src/oauth/qoauth1signature_p.h b/src/oauth/qoauth1signature_p.h new file mode 100644 index 0000000..5b68cc2 --- /dev/null +++ b/src/oauth/qoauth1signature_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the Network Access API. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QOAUTH1SIGNATURE_P_H +#define QOAUTH1SIGNATURE_P_H + +#include <QtNetworkAuth/qoauth1signature.h> + +#include <QtCore/qurl.h> +#include <QtCore/qstring.h> +#include <QtCore/qvariant.h> +#include <QtCore/qshareddata.h> + +QT_BEGIN_NAMESPACE + +class QOAuth1SignaturePrivate : public QSharedData +{ +public: + QOAuth1SignaturePrivate(); + QOAuth1SignaturePrivate(const QUrl &url, QOAuth1Signature::HttpRequestMethod method, + const QVariantMap ¶meters, + const QString &clientSharedKey = QString(), + const QString &tokenSecret = QString()); + + QByteArray signatureBaseString() const; + QByteArray secret() const; + static QByteArray parameterString(const QVariantMap ¶meters); + static QByteArray encodeHeaders(const QVariantMap &headers); + + + QOAuth1Signature::HttpRequestMethod method = QOAuth1Signature::HttpRequestMethod::Post; + QUrl url; + QString clientSharedKey; + QString tokenSecret; + QVariantMap parameters; + + static QOAuth1SignaturePrivate shared_null; +}; + +QT_END_NAMESPACE + +#endif // QOAUTH1SIGNATURE_P_H diff --git a/src/oauth/qoauth2authorizationcodeflow.cpp b/src/oauth/qoauth2authorizationcodeflow.cpp new file mode 100644 index 0000000..a41993d --- /dev/null +++ b/src/oauth/qoauth2authorizationcodeflow.cpp @@ -0,0 +1,353 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT_NO_HTTP + +#include <qoauth2authorizationcodeflow.h> +#include <private/qoauth2authorizationcodeflow_p.h> + +#include <qmap.h> +#include <qurl.h> +#include <qvariant.h> +#include <qurlquery.h> +#include <qjsonobject.h> +#include <qjsondocument.h> +#include <qauthenticator.h> +#include <qoauthhttpserverreplyhandler.h> + +#include <functional> + +QT_BEGIN_NAMESPACE + +QOAuth2AuthorizationCodeFlowPrivate::QOAuth2AuthorizationCodeFlowPrivate( + const QUrl &authorizationUrl, const QUrl &accessTokenUrl, const QString &clientIdentifier, + QNetworkAccessManager *manager) : + QAbstractOAuth2Private(qMakePair(clientIdentifier, QString()), authorizationUrl, manager), + accessTokenUrl(accessTokenUrl) +{} + +QOAuth2AuthorizationCodeFlowPrivate::QOAuth2AuthorizationCodeFlowPrivate( + QNetworkAccessManager *manager) : QAbstractOAuth2Private(manager) +{} + +void QOAuth2AuthorizationCodeFlowPrivate::_q_handleCallback(const QVariantMap &data) +{ + Q_Q(QOAuth2AuthorizationCodeFlow); + using Key = QAbstractOAuth2Private::OAuth2KeyString; + + if (status != QAbstractOAuth::Status::NotAuthenticated) { + qWarning("QOAuth2AuthorizationCodeFlow: Unexpected call"); + return; + } + + Q_ASSERT(!state.isEmpty()); + + const QString error = data.value(Key::error).toString(); + const QString code = data.value(Key::code).toString(); + const QString receivedState = data.value(Key::state).toString(); + if (error.size()) { + const QString uri = data.value(Key::errorUri).toString(); + const QString description = data.value(Key::errorDescription).toString(); + qWarning("QOAuth2AuthorizationCodeFlow: AuthenticationError: %s(%s): %s", + qPrintable(error), qPrintable(uri), qPrintable(description)); + Q_EMIT q->error(error, description, uri); + return; + } + if (code.isEmpty()) { + qWarning("QOAuth2AuthorizationCodeFlow: AuthenticationError: Code not received"); + return; + } + if (receivedState.isEmpty()) { + qWarning("QOAuth2AuthorizationCodeFlow: State not received"); + return; + } + if (state != receivedState) { + qWarning("QOAuth2AuthorizationCodeFlow: State mismatch"); + return; + } + + setStatus(QAbstractOAuth::Status::TemporaryCredentialsReceived); + + QVariantMap copy(data); + copy.remove(Key::code); + extraTokens = copy; + q->requestAccessToken(code); +} + +void QOAuth2AuthorizationCodeFlowPrivate::_q_accessTokenRequestFinished(const QVariantMap &values) +{ + Q_Q(QOAuth2AuthorizationCodeFlow); + using Key = QAbstractOAuth2Private::OAuth2KeyString; + + if (values.contains(Key::error)) { + const QString error = values.value(Key::error).toString(); + qWarning("QOAuth2AuthorizationCodeFlow Error: %s", qPrintable(error)); + return; + } + + bool ok; + const QString accessToken = values.value(Key::accessToken).toString(); + tokenType = values.value(Key::tokenType).toString(); + int expiresIn = values.value(Key::expiresIn).toInt(&ok); + if (!ok) + expiresIn = -1; + refreshToken = values.value(Key::refreshToken).toString(); + scope = values.value(Key::scope).toString(); + if (accessToken.isEmpty()) { + qWarning("QOAuth2AuthorizationCodeFlow: Access token not received"); + return; + } + q->setToken(accessToken); + + const QDateTime currentDateTime = QDateTime::currentDateTime(); + if (expiresIn > 0 && currentDateTime.secsTo(expiresAt) != expiresIn) { + expiresAt = currentDateTime.addSecs(expiresIn); + Q_EMIT q->expirationAtChanged(expiresAt); + } + + setStatus(QAbstractOAuth::Status::Granted); +} + +void QOAuth2AuthorizationCodeFlowPrivate::_q_authenticate(QNetworkReply *reply, + QAuthenticator *authenticator) +{ + if (reply == currentReply){ + const auto url = reply->url(); + if (url == accessTokenUrl) { + authenticator->setUser(clientCredentials.first); + authenticator->setPassword(QString()); + } + } +} + +QOAuth2AuthorizationCodeFlow::QOAuth2AuthorizationCodeFlow(QObject *parent) : + QAbstractOAuth2(*new QOAuth2AuthorizationCodeFlowPrivate, parent) +{} + +QOAuth2AuthorizationCodeFlow::QOAuth2AuthorizationCodeFlow(QNetworkAccessManager *manager, + QObject *parent) : + QAbstractOAuth2(*new QOAuth2AuthorizationCodeFlowPrivate(manager), parent) +{} + +QOAuth2AuthorizationCodeFlow::QOAuth2AuthorizationCodeFlow(const QString &clientIdentifier, + QNetworkAccessManager *manager, + QObject *parent) : + QAbstractOAuth2(*new QOAuth2AuthorizationCodeFlowPrivate(QUrl(), QUrl(), clientIdentifier, + manager), + parent) +{} + +QOAuth2AuthorizationCodeFlow::QOAuth2AuthorizationCodeFlow(const QUrl &authenticateUrl, + const QUrl &accessTokenUrl, + QNetworkAccessManager *manager, + QObject *parent) : + QAbstractOAuth2(*new QOAuth2AuthorizationCodeFlowPrivate(authenticateUrl, accessTokenUrl, + QString(), manager), + parent) +{} + +QOAuth2AuthorizationCodeFlow::QOAuth2AuthorizationCodeFlow(const QString &clientIdentifier, + const QUrl &authenticateUrl, + const QUrl &accessTokenUrl, + QNetworkAccessManager *manager, + QObject *parent) : + QAbstractOAuth2(*new QOAuth2AuthorizationCodeFlowPrivate(authenticateUrl, accessTokenUrl, + clientIdentifier, manager), + parent) +{} + +QOAuth2AuthorizationCodeFlow::~QOAuth2AuthorizationCodeFlow() +{} + +QString QOAuth2AuthorizationCodeFlow::responseType() const +{ + return QStringLiteral("code"); +} + +QUrl QOAuth2AuthorizationCodeFlow::accessTokenUrl() const +{ + Q_D(const QOAuth2AuthorizationCodeFlow); + return d->accessTokenUrl; +} + +void QOAuth2AuthorizationCodeFlow::setAccessTokenUrl(const QUrl &accessTokenUrl) +{ + Q_D(QOAuth2AuthorizationCodeFlow); + if (d->accessTokenUrl != accessTokenUrl) { + d->accessTokenUrl = accessTokenUrl; + Q_EMIT accessTokenUrlChanged(accessTokenUrl); + } +} + +void QOAuth2AuthorizationCodeFlow::grant() +{ + Q_D(QOAuth2AuthorizationCodeFlow); + if (d->authorizationUrl.isEmpty()) { + qWarning("QOAuth2AuthorizationCodeFlow::grant: No authenticate Url set"); + return; + } + if (d->accessTokenUrl.isEmpty()) { + qWarning("QOAuth2AuthorizationCodeFlow::grant: No request access token Url set"); + return; + } + + resourceOwnerAuthorization(d->authorizationUrl); +} + +void QOAuth2AuthorizationCodeFlow::refreshAccessToken() +{ + Q_D(QOAuth2AuthorizationCodeFlow); + + if (d->refreshToken.isEmpty()) { + qWarning("QOAuth2AuthorizationCodeFlow::refreshAccessToken: Cannot refresh access token. " + "Empty refresh token"); + return; + } + if (d->status == Status::RefreshingToken) { + qWarning("QOAuth2AuthorizationCodeFlow::refreshAccessToken: Cannot refresh access token. " + "Refresh Access Token in progress"); + return; + } + + using Key = QAbstractOAuth2Private::OAuth2KeyString; + + QVariantMap parameters; + QNetworkRequest request(d->accessTokenUrl); + QUrlQuery query; + parameters.insert(Key::grantType, QStringLiteral("refresh_token")); + parameters.insert(Key::refreshToken, d->refreshToken); + parameters.insert(Key::redirectUri, QUrl::toPercentEncoding(callback())); + query = QAbstractOAuthPrivate::createQuery(parameters); + request.setHeader(QNetworkRequest::ContentTypeHeader, + QStringLiteral("application/x-www-form-urlencoded")); + + const QString data = query.toString(QUrl::FullyEncoded); + d->currentReply = d->networkAccessManager()->post(request, data.toUtf8()); + d->status = Status::RefreshingToken; + + connect(d->currentReply.data(), &QNetworkReply::finished, + std::bind(&QAbstractOAuthReplyHandler::networkReplyFinished, replyHandler(), + d->currentReply.data())); + connect(d->currentReply.data(), &QNetworkReply::finished, d->currentReply.data(), + &QNetworkReply::deleteLater); + QObjectPrivate::connect(d->networkAccessManager(), + &QNetworkAccessManager::authenticationRequired, + d, &QOAuth2AuthorizationCodeFlowPrivate::_q_authenticate, + Qt::UniqueConnection); +} + +QUrl QOAuth2AuthorizationCodeFlow::buildAuthenticateUrl(const QVariantMap ¶meters) +{ + Q_D(QOAuth2AuthorizationCodeFlow); + using Key = QAbstractOAuth2Private::OAuth2KeyString; + + if (d->state.isEmpty()) + setState(QAbstractOAuth2Private::generateRandomState()); + Q_ASSERT(!d->state.isEmpty()); + const QString state = d->state; + + QVariantMap p(parameters); + QUrl url(d->authorizationUrl); + p.insert(Key::responseType, responseType()); + p.insert(Key::clientIdentifier, d->clientCredentials.first); + p.insert(Key::redirectUri, callback()); + p.insert(Key::scope, d->scope); + p.insert(Key::state, state); + if (d->modifyParametersFunction) + d->modifyParametersFunction(Stage::RequestingAuthorization, &p); + url.setQuery(d->createQuery(p)); + connect(d->replyHandler.data(), &QAbstractOAuthReplyHandler::callbackReceived, this, + &QOAuth2AuthorizationCodeFlow::authorizationCallbackReceived, Qt::UniqueConnection); + setStatus(QAbstractOAuth::Status::NotAuthenticated); + qDebug("QOAuth2AuthorizationCodeFlow::buildAuthenticateUrl: %s", qPrintable(url.toString())); + return url; +} + +void QOAuth2AuthorizationCodeFlow::requestAccessToken(const QString &code) +{ + Q_D(QOAuth2AuthorizationCodeFlow); + using Key = QAbstractOAuth2Private::OAuth2KeyString; + + QVariantMap parameters; + QNetworkRequest request(d->accessTokenUrl); + QUrlQuery query; + parameters.insert(Key::grantType, QStringLiteral("authorization_code")); + parameters.insert(Key::code, QUrl::toPercentEncoding(code)); + parameters.insert(Key::redirectUri, QUrl::toPercentEncoding(callback())); + parameters.insert(Key::clientIdentifier, QUrl::toPercentEncoding(d->clientCredentials.first)); + if (!d->clientCredentials.second.isEmpty()) + parameters.insert(Key::clientSharedSecret, d->clientCredentials.second); + if (d->modifyParametersFunction) + d->modifyParametersFunction(Stage::RequestingAccessToken, ¶meters); + query = QAbstractOAuthPrivate::createQuery(parameters); + request.setHeader(QNetworkRequest::ContentTypeHeader, + QStringLiteral("application/x-www-form-urlencoded")); + + const QString data = query.toString(QUrl::FullyEncoded); + d->currentReply = d->networkAccessManager()->post(request, data.toUtf8()); + QObject::connect(d->currentReply.data(), &QNetworkReply::finished, + std::bind(&QAbstractOAuthReplyHandler::networkReplyFinished, replyHandler(), + d->currentReply.data())); + QObjectPrivate::connect(d->replyHandler.data(), &QAbstractOAuthReplyHandler::tokensReceived, d, + &QOAuth2AuthorizationCodeFlowPrivate::_q_accessTokenRequestFinished, + Qt::UniqueConnection); + QObjectPrivate::connect(d->networkAccessManager(), + &QNetworkAccessManager::authenticationRequired, + d, &QOAuth2AuthorizationCodeFlowPrivate::_q_authenticate, + Qt::UniqueConnection); +} + +void QOAuth2AuthorizationCodeFlow::resourceOwnerAuthorization(const QUrl &url, + const QVariantMap ¶meters) +{ + Q_D(QOAuth2AuthorizationCodeFlow); + if (Q_UNLIKELY(url != d->authorizationUrl)) { + qWarning("Invalid URL: %s", qPrintable(url.toString())); + return; + } + const QUrl u = buildAuthenticateUrl(parameters); + QObjectPrivate::connect(this, &QOAuth2AuthorizationCodeFlow::authorizationCallbackReceived, d, + &QOAuth2AuthorizationCodeFlowPrivate::_q_handleCallback, + Qt::UniqueConnection); + Q_EMIT authorizeWithBrowser(u); +} + +QT_END_NAMESPACE + +#endif // QT_NO_HTTP diff --git a/src/oauth/qoauth2authorizationcodeflow.h b/src/oauth/qoauth2authorizationcodeflow.h new file mode 100644 index 0000000..a67873c --- /dev/null +++ b/src/oauth/qoauth2authorizationcodeflow.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOAUTH2AUTHORIZATIONCODEFLOW_H +#define QOAUTH2AUTHORIZATIONCODEFLOW_H + +#ifndef QT_NO_HTTP + +#include <QtNetworkAuth/qoauthglobal.h> +#include <QtNetworkAuth/qabstractoauth2.h> + +QT_BEGIN_NAMESPACE + +class QUrl; +class QString; +class QNetworkAccessManager; + +class QOAuth2AuthorizationCodeFlowPrivate; +class Q_OAUTH_EXPORT QOAuth2AuthorizationCodeFlow : public QAbstractOAuth2 +{ + Q_OBJECT + Q_PROPERTY(QUrl accessTokenUrl + READ accessTokenUrl + WRITE setAccessTokenUrl + NOTIFY accessTokenUrlChanged) + +public: + explicit QOAuth2AuthorizationCodeFlow(QObject *parent = nullptr); + explicit QOAuth2AuthorizationCodeFlow(QNetworkAccessManager *manager, + QObject *parent = nullptr); + + QOAuth2AuthorizationCodeFlow(const QString &clientIdentifier, + QNetworkAccessManager *manager, + QObject *parent = nullptr); + + QOAuth2AuthorizationCodeFlow(const QUrl &authorizationUrl, + const QUrl &accessTokenUrl, + QNetworkAccessManager *manager, + QObject *parent = nullptr); + + QOAuth2AuthorizationCodeFlow(const QString &clientIdentifier, + const QUrl &authorizationUrl, + const QUrl &accessTokenUrl, + QNetworkAccessManager *manager, + QObject *parent = nullptr); + + ~QOAuth2AuthorizationCodeFlow(); + + QString responseType() const override; + + QUrl accessTokenUrl() const; + void setAccessTokenUrl(const QUrl &accessTokenUrl); + +public Q_SLOTS: + void grant() override; + void refreshAccessToken(); + +protected: + QUrl buildAuthenticateUrl(const QVariantMap ¶meters = QVariantMap()); + void requestAccessToken(const QString &code); + void resourceOwnerAuthorization(const QUrl &url, + const QVariantMap ¶meters = QVariantMap()) override; + +Q_SIGNALS: + void accessTokenUrlChanged(const QUrl &accessTokenUrl); + +private: + Q_DISABLE_COPY(QOAuth2AuthorizationCodeFlow) + Q_DECLARE_PRIVATE(QOAuth2AuthorizationCodeFlow) +}; + +QT_END_NAMESPACE + +#endif // QT_NO_HTTP + +#endif // QOAUTH2AUTHORIZATIONCODEFLOW_H diff --git a/src/oauth/qoauth2authorizationcodeflow_p.h b/src/oauth/qoauth2authorizationcodeflow_p.h new file mode 100644 index 0000000..54a4c8d --- /dev/null +++ b/src/oauth/qoauth2authorizationcodeflow_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the Network Access API. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QOAUTH2AUTHORIZATIONCODEFLOW_P_H +#define QOAUTH2AUTHORIZATIONCODEFLOW_P_H + +#ifndef QT_NO_HTTP + +#include <private/qabstractoauth2_p.h> + +#include <QtNetworkAuth/qoauthglobal.h> +#include <QtNetworkAuth/qoauth2authorizationcodeflow.h> + +#include <QtCore/qstring.h> +#include <QtCore/qdatetime.h> + +QT_BEGIN_NAMESPACE + +class QOAuth2AuthorizationCodeFlowPrivate : public QAbstractOAuth2Private +{ + Q_DECLARE_PUBLIC(QOAuth2AuthorizationCodeFlow) + +public: + QOAuth2AuthorizationCodeFlowPrivate(const QUrl &authorizationUrl, + const QUrl &accessTokenUrl, + const QString &clientIdentifier, + QNetworkAccessManager *manager = nullptr); + QOAuth2AuthorizationCodeFlowPrivate(QNetworkAccessManager *manager = nullptr); + + void _q_handleCallback(const QVariantMap &data); + void _q_accessTokenRequestFinished(const QVariantMap &values); + void _q_authenticate(QNetworkReply *reply, QAuthenticator *authenticator); + + QUrl accessTokenUrl; + QString tokenType; + QPointer<QNetworkReply> currentReply; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_HTTP + +#endif // QOAUTH2AUTHORIZATIONCODEFLOW_P_H diff --git a/src/oauth/qoauthglobal.h b/src/oauth/qoauthglobal.h new file mode 100644 index 0000000..d84a01e --- /dev/null +++ b/src/oauth/qoauthglobal.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOAUTHGLOBAL_H +#define QOAUTHGLOBAL_H + +#include <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE + +#if defined(QT_SHARED) || !defined(QT_STATIC) +# if defined(QT_BUILD_NETWORKAUTH_LIB) +# define Q_OAUTH_EXPORT Q_DECL_EXPORT +# else +# define Q_OAUTH_EXPORT Q_DECL_IMPORT +# endif +#else +# define Q_OAUTH_EXPORT +#endif + +QT_END_NAMESPACE + +#endif // QOAUTHGLOBAL_H diff --git a/src/oauth/qoauthhttpserverreplyhandler.cpp b/src/oauth/qoauthhttpserverreplyhandler.cpp new file mode 100644 index 0000000..68fec3e --- /dev/null +++ b/src/oauth/qoauthhttpserverreplyhandler.cpp @@ -0,0 +1,329 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT_NO_HTTP + +#include <qabstractoauth.h> +#include <qoauthhttpserverreplyhandler.h> + +#include <private/qoauthhttpserverreplyhandler_p.h> + +#include <QtCore/qurl.h> +#include <QtCore/qurlquery.h> +#include <QtCore/qcoreapplication.h> + +#include <QtNetwork/qtcpsocket.h> +#include <QtNetwork/qnetworkreply.h> + +#include <cctype> +#include <cstring> +#include <functional> + +QT_BEGIN_NAMESPACE + +QOAuthHttpServerReplyHandlerPrivate::QOAuthHttpServerReplyHandlerPrivate( + QOAuthHttpServerReplyHandler *p) : + text(QObject::tr("Callback received. Feel free to close this page.")), q_ptr(p) +{ + QObject::connect(&httpServer, &QTcpServer::newConnection, + std::bind(&QOAuthHttpServerReplyHandlerPrivate::_q_clientConnected, this)); +} + +QOAuthHttpServerReplyHandlerPrivate::~QOAuthHttpServerReplyHandlerPrivate() +{ + if (httpServer.isListening()) + httpServer.close(); +} + +void QOAuthHttpServerReplyHandlerPrivate::_q_clientConnected() +{ + QTcpSocket *socket = httpServer.nextPendingConnection(); + + QObject::connect(socket, &QTcpSocket::disconnected, socket, &QTcpSocket::deleteLater); + QObject::connect(socket, &QTcpSocket::readyRead, + std::bind(&QOAuthHttpServerReplyHandlerPrivate::_q_readData, this, socket)); +} + +void QOAuthHttpServerReplyHandlerPrivate::_q_readData(QTcpSocket *socket) +{ + if (!clients.contains(socket)) + clients[socket].port = httpServer.serverPort(); + + QHttpRequest *request = &clients[socket]; + bool error; + + if (Q_LIKELY(request->state == QHttpRequest::State::ReadingMethod)) + if (Q_UNLIKELY(error = !request->readMethod(socket))) + qWarning("QOAuthHttpServerReplyHandlerPrivate::_q_readData: Invalid Method"); + + if (Q_LIKELY(!error && request->state == QHttpRequest::State::ReadingUrl)) + if (Q_UNLIKELY(error = !request->readUrl(socket))) + qWarning("QOAuthHttpServerReplyHandlerPrivate::_q_readData: Invalid URL"); + + if (Q_LIKELY(!error && request->state == QHttpRequest::State::ReadingStatus)) + if (Q_UNLIKELY(error = !request->readStatus(socket))) + qWarning("QOAuthHttpServerReplyHandlerPrivate::_q_readData: Invalid Status"); + + if (Q_LIKELY(!error && request->state == QHttpRequest::State::ReadingHeader)) + if (Q_UNLIKELY(error = !request->readHeader(socket))) + qWarning("QOAuthHttpServerReplyHandlerPrivate::_q_readData: Invalid Header"); + + if (error) { + socket->disconnectFromHost(); + clients.remove(socket); + } else if (!request->url.isEmpty()) { + Q_ASSERT(request->state != QHttpRequest::State::ReadingUrl); + _q_answerClient(socket, request->url); + clients.remove(socket); + } +} + +void QOAuthHttpServerReplyHandlerPrivate::_q_answerClient(QTcpSocket *socket, const QUrl &url) +{ + Q_Q(QOAuthHttpServerReplyHandler); + if (!url.path().startsWith(QStringLiteral("/cb"))) { + qWarning("QOAuthHttpServerReplyHandlerPrivate::_q_answerClient: Invalid request: %s", + qPrintable(url.toString())); + } else { + QVariantMap receivedData; + const QUrlQuery query(url.query()); + const auto items = query.queryItems(); + for (auto it = items.begin(), end = items.end(); it != end; ++it) + receivedData.insert(it->first, it->second); + Q_EMIT q->callbackReceived(receivedData); + + const QString html = QLatin1String("<html><head><title>") + + qApp->applicationName() + + QLatin1String("</title></head><body>") + + text + + QLatin1String("</body></html>"); + + const QByteArray htmlSize = QString::number(html.size()).toUtf8(); + const QByteArray replyMessage = QByteArrayLiteral("HTTP/1.0 200 OK \r\n" + "Content-Type: text/html; " + "charset=\"utf-8\"\r\n" + "Content-Length: ") + htmlSize + + QByteArrayLiteral("\r\n\r\n") + + html.toUtf8(); + + socket->write(replyMessage); + } + socket->disconnectFromHost(); +} + +bool QOAuthHttpServerReplyHandlerPrivate::QHttpRequest::readMethod(QTcpSocket *socket) +{ + bool finished = false; + while (socket->bytesAvailable() && !finished) { + const auto c = socket->read(1).at(0); + if (std::isupper(c) && fragment.size() < 6) + fragment += c; + else + finished = true; + } + if (finished) { + if (fragment == "HEAD") + method = Method::Head; + else if (fragment == "GET") + method = Method::Get; + else if (fragment == "PUT") + method = Method::Put; + else if (fragment == "POST") + method = Method::Post; + else if (fragment == "DELETE") + method = Method::Delete; + else + qWarning("QOAuthHttpServerReplyHandlerPrivate::QHttpRequest::readMethod: Invalid " + "operation %s", fragment.data()); + + state = State::ReadingUrl; + fragment.clear(); + + return method != Method::Unknown; + } + return true; +} + +bool QOAuthHttpServerReplyHandlerPrivate::QHttpRequest::readUrl(QTcpSocket *socket) +{ + bool finished = false; + while (socket->bytesAvailable() && !finished) { + const auto c = socket->read(1).at(0); + if (std::isspace(c)) + finished = true; + else + fragment += c; + } + if (finished) { + if (!fragment.startsWith("/")) { + qWarning("QOAuthHttpServerReplyHandlerPrivate::QHttpRequest::readUrl: Invalid " + "URL path %s", fragment.constData()); + return false; + } + url.setUrl(QStringLiteral("http://localhost:") + QString::number(port) + + QString::fromUtf8(fragment)); + state = State::ReadingStatus; + if (!url.isValid()) { + qWarning("QOAuthHttpServerReplyHandlerPrivate::QHttpRequest::readUrl: Invalid " + "URL %s", fragment.constData()); + return false; + } + fragment.clear(); + return true; + } + return true; +} + +bool QOAuthHttpServerReplyHandlerPrivate::QHttpRequest::readStatus(QTcpSocket *socket) +{ + bool finished = false; + while (socket->bytesAvailable() && !finished) { + fragment += socket->read(1); + if (fragment.endsWith("\r\n")) { + finished = true; + fragment.resize(fragment.size() - 2); + } + } + if (finished) { + if (!std::isdigit(fragment.at(fragment.size() - 3)) || + !std::isdigit(fragment.at(fragment.size() - 1))) { + qWarning("QOAuthHttpServerReplyHandlerPrivate::QHttpRequest::readStatus: Invalid " + "version"); + return false; + } + version = qMakePair(fragment.at(fragment.size() - 3) - '0', + fragment.at(fragment.size() - 1) - '0'); + state = State::ReadingHeader; + fragment.clear(); + } + return true; +} + +bool QOAuthHttpServerReplyHandlerPrivate::QHttpRequest::readHeader(QTcpSocket *socket) +{ + while (socket->bytesAvailable()) { + fragment += socket->read(1); + if (fragment.endsWith("\r\n")) { + if (fragment == "\r\n") { + state = State::ReadingBody; + fragment.clear(); + return true; + } else { + fragment.chop(2); + const int index = fragment.indexOf(':'); + if (index == -1) + return false; + + const QByteArray key = fragment.mid(0, index).trimmed(); + const QByteArray value = fragment.mid(index + 1).trimmed(); + headers.insert(key, value); + fragment.clear(); + } + } + } + return false; +} + +QOAuthHttpServerReplyHandler::QOAuthHttpServerReplyHandler(QObject *parent) : + QOAuthHttpServerReplyHandler(QHostAddress::Any, 0, parent) +{} + +QOAuthHttpServerReplyHandler::QOAuthHttpServerReplyHandler(quint16 port, QObject *parent) : + QOAuthHttpServerReplyHandler(QHostAddress::Any, port, parent) +{} + +QOAuthHttpServerReplyHandler::QOAuthHttpServerReplyHandler(const QHostAddress &address, + quint16 port, QObject *parent) : + QOAuthOobReplyHandler(parent), + d_ptr(new QOAuthHttpServerReplyHandlerPrivate(this)) +{ + listen(address, port); +} + +QOAuthHttpServerReplyHandler::~QOAuthHttpServerReplyHandler() +{} + +QString QOAuthHttpServerReplyHandler::callback() const +{ + Q_D(const QOAuthHttpServerReplyHandler); + + Q_ASSERT(d->httpServer.isListening()); + const QUrl url(QString::fromLatin1("http://localhost:%1/cb").arg(d->httpServer.serverPort())); + return url.toString(QUrl::EncodeDelimiters); +} + +QString QOAuthHttpServerReplyHandler::callbackText() const +{ + Q_D(const QOAuthHttpServerReplyHandler); + return d->text; +} + +void QOAuthHttpServerReplyHandler::setCallbackText(const QString &text) +{ + Q_D(QOAuthHttpServerReplyHandler); + d->text = text; +} + +quint16 QOAuthHttpServerReplyHandler::port() const +{ + Q_D(const QOAuthHttpServerReplyHandler); + return d->httpServer.serverPort(); +} + +bool QOAuthHttpServerReplyHandler::listen(const QHostAddress &address, quint16 port) +{ + Q_D(QOAuthHttpServerReplyHandler); + return d->httpServer.listen(address, port); +} + +void QOAuthHttpServerReplyHandler::close() +{ + Q_D(QOAuthHttpServerReplyHandler); + return d->httpServer.close(); +} + +bool QOAuthHttpServerReplyHandler::isListening() const +{ + Q_D(const QOAuthHttpServerReplyHandler); + return d->httpServer.isListening(); +} + +QT_END_NAMESPACE + +#endif // QT_NO_HTTP diff --git a/src/oauth/qoauthhttpserverreplyhandler.h b/src/oauth/qoauthhttpserverreplyhandler.h new file mode 100644 index 0000000..2437e08 --- /dev/null +++ b/src/oauth/qoauthhttpserverreplyhandler.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOAUTHHTTPSERVERREPLYHANDLER_H +#define QOAUTHHTTPSERVERREPLYHANDLER_H + +#ifndef QT_NO_HTTP + +#include <QtNetworkAuth/qoauthglobal.h> +#include <QtNetworkAuth/qoauthoobreplyhandler.h> + +#include <QtNetwork/qhostaddress.h> + +QT_BEGIN_NAMESPACE + +class QUrlQuery; + +class QOAuthHttpServerReplyHandlerPrivate; +class Q_OAUTH_EXPORT QOAuthHttpServerReplyHandler : public QOAuthOobReplyHandler +{ + Q_OBJECT + +public: + explicit QOAuthHttpServerReplyHandler(QObject *parent = nullptr); + explicit QOAuthHttpServerReplyHandler(quint16 port, QObject *parent = nullptr); + explicit QOAuthHttpServerReplyHandler(const QHostAddress &address, quint16 port, + QObject *parent = nullptr); + ~QOAuthHttpServerReplyHandler(); + + QString callback() const override; + + QString callbackText() const; + void setCallbackText(const QString &text); + + quint16 port() const; + + bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0); + void close(); + bool isListening() const; + +private: + Q_DECLARE_PRIVATE(QOAuthHttpServerReplyHandler) + QScopedPointer<QOAuthHttpServerReplyHandlerPrivate> d_ptr; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_HTTP + +#endif // QOAUTHHTTPSERVERREPLYHANDLER_H diff --git a/src/oauth/qoauthhttpserverreplyhandler_p.h b/src/oauth/qoauthhttpserverreplyhandler_p.h new file mode 100644 index 0000000..7e0be6b --- /dev/null +++ b/src/oauth/qoauthhttpserverreplyhandler_p.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the Network Access API. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QOAUTHHTTPSERVERREPLYHANDLER_P_H +#define QOAUTHHTTPSERVERREPLYHANDLER_P_H + +#ifndef QT_NO_HTTP + +#include <QtNetworkAuth/qoauthglobal.h> +#include <QtNetworkAuth/qoauthhttpserverreplyhandler.h> + +#include <private/qobject_p.h> + +#include <QtNetwork/qtcpserver.h> + +QT_BEGIN_NAMESPACE + +class QOAuthHttpServerReplyHandlerPrivate +{ + Q_DECLARE_PUBLIC(QOAuthHttpServerReplyHandler) + +public: + explicit QOAuthHttpServerReplyHandlerPrivate(QOAuthHttpServerReplyHandler *p); + ~QOAuthHttpServerReplyHandlerPrivate(); + + QTcpServer httpServer; + QString text; + QHostAddress listenAddress = QHostAddress::LocalHost; + +private: + void _q_clientConnected(); + void _q_readData(QTcpSocket *socket); + void _q_answerClient(QTcpSocket *socket, const QUrl &url); + + struct QHttpRequest { + quint16 port = 0; + + bool readMethod(QTcpSocket *socket); + bool readUrl(QTcpSocket *socket); + bool readStatus(QTcpSocket *socket); + bool readHeader(QTcpSocket *socket); + + enum class State { + ReadingMethod, + ReadingUrl, + ReadingStatus, + ReadingHeader, + ReadingBody, + AllDone + } state = State::ReadingMethod; + QByteArray fragment; + + enum class Method { + Unknown, + Head, + Get, + Put, + Post, + Delete, + } method = Method::Unknown; + QUrl url; + QPair<quint8, quint8> version; + QMap<QByteArray, QByteArray> headers; + }; + + QMap<QTcpSocket *, QHttpRequest> clients; + + QOAuthHttpServerReplyHandler *q_ptr; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_HTTP + +#endif // QOAUTHHTTPSERVERREPLYHANDLER_P_H diff --git a/src/oauth/qoauthoobreplyhandler.cpp b/src/oauth/qoauthoobreplyhandler.cpp new file mode 100644 index 0000000..c4e0bd8 --- /dev/null +++ b/src/oauth/qoauthoobreplyhandler.cpp @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT_NO_HTTP + +#include "qoauthoobreplyhandler.h" + +#include <QtCore/qurlquery.h> +#include <QtCore/qjsonobject.h> +#include <QtCore/qjsondocument.h> + +#include <QtNetwork/qnetworkreply.h> + +QT_BEGIN_NAMESPACE + +QOAuthOobReplyHandler::QOAuthOobReplyHandler(QObject *parent) + : QAbstractOAuthReplyHandler(parent) +{} + +QString QOAuthOobReplyHandler::callback() const +{ + return QStringLiteral("oob"); +} + +void QOAuthOobReplyHandler::networkReplyFinished(QNetworkReply *reply) +{ + if (reply->error() != QNetworkReply::NoError) { + qWarning("QOAuthOobReplyHandler::networkReplyFinished: %s", + qPrintable(reply->errorString())); + return; + } + if (reply->header(QNetworkRequest::ContentTypeHeader).isNull()) { + qWarning("QOAuthOobReplyHandler::networkReplyFinished: Empty Content-type header"); + return; + } + const QString contentType = reply->header(QNetworkRequest::ContentTypeHeader).isNull() ? + QStringLiteral("text/html") : + reply->header(QNetworkRequest::ContentTypeHeader).toString(); + const QByteArray data = reply->readAll(); + if (data.isEmpty()) { + qWarning("QOAuthOobReplyHandler::networkReplyFinished: No received data"); + return; + } + + Q_EMIT replyDataReceived(data); + + QVariantMap ret; + + if (contentType.startsWith(QStringLiteral("text/html")) || + contentType.startsWith(QStringLiteral("application/x-www-form-urlencoded"))) { + ret = parseResponse(data); + } else if (contentType.startsWith(QStringLiteral("application/json"))) { + const QJsonDocument document = QJsonDocument::fromJson(data); + if (!document.isObject()) { + qWarning("QOAuthOobReplyHandler::networkReplyFinished: Received data is not a JSON" + "object: %s", qPrintable(QString::fromUtf8(data))); + return; + } + const QJsonObject object = document.object(); + if (object.isEmpty()) { + qWarning("QOAuthOobReplyHandler::networkReplyFinished: Received empty JSON object: %s", + qPrintable(QString::fromUtf8(data))); + } + ret = object.toVariantMap(); + } else { + qWarning("QOAuthOobReplyHandler::networkReplyFinished: Unknown Content-type: %s", + qPrintable(contentType)); + return; + } + + Q_EMIT tokensReceived(ret); +} + +QVariantMap QOAuthOobReplyHandler::parseResponse(const QByteArray &response) +{ + QVariantMap ret; + QUrlQuery query(QString::fromUtf8(response)); + auto queryItems = query.queryItems(QUrl::FullyDecoded); + for (auto it = queryItems.begin(), end = queryItems.end(); it != end; ++it) + ret.insert(it->first, it->second); + return ret; +} + +QT_END_NAMESPACE + +#endif // QT_NO_HTTP diff --git a/src/oauth/qoauthoobreplyhandler.h b/src/oauth/qoauthoobreplyhandler.h new file mode 100644 index 0000000..1ca246c --- /dev/null +++ b/src/oauth/qoauthoobreplyhandler.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOAUTHOOBREPLYHANDLER_H +#define QOAUTHOOBREPLYHANDLER_H + +#ifndef QT_NO_HTTP + +#include <QtNetworkAuth/qoauthglobal.h> +#include <QtNetworkAuth/qabstractoauthreplyhandler.h> + +QT_BEGIN_NAMESPACE + +class Q_OAUTH_EXPORT QOAuthOobReplyHandler : public QAbstractOAuthReplyHandler +{ + Q_OBJECT + +public: + explicit QOAuthOobReplyHandler(QObject *parent = nullptr); + + QString callback() const override; + +protected: + void networkReplyFinished(QNetworkReply *reply) override; + +private: + QVariantMap parseResponse(const QByteArray &response); +}; + +QT_END_NAMESPACE + +#endif // QT_NO_HTTP + +#endif // QOAUTHOOBREPLYHANDLER_H diff --git a/src/src.pro b/src/src.pro new file mode 100644 index 0000000..10821d5 --- /dev/null +++ b/src/src.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs +CONFIG += ordered + +SUBDIRS += oauth |
