diff options
-rw-r--r-- | .qmake.conf | 2 | ||||
-rw-r--r-- | src/oauth/qabstractoauth.cpp | 24 | ||||
-rw-r--r-- | src/oauth/qabstractoauth.h | 5 | ||||
-rw-r--r-- | src/oauth/qabstractoauth2.cpp | 11 | ||||
-rw-r--r-- | src/oauth/qabstractoauth2_p.h | 4 | ||||
-rw-r--r-- | src/oauth/qabstractoauth_p.h | 4 | ||||
-rw-r--r-- | src/oauth/qoauth1.cpp | 95 | ||||
-rw-r--r-- | src/oauth/qoauth1.h | 3 | ||||
-rw-r--r-- | src/oauth/qoauth1_p.h | 12 | ||||
-rw-r--r-- | src/oauth/qoauth1signature.cpp | 39 | ||||
-rw-r--r-- | src/oauth/qoauth1signature.h | 3 | ||||
-rw-r--r-- | src/oauth/qoauth1signature_p.h | 1 | ||||
-rw-r--r-- | tests/auto/abstractoauth/tst_abstractoauth.cpp | 11 | ||||
-rw-r--r-- | tests/auto/oauth1/BLACKLIST | 2 | ||||
-rw-r--r-- | tests/auto/oauth1/tst_oauth1.cpp | 269 | ||||
-rw-r--r-- | tests/auto/oauth1signature/tst_oauth1signature.cpp | 35 | ||||
-rw-r--r-- | tests/auto/oauth2/tst_oauth2.cpp | 11 |
17 files changed, 511 insertions, 20 deletions
diff --git a/.qmake.conf b/.qmake.conf index b69e88b..a0d7ee5 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -2,4 +2,4 @@ load(qt_build_config) CONFIG += warning_clean -MODULE_VERSION = 5.12.3 +MODULE_VERSION = 5.13.0 diff --git a/src/oauth/qabstractoauth.cpp b/src/oauth/qabstractoauth.cpp index 5326292..a7a6c29 100644 --- a/src/oauth/qabstractoauth.cpp +++ b/src/oauth/qabstractoauth.cpp @@ -44,6 +44,7 @@ #include <QtNetwork/qnetworkrequest.h> #include <QtNetwork/qnetworkaccessmanager.h> +#include <QtNetwork/qnetworkreply.h> #include <random> @@ -91,7 +92,7 @@ QT_BEGIN_NAMESPACE \enum QAbstractOAuth::Stage Identifies an authentication stage. It's passed to a - ModifyParametersFunction so that it can make different changes to + modifyParametersFunction so that it can make different changes to parameters at each call to it during the process of authentication. @@ -510,8 +511,25 @@ void QAbstractOAuth::setReplyHandler(QAbstractOAuthReplyHandler *handler) } /*! + \since 5.13 + + Authorizes the given \a request by adding a header and \a body to + it required for authenticated requests. + + The \a verb must be a valid HTTP verb and the same as the one that will be + used to send the \a request. +*/ +void QAbstractOAuth::prepareRequest(QNetworkRequest *request, + const QByteArray &verb, + const QByteArray &body) +{ + Q_D(QAbstractOAuth); + d->prepareRequestImpl(request, verb, body); +} + +/*! Returns the current parameter-modification function. - \sa ModifyParametersFunction, setModifyParametersFunction(), Stage + \sa QAbstractOAuth::ModifyParametersFunction, setModifyParametersFunction(), Stage */ QAbstractOAuth::ModifyParametersFunction QAbstractOAuth::modifyParametersFunction() const { @@ -524,7 +542,7 @@ QAbstractOAuth::ModifyParametersFunction QAbstractOAuth::modifyParametersFunctio This function is used to customize the parameters sent to the server during a specified authorization stage. The number of calls to this function depends on the flow used during the authentication. - \sa modifyParametersFunction(), ModifyParametersFunction, Stage + \sa modifyParametersFunction(), Stage */ void QAbstractOAuth::setModifyParametersFunction( const QAbstractOAuth::ModifyParametersFunction &modifyParametersFunction) diff --git a/src/oauth/qabstractoauth.h b/src/oauth/qabstractoauth.h index ab1310b..bb610a8 100644 --- a/src/oauth/qabstractoauth.h +++ b/src/oauth/qabstractoauth.h @@ -138,6 +138,11 @@ public: Q_INVOKABLE virtual QNetworkReply *deleteResource( const QUrl &url, const QVariantMap ¶meters = QVariantMap()) = 0; + // ### Qt 6: Make this method pure virtual and remove the private implementation + void prepareRequest(QNetworkRequest *request, + const QByteArray &verb, + const QByteArray &body = QByteArray()); + ModifyParametersFunction modifyParametersFunction() const; void setModifyParametersFunction(const ModifyParametersFunction &modifyParametersFunction); diff --git a/src/oauth/qabstractoauth2.cpp b/src/oauth/qabstractoauth2.cpp index 96d338d..cd428ad 100644 --- a/src/oauth/qabstractoauth2.cpp +++ b/src/oauth/qabstractoauth2.cpp @@ -169,6 +169,17 @@ QNetworkRequest QAbstractOAuth2Private::createRequest(QUrl url, const QVariantMa return request; } +void QAbstractOAuth2Private::prepareRequestImpl(QNetworkRequest *request, + const QByteArray &verb, + const QByteArray &body) +{ + Q_UNUSED(verb) + Q_UNUSED(body) + request->setHeader(QNetworkRequest::UserAgentHeader, userAgent); + const QString bearer = bearerFormat.arg(token); + request->setRawHeader("Authorization", bearer.toUtf8()); +} + /*! Constructs a QAbstractOAuth2 object using \a parent as parent. */ diff --git a/src/oauth/qabstractoauth2_p.h b/src/oauth/qabstractoauth2_p.h index b4a7b04..4dea113 100644 --- a/src/oauth/qabstractoauth2_p.h +++ b/src/oauth/qabstractoauth2_p.h @@ -70,6 +70,10 @@ public: static QString generateRandomState(); QNetworkRequest createRequest(QUrl url, const QVariantMap *parameters = nullptr); + void prepareRequestImpl(QNetworkRequest *request, + const QByteArray &verb, + const QByteArray &body) override; + QString clientIdentifierSharedKey; QString scope; QString state = generateRandomState(); diff --git a/src/oauth/qabstractoauth_p.h b/src/oauth/qabstractoauth_p.h index 4ec3802..58c4b21 100644 --- a/src/oauth/qabstractoauth_p.h +++ b/src/oauth/qabstractoauth_p.h @@ -77,6 +77,10 @@ public: void setStatus(QAbstractOAuth::Status status); static QByteArray generateRandomString(quint8 length); + virtual void prepareRequestImpl(QNetworkRequest *request, + const QByteArray &verb, + const QByteArray &body) = 0; + const QLoggingCategory loggingCategory; QString clientIdentifier; QString token; diff --git a/src/oauth/qoauth1.cpp b/src/oauth/qoauth1.cpp index 9713a30..efc7712 100644 --- a/src/oauth/qoauth1.cpp +++ b/src/oauth/qoauth1.cpp @@ -243,12 +243,29 @@ QByteArray QOAuth1Private::generateSignature(const QVariantMap ¶meters, const QUrl &url, QNetworkAccessManager::Operation operation) const { - const QOAuth1Signature signature(url, - clientIdentifierSharedKey, - tokenSecret, - static_cast<QOAuth1Signature::HttpRequestMethod>(operation), - parameters); + QOAuth1Signature signature(url, + clientIdentifierSharedKey, + tokenSecret, + static_cast<QOAuth1Signature::HttpRequestMethod>(operation), + parameters); + return formatSignature(signature); +} + +QByteArray QOAuth1Private::generateSignature(const QVariantMap ¶meters, + const QUrl &url, + const QByteArray &verb) const +{ + QOAuth1Signature signature(url, + clientIdentifierSharedKey, + tokenSecret, + QOAuth1Signature::HttpRequestMethod::Custom, + parameters); + signature.setCustomMethodString(verb); + return formatSignature(signature); +} +QByteArray QOAuth1Private::formatSignature(const QOAuth1Signature &signature) const +{ switch (signatureMethod) { case QOAuth1::SignatureMethod::Hmac_Sha1: return signature.hmacSha1().toBase64(); @@ -260,6 +277,38 @@ QByteArray QOAuth1Private::generateSignature(const QVariantMap ¶meters, } } +QVariantMap QOAuth1Private::createOAuthBaseParams() const +{ + QVariantMap oauthParams; + + const auto currentDateTime = QDateTime::currentDateTimeUtc(); + + oauthParams.insert(Key::oauthConsumerKey, clientIdentifier); + oauthParams.insert(Key::oauthVersion, QStringLiteral("1.0")); + oauthParams.insert(Key::oauthToken, token); + oauthParams.insert(Key::oauthSignatureMethod, signatureMethodString()); + oauthParams.insert(Key::oauthNonce, QOAuth1::nonce()); + oauthParams.insert(Key::oauthTimestamp, QString::number(currentDateTime.toTime_t())); + + return oauthParams; +} + +void QOAuth1Private::prepareRequestImpl(QNetworkRequest *request, + const QByteArray &verb, + const QByteArray &body) +{ + Q_Q(QOAuth1); + QVariantMap signingParams; + if (verb == "POST" && + request->header(QNetworkRequest::ContentTypeHeader).toByteArray() + == "application/x-www-form-urlencoded") { + QUrlQuery query(QString::fromUtf8(body)); + for (const auto &item : query.queryItems(QUrl::FullyDecoded)) + signingParams.insert(item.first, item.second); + } + q->setup(request, signingParams, verb); +} + void QOAuth1Private::_q_onTokenRequestError(QNetworkReply::NetworkError error) { Q_Q(QOAuth1); @@ -701,6 +750,8 @@ QNetworkReply *QOAuth1::requestTokenCredentials(QNetworkAccessManager::Operation /*! Signs the \a request using \a signingParameters and \a operation. + + \overload */ void QOAuth1::setup(QNetworkRequest *request, const QVariantMap &signingParameters, @@ -708,16 +759,7 @@ void QOAuth1::setup(QNetworkRequest *request, { Q_D(const QOAuth1); - QVariantMap oauthParams; - - const auto currentDateTime = QDateTime::currentDateTimeUtc(); - - oauthParams.insert(Key::oauthConsumerKey, d->clientIdentifier); - oauthParams.insert(Key::oauthVersion, QStringLiteral("1.0")); - oauthParams.insert(Key::oauthToken, d->token); - oauthParams.insert(Key::oauthSignatureMethod, d->signatureMethodString()); - oauthParams.insert(Key::oauthNonce, QOAuth1::nonce()); - oauthParams.insert(Key::oauthTimestamp, QString::number(currentDateTime.toTime_t())); + auto oauthParams = d->createOAuthBaseParams(); // Add signature parameter { @@ -747,6 +789,29 @@ void QOAuth1::setup(QNetworkRequest *request, } /*! + \since 5.13 + + Signs the \a request using \a signingParameters and \a operationVerb. + + \overload +*/ +void QOAuth1::setup(QNetworkRequest *request, const QVariantMap &signingParameters, const QByteArray &operationVerb) +{ + Q_D(const QOAuth1); + + auto oauthParams = d->createOAuthBaseParams(); + + // Add signature parameter + { + const auto parameters = QVariantMap(oauthParams).unite(signingParameters); + const auto signature = d->generateSignature(parameters, request->url(), operationVerb); + oauthParams.insert(Key::oauthSignature, signature); + } + + request->setRawHeader("Authorization", generateAuthorizationHeader(oauthParams)); +} + +/*! Generates a nonce. \b {See also}: \l {https://tools.ietf.org/html/rfc5849#section-3.3} diff --git a/src/oauth/qoauth1.h b/src/oauth/qoauth1.h index ef4f04e..20efce7 100644 --- a/src/oauth/qoauth1.h +++ b/src/oauth/qoauth1.h @@ -120,6 +120,9 @@ protected: void setup(QNetworkRequest *request, const QVariantMap &signingParameters, QNetworkAccessManager::Operation operation); + void setup(QNetworkRequest *request, + const QVariantMap &signingParameters, + const QByteArray &operationVerb); static QByteArray nonce(); static QByteArray generateAuthorizationHeader(const QVariantMap &oauthParams); diff --git a/src/oauth/qoauth1_p.h b/src/oauth/qoauth1_p.h index ee57fba..f60db5b 100644 --- a/src/oauth/qoauth1_p.h +++ b/src/oauth/qoauth1_p.h @@ -58,6 +58,8 @@ QT_BEGIN_NAMESPACE +class QOAuth1Signature; + class QOAuth1Private : public QAbstractOAuthPrivate { Q_DECLARE_PUBLIC(QOAuth1) @@ -82,6 +84,16 @@ public: QByteArray generateSignature(const QVariantMap ¶meters, const QUrl &url, QNetworkAccessManager::Operation operation) const; + QByteArray generateSignature(const QVariantMap ¶meters, + const QUrl &url, + const QByteArray &verb) const; + QByteArray formatSignature(const QOAuth1Signature &signature) const; + + QVariantMap createOAuthBaseParams() const; + + void prepareRequestImpl(QNetworkRequest *request, + const QByteArray &verb, + const QByteArray &body) override; void _q_onTokenRequestError(QNetworkReply::NetworkError error); void _q_tokensReceived(const QVariantMap &tokens); diff --git a/src/oauth/qoauth1signature.cpp b/src/oauth/qoauth1signature.cpp index 46d8874..aec2de3 100644 --- a/src/oauth/qoauth1signature.cpp +++ b/src/oauth/qoauth1signature.cpp @@ -126,6 +126,14 @@ QByteArray QOAuth1SignaturePrivate::signatureBaseString() const case QOAuth1Signature::HttpRequestMethod::Delete: base.append("DELETE"); break; + case QOAuth1Signature::HttpRequestMethod::Custom: + if (!customVerb.isEmpty()) { + base.append(customVerb); + } else { + qCCritical(loggingCategory, "QOAuth1Signature: HttpRequestMethod::Custom requires " + "the verb to be set via setCustomMethodString"); + } + break; default: qCCritical(loggingCategory, "QOAuth1Signature: HttpRequestMethod not supported"); } @@ -244,6 +252,37 @@ void QOAuth1Signature::setHttpRequestMethod(QOAuth1Signature::HttpRequestMethod } /*! + \since 5.13 + + Returns the custom method string. + + \sa httpRequestMethod() +*/ +QByteArray QOAuth1Signature::customMethodString() const +{ + return d->customVerb; +} + +/*! + \since 5.13 + + Sets a custom request method. Will set the httpRequestMethod + to QOAuth1Signature::HttpRequestMethod::Custom and store the + \a verb to use it for the generation of the signature. + + \note Using this method is required when working with custom verbs. + Setting only the request method will fail, as the signure needs to + know the actual verb. + + \sa setHttpRequestMethod(), HttpRequestMethod +*/ +void QOAuth1Signature::setCustomMethodString(const QByteArray &verb) +{ + d->method = QOAuth1Signature::HttpRequestMethod::Custom; + d->customVerb = verb; +} + +/*! Returns the URL. */ QUrl QOAuth1Signature::url() const diff --git a/src/oauth/qoauth1signature.h b/src/oauth/qoauth1signature.h index 6890974..a75cfc0 100644 --- a/src/oauth/qoauth1signature.h +++ b/src/oauth/qoauth1signature.h @@ -70,6 +70,9 @@ public: HttpRequestMethod httpRequestMethod() const; void setHttpRequestMethod(HttpRequestMethod method); + QByteArray customMethodString() const; + void setCustomMethodString(const QByteArray &verb); + QUrl url() const; void setUrl(const QUrl &url); diff --git a/src/oauth/qoauth1signature_p.h b/src/oauth/qoauth1signature_p.h index d4b7104..cc648d9 100644 --- a/src/oauth/qoauth1signature_p.h +++ b/src/oauth/qoauth1signature_p.h @@ -66,6 +66,7 @@ public: QOAuth1Signature::HttpRequestMethod method = QOAuth1Signature::HttpRequestMethod::Post; + QByteArray customVerb; QUrl url; QString clientSharedKey; QString tokenSecret; diff --git a/tests/auto/abstractoauth/tst_abstractoauth.cpp b/tests/auto/abstractoauth/tst_abstractoauth.cpp index b422dc9..775f930 100644 --- a/tests/auto/abstractoauth/tst_abstractoauth.cpp +++ b/tests/auto/abstractoauth/tst_abstractoauth.cpp @@ -39,8 +39,17 @@ class tst_AbstractOAuth : public QObject Q_OBJECT private: + struct AbstractOAuthPrivate : public QAbstractOAuthPrivate { + AbstractOAuthPrivate() : QAbstractOAuthPrivate("", QUrl(), QString(), nullptr) + {} + + void prepareRequestImpl(QNetworkRequest *, + const QByteArray &, + const QByteArray &) override {} + }; + struct AbstractOAuth : QAbstractOAuth { - AbstractOAuth() : QAbstractOAuth(*new QAbstractOAuthPrivate("", QUrl(), QString(), nullptr), + AbstractOAuth() : QAbstractOAuth(*new AbstractOAuthPrivate(), nullptr) {} diff --git a/tests/auto/oauth1/BLACKLIST b/tests/auto/oauth1/BLACKLIST index 44de921..3f74a03 100644 --- a/tests/auto/oauth1/BLACKLIST +++ b/tests/auto/oauth1/BLACKLIST @@ -15,3 +15,5 @@ windows * [authenticatedCalls] * +[prepareRequestCalls] +* diff --git a/tests/auto/oauth1/tst_oauth1.cpp b/tests/auto/oauth1/tst_oauth1.cpp index dbc793c..eb13420 100644 --- a/tests/auto/oauth1/tst_oauth1.cpp +++ b/tests/auto/oauth1/tst_oauth1.cpp @@ -155,15 +155,29 @@ private Q_SLOTS: void getToken_data(); void getToken(); + void prepareRequestSignature_data(); + void prepareRequestSignature(); + void grant_data(); void grant(); void authenticatedCalls_data(); void authenticatedCalls(); + void prepareRequestCalls_data(); + void prepareRequestCalls(); + void secondTemporaryToken(); }; +const auto oauthVersion = QStringLiteral("oauth_version"); +const auto oauthConsumerKey = QStringLiteral("oauth_consumer_key"); +const auto oauthNonce = QStringLiteral("oauth_nonce"); +const auto oauthSignatureMethod = QStringLiteral("oauth_signature_method"); +const auto oauthTimestamp = QStringLiteral("oauth_timestamp"); +const auto oauthToken = QStringLiteral("oauth_token"); +const auto oauthSignature = QStringLiteral("oauth_signature"); + bool hostReachable(const QLatin1String &host) { // check host exists @@ -476,6 +490,118 @@ void tst_OAuth1::getToken() QCOMPARE(oauthHeaders["oauth_signature"], expectedSignature); } +void tst_OAuth1::prepareRequestSignature_data() +{ + QTest::addColumn<QString>("consumerKey"); + QTest::addColumn<QString>("consumerSecret"); + QTest::addColumn<QString>("accessKey"); + QTest::addColumn<QString>("accessKeySecret"); + QTest::addColumn<QNetworkRequest>("request"); + QTest::addColumn<QByteArray>("operation"); + QTest::addColumn<QByteArray>("body"); + QTest::addColumn<QVariantMap>("extraParams"); + + QTest::newRow("get_simple") + << "key" + << "secret" + << "accesskey" + << "accesssecret" + << QNetworkRequest(QUrl("http://term.ie/oauth/example/echo_api.php")) + << QByteArray("GET") + << QByteArray() + << QVariantMap(); + + QTest::newRow("get_params") + << "key" + << "secret" + << "accesskey" + << "accesssecret" + << QNetworkRequest(QUrl("http://term.ie/oauth/example/echo_api.php?" + "first=first&second=second")) + << QByteArray("GET") + << QByteArray() + << QVariantMap(); + + QNetworkRequest postRequest(QUrl("http://term.ie/oauth/example/echo_api.php")); + postRequest.setHeader(QNetworkRequest::ContentTypeHeader, + QByteArray("application/x-www-form-urlencoded")); + QTest::newRow("post_params") + << "key" + << "secret" + << "accesskey" + << "accesssecret" + << postRequest + << QByteArray("POST") + << QByteArray("first=first&second=second") + << QVariantMap({ + {"first", "first"}, + {"second", "second"} + }); + + QTest::newRow("patch_param") + << "key" + << "secret" + << "accesskey" + << "accesssecret" + << QNetworkRequest(QUrl("http://term.ie/oauth/example/echo_api.php?" + "first=first&second=second")) + << QByteArray("PATCH") + << QByteArray() + << QVariantMap(); +} + +void tst_OAuth1::prepareRequestSignature() +{ + QFETCH(QString, consumerKey); + QFETCH(QString, consumerSecret); + QFETCH(QString, accessKey); + QFETCH(QString, accessKeySecret); + QFETCH(QNetworkRequest, request); + QFETCH(QByteArray, operation); + QFETCH(QByteArray, body); + QFETCH(QVariantMap, extraParams); + + QOAuth1 o1; + o1.setClientCredentials(consumerKey, consumerSecret); + o1.setTokenCredentials(accessKey, accessKeySecret); + + o1.prepareRequest(&request, operation, body); + + // extract oauth parameters from the headers + QVariantMap authArgs; + const auto authHeader = request.rawHeader("Authorization"); + QCOMPARE(authHeader.mid(0, 6), "OAuth "); + const auto values = authHeader.mid(6).split(','); + for (const auto &pair : values) { + const auto argPair = pair.split('='); + QCOMPARE(argPair.size(), 2); + QCOMPARE(argPair[1].front(), '\"'); + QCOMPARE(argPair[1].back(), '\"'); + authArgs.insert(argPair[0], argPair[1].mid(1, argPair[1].size() - 2)); + } + + //compare known parameters + QCOMPARE(authArgs.value(oauthConsumerKey).toByteArray(), consumerKey); + QCOMPARE(authArgs.value(oauthToken).toByteArray(), accessKey); + QCOMPARE(authArgs.value(oauthSignatureMethod).toByteArray(), QByteArray("HMAC-SHA1")); + QCOMPARE(authArgs.value(oauthVersion).toByteArray(), QByteArray("1.0")); + QVERIFY(authArgs.contains(oauthNonce)); + QVERIFY(authArgs.contains(oauthTimestamp)); + QVERIFY(authArgs.contains(oauthSignature)); + + // verify the signature + const auto sigString = QUrl::fromPercentEncoding(authArgs.take(oauthSignature) + .toByteArray()).toUtf8(); + QOAuth1Signature signature(request.url(), + consumerSecret, + accessKeySecret, + QOAuth1Signature::HttpRequestMethod::Custom, + authArgs.unite(extraParams)); + signature.setCustomMethodString(operation); + const auto signatureData = signature.hmacSha1(); + QCOMPARE(signatureData.toBase64(), sigString); +} + void tst_OAuth1::grant_data() { QTest::addColumn<QString>("consumerKey"); @@ -713,6 +839,149 @@ void tst_OAuth1::authenticatedCalls() reply.clear(); } +void tst_OAuth1::prepareRequestCalls_data() +{ + QTest::addColumn<QString>("consumerKey"); + QTest::addColumn<QString>("consumerSecret"); + QTest::addColumn<QString>("accessKey"); + QTest::addColumn<QString>("accessKeySecret"); + QTest::addColumn<QUrl>("url"); + QTest::addColumn<QVariantMap>("parameters"); + QTest::addColumn<QByteArray>("operation"); + + const QVariantMap parameters { { QStringLiteral("first"), QStringLiteral("first") }, + { QStringLiteral("second"), QStringLiteral("second") }, + { QStringLiteral("third"), QStringLiteral("third") }, + { QStringLiteral("c2&a3"), QStringLiteral("2=%$&@q") } + }; + + if (hostReachable(QLatin1String("term.ie"))) { + QTest::newRow("term.ie_get") << "key" + << "secret" + << "accesskey" + << "accesssecret" + << QUrl("http://term.ie/oauth/example/echo_api.php") + << parameters + << QByteArray("GET"); + QTest::newRow("term.ie_post") << "key" + << "secret" + << "accesskey" + << "accesssecret" + << QUrl("http://term.ie/oauth/example/echo_api.php") + << parameters + << QByteArray("POST"); + QTest::newRow("term.ie_percent_encoded_query") + << "key" + << "secret" + << "accesskey" + << "accesssecret" + << QUrl("http://term.ie/oauth/example/echo_api.php?key=%40value+1%2B2=3") + << parameters + << QByteArray("GET"); + } + if (hostReachable(QLatin1String("oauthbin.com"))) { + QTest::newRow("oauthbin.com_get") << "key" + << "secret" + << "accesskey" + << "accesssecret" + << QUrl("http://oauthbin.com/v1/echo") + << parameters + << QByteArray("GET"); + QTest::newRow("oauthbin.com_post") << "key" + << "secret" + << "accesskey" + << "accesssecret" + << QUrl("http://oauthbin.com/v1/echo") + << parameters + << QByteArray("POST"); + QTest::newRow("oauthbin.com_percent_encoded_query") + << "key" + << "secret" + << "accesskey" + << "accesssecret" + << QUrl("http://oauthbin.com/v1/echo?key=%40value+1%2B2=3") + << parameters + << QByteArray("GET"); + QTest::newRow("oauthbin.com_patch") << "key" + << "secret" + << "accesskey" + << "accesssecret" + << QUrl("http://oauthbin.com/v1/echo") + << parameters + << QByteArray("PATCH"); + } +} + +void tst_OAuth1::prepareRequestCalls() +{ + QFETCH(QString, consumerKey); + QFETCH(QString, consumerSecret); + QFETCH(QString, accessKey); + QFETCH(QString, accessKeySecret); + QFETCH(QUrl, url); + QFETCH(QVariantMap, parameters); + QFETCH(QByteArray, operation); + + QNetworkAccessManager networkAccessManager; + QNetworkReplyPtr reply; + QString receivedData; + QString parametersString; + { + if (url.hasQuery()) { + parametersString = url.query(QUrl::FullyDecoded); + if (!parameters.empty()) + parametersString.append(QLatin1Char('&')); + } + bool first = true; + for (auto it = parameters.begin(), end = parameters.end(); it != end; ++it) { + if (first) + first = false; + else + parametersString += QLatin1Char('&'); + parametersString += it.key() + QLatin1Char('=') + it.value().toString(); + } + } + + QOAuth1 o1(&networkAccessManager); + o1.setClientCredentials(consumerKey, consumerSecret); + o1.setTokenCredentials(accessKey, accessKeySecret); + + if (operation != "POST") { + QUrlQuery query(url.query()); + for (auto it = parameters.begin(), end = parameters.end(); it != end; ++it) + query.addQueryItem(it.key(), it.value().toString()); + url.setQuery(query); + } + QNetworkRequest request(url); + QByteArray body; + if (operation == "POST") { + QUrlQuery query(url.query()); + for (auto it = parameters.begin(), end = parameters.end(); it != end; ++it) + query.addQueryItem(it.key(), it.value().toString()); + body = query.toString().toUtf8(); + request.setHeader(QNetworkRequest::ContentTypeHeader, + QByteArray("application/x-www-form-urlencoded")); + } + + o1.prepareRequest(&request, operation, body); + if (body.isEmpty()) + reply.reset(o1.networkAccessManager()->sendCustomRequest(request, operation)); + else + reply.reset(o1.networkAccessManager()->sendCustomRequest(request, operation, body)); + QVERIFY(!reply.isNull()); + QVERIFY(!reply->isFinished()); + + connect(&networkAccessManager, &QNetworkAccessManager::finished, + [&](QNetworkReply *reply) { + QByteArray data = reply->readAll(); + QUrlQuery query(QString::fromUtf8(data)); + receivedData = query.toString(QUrl::FullyDecoded); + }); + QVERIFY(waitForFinish(reply) == Success); + QCOMPARE(receivedData, parametersString); + reply.clear(); +} + void tst_OAuth1::secondTemporaryToken() { QNetworkAccessManager networkAccessManager; diff --git a/tests/auto/oauth1signature/tst_oauth1signature.cpp b/tests/auto/oauth1signature/tst_oauth1signature.cpp index 616578e..912aa44 100644 --- a/tests/auto/oauth1signature/tst_oauth1signature.cpp +++ b/tests/auto/oauth1signature/tst_oauth1signature.cpp @@ -101,6 +101,7 @@ void tst_OAuth1Signature::signatures_data() { QTest::addColumn<QUrl>("url"); QTest::addColumn<QOAuth1Signature::HttpRequestMethod>("method"); + QTest::addColumn<QByteArray>("customVerb"); QTest::addColumn<QString>("version"); QTest::addColumn<QString>("consumerKey"); QTest::addColumn<QString>("consumerSecret"); @@ -113,6 +114,7 @@ void tst_OAuth1Signature::signatures_data() QTest::newRow("standard") << QUrl("http://example.net") << QOAuth1Signature::HttpRequestMethod::Get + << QByteArray() << "1.0" << "key" << "secret" @@ -124,6 +126,7 @@ void tst_OAuth1Signature::signatures_data() << "mQaARxv7pqJyViuwNGtUfm6QSIQ="; QTest::newRow("post") << QUrl("http://example.net") << QOAuth1Signature::HttpRequestMethod::Post + << QByteArray() << "1.0" << "key" << "secret" @@ -135,6 +138,7 @@ void tst_OAuth1Signature::signatures_data() << "L4blJKqYMTSNUEt32rCgDLhxQxM="; QTest::newRow("put") << QUrl("http://example.net") << QOAuth1Signature::HttpRequestMethod::Put + << QByteArray() << "1.0" << "key" << "secret" @@ -146,6 +150,7 @@ void tst_OAuth1Signature::signatures_data() << "+eiZ+phNoYnETf6SqI+XSE43JSY="; QTest::newRow("delete") << QUrl("http://example.net") << QOAuth1Signature::HttpRequestMethod::Delete + << QByteArray() << "1.0" << "key" << "secret" @@ -157,6 +162,7 @@ void tst_OAuth1Signature::signatures_data() << "enbOVNG7/vGliie2/L44NdccMaw="; QTest::newRow("head") << QUrl("http://example.net") << QOAuth1Signature::HttpRequestMethod::Head + << QByteArray() << "1.0" << "key" << "secret" @@ -168,6 +174,7 @@ void tst_OAuth1Signature::signatures_data() << "6v74w0rRsVibJsJ796Nj8cJPqEU="; QTest::newRow("no-hmac-key") << QUrl("http://example.net") << QOAuth1Signature::HttpRequestMethod::Get + << QByteArray() << "1.0" << "key" << QString() @@ -179,6 +186,7 @@ void tst_OAuth1Signature::signatures_data() << "N2qP+LJdLbjalZq71M7oxPdeUjc="; QTest::newRow("custom-values") << QUrl("http://example.net") << QOAuth1Signature::HttpRequestMethod::Get + << QByteArray() << "1.0" << "key" << "secret" @@ -191,6 +199,30 @@ void tst_OAuth1Signature::signatures_data() { "secondKey", "secondValue" } } << "xNXgQaO0LrQMbJZGSfKFUmWwGDw="; + QTest::newRow("custom-verb-get") << QUrl("http://example.net") + << QOAuth1Signature::HttpRequestMethod::Custom + << QByteArray("GET") + << "1.0" + << "key" + << "secret" + << "accesskey" + << "accesssecret" + << "468167367" + << "1494852816" + << QVariantMap() + << "mQaARxv7pqJyViuwNGtUfm6QSIQ="; + QTest::newRow("custom-verb-patch") << QUrl("http://example.net") + << QOAuth1Signature::HttpRequestMethod::Custom + << QByteArray("PATCH") + << "1.0" + << "key" + << "secret" + << "accesskey" + << "accesssecret" + << "468167367" + << "1494852816" + << QVariantMap() + << "kcRO68D7IBQWlQvUR/jkhuF8AKM="; } void tst_OAuth1Signature::signatures() @@ -200,6 +232,7 @@ void tst_OAuth1Signature::signatures() QFETCH(QUrl, url); QFETCH(QOAuth1Signature::HttpRequestMethod, method); + QFETCH(QByteArray, customVerb); QFETCH(QString, version); QFETCH(QString, consumerKey); QFETCH(QString, consumerSecret); @@ -218,6 +251,8 @@ void tst_OAuth1Signature::signatures() parameters.insert(oauthToken, token); QOAuth1Signature signature(url, consumerSecret, tokenSecret, method, parameters); + if (method == QOAuth1Signature::HttpRequestMethod::Custom) + signature.setCustomMethodString(customVerb); const auto signatureData = signature.hmacSha1(); QCOMPARE(signatureData.toBase64(), result); } diff --git a/tests/auto/oauth2/tst_oauth2.cpp b/tests/auto/oauth2/tst_oauth2.cpp index f3a30a9..a82ad82 100644 --- a/tests/auto/oauth2/tst_oauth2.cpp +++ b/tests/auto/oauth2/tst_oauth2.cpp @@ -41,6 +41,7 @@ private Q_SLOTS: void getToken(); void refreshToken(); void getAndRefreshToken(); + void prepareRequest(); }; struct ReplyHandler : QAbstractOAuthReplyHandler @@ -165,5 +166,15 @@ void tst_OAuth2::getAndRefreshToken() QCOMPARE(oauth2.token(), QLatin1String("refresh_token")); } +void tst_OAuth2::prepareRequest() +{ + QOAuth2AuthorizationCodeFlow oauth2; + oauth2.setToken(QStringLiteral("access_token")); + + QNetworkRequest request(QUrl("http://localhost")); + oauth2.prepareRequest(&request, QByteArray()); + QCOMPARE(request.rawHeader("Authorization"), QByteArray("Bearer access_token")); +} + QTEST_MAIN(tst_OAuth2) #include "tst_oauth2.moc" |