diff options
author | Felix Barz <barz.felix@web.de> | 2018-10-16 18:44:09 +0200 |
---|---|---|
committer | Felix Barz <barz.felix@web.de> | 2019-01-28 15:14:05 +0000 |
commit | 297e842a7fcbe0def4223625b8daa3e46afe057f (patch) | |
tree | 7db5c3a24462ceab60459740a435228141722158 /tests | |
parent | 94640eb27c054baffddf44fcaebf26f800a6415e (diff) |
Add method to prepare network requests
Sending authenticated requests is limited to the APIs the QAbstractOAuth
class provides. With the newly added prepareRequest and
sendCustomRequest methods, one can set the correct headers etc. on any
custom created network request, including the verb that is used to the
request as well the body to be sent. This includes a modifcation to the
QOAuth1Signature class to make it possible to sign requests with a
custom method.
[ChangeLog][QOAuth1Signature] Added customMethodString and
setCustomMethodString methods to support signing requests with custom
methods.
[ChangeLog][QAbstractOAuth] Added prepareRequest and sendCustomRequest
methods to authenticate any custom request, including custom verbs and
bodies.
Change-Id: I86459844ee919e8c9d60d349257b57afbc3fa07a
Reviewed-by: MÃ¥rten Nordheim <marten.nordheim@qt.io>
Diffstat (limited to 'tests')
-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 |
5 files changed, 327 insertions, 1 deletions
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" |