/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** 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 General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** 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-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include Q_DECLARE_METATYPE(QNetworkAccessManager::Operation) Q_DECLARE_METATYPE(QAbstractOAuth::Error) typedef QSharedPointer QNetworkReplyPtr; class tst_QOAuth : public QObject { Q_OBJECT QEventLoop *loop; enum RunSimpleRequestReturn { Timeout = 0, Success, Failure }; int returnCode; using QObject::connect; static bool connect(const QNetworkReplyPtr &ptr, const char *signal, const QObject *receiver, const char *slot, Qt::ConnectionType ct = Qt::AutoConnection) { return connect(ptr.data(), signal, receiver, slot, ct); } bool connect(const QNetworkReplyPtr &ptr, const char *signal, const char *slot, Qt::ConnectionType ct = Qt::AutoConnection) { return connect(ptr.data(), signal, slot, ct); } public: int waitForFinish(QNetworkReplyPtr &reply); void test_data(); void fillParameters(QVariantMap *parameters, const QUrlQuery &query); public Q_SLOTS: void finished(); void gotError(); private Q_SLOTS: void createSignature(); void getToken_data(); void getToken(); void grant_data(); void grant(); void authenticatedCalls_data(); void authenticatedCalls(); }; int tst_QOAuth::waitForFinish(QNetworkReplyPtr &reply) { int count = 0; connect(reply, SIGNAL(finished()), SLOT(finished())); connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(gotError())); returnCode = Success; loop = new QEventLoop; QSignalSpy spy(reply.data(), SIGNAL(downloadProgress(qint64,qint64))); while (!reply->isFinished()) { QTimer::singleShot(5000, loop, SLOT(quit())); if ( loop->exec() == Timeout && count == spy.count() && !reply->isFinished()) { returnCode = Timeout; break; } count = spy.count(); } delete loop; loop = 0; return returnCode; } void tst_QOAuth::test_data() { QTest::addColumn("consumerKey"); QTest::addColumn("consumerSecret"); QTest::addColumn("requestToken"); QTest::addColumn("requestTokenSecret"); QTest::addColumn("accessToken"); QTest::addColumn("accessTokenSecret"); QTest::addColumn("requestTokenUrl"); QTest::addColumn("accessTokenUrl"); QTest::addColumn("authenticatedCallUrl"); QTest::addColumn("requestType"); QTest::newRow("term.ie_get") << "key" << "secret" << "requestkey" << "requestsecret" << "accesskey" << "accesssecret" << QUrl("http://term.ie/oauth/example/request_token.php") << QUrl("http://term.ie/oauth/example/access_token.php") << QUrl("http://term.ie/oauth/example/echo_api.php") << QNetworkAccessManager::GetOperation; QTest::newRow("term.ie_post") << "key" << "secret" << "requestkey" << "requestsecret" << "accesskey" << "accesssecret" << QUrl("http://term.ie/oauth/example/request_token.php") << QUrl("http://term.ie/oauth/example/access_token.php") << QUrl("http://term.ie/oauth/example/echo_api.php") << QNetworkAccessManager::PostOperation; QTest::newRow("oauthbin.com_get") << "key" << "secret" << "requestkey" << "requestsecret" << "accesskey" << "accesssecret" << QUrl("http://oauthbin.com/v1/request-token") << QUrl("http://oauthbin.com/v1/access-token") << QUrl("http://oauthbin.com/v1/echo") << QNetworkAccessManager::GetOperation; QTest::newRow("oauthbin.com_post") << "key" << "secret" << "requestkey" << "requestsecret" << "accesskey" << "accesssecret" << QUrl("http://oauthbin.com/v1/request-token") << QUrl("http://oauthbin.com/v1/access-token") << QUrl("http://oauthbin.com/v1/echo") << QNetworkAccessManager::PostOperation; } void tst_QOAuth::fillParameters(QVariantMap *parameters, const QUrlQuery &query) { const auto list = query.queryItems(); for (auto it = list.begin(), end = list.end(); it != end; ++it) parameters->insert(it->first, it->second); } void tst_QOAuth::finished() { loop->exit(returnCode = Success); } void tst_QOAuth::gotError() { loop->exit(returnCode = Failure); disconnect(QObject::sender(), SIGNAL(finished()), this, 0); } void tst_QOAuth::createSignature() { // Example from https://dev.twitter.com/oauth/overview/creating-signatures QByteArray parameterString, signatureBase, signature; const QUrl url(QStringLiteral("https://api.twitter.com/1/statuses/update.json?include_entities=true")); const QString data = QUrl::fromPercentEncoding("status=Hello%20Ladies%20%2b%20Gentlemen%2c%20a%20signed%20OAuth%20request%21"); const auto consumerSecret = QStringLiteral("kAcSOqF21Fu85e7zjz7ZN2U4ZRhfV3WpwPAoE3Z7kBw"); const auto oauthTokenSecret = QStringLiteral("LswwdoUaIvS8ltyTt5jkRh4J50vUPVVHtR2YPi5kE"); QNetworkAccessManager::Operation operation = QNetworkAccessManager::PostOperation; QVariantMap oauthParams; oauthParams.insert(QStringLiteral("oauth_consumer_key"), QStringLiteral("xvz1evFS4wEEPTGEFPHBog")); oauthParams.insert(QStringLiteral("oauth_nonce"), QStringLiteral("kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg")); oauthParams.insert(QStringLiteral("oauth_signature_method"), QStringLiteral("HMAC-SHA1")); oauthParams.insert(QStringLiteral("oauth_timestamp"), QStringLiteral("1318622958")); oauthParams.insert(QStringLiteral("oauth_token"), QStringLiteral("370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb")); oauthParams.insert(QStringLiteral("oauth_version"), QStringLiteral("1.0")); QVariantMap otherParametersFromData; fillParameters(&otherParametersFromData, QUrlQuery(data)); // HTTP Method QCOMPARE(QOAuth1Private::operationName(operation), QStringLiteral("POST")); // Base URL QCOMPARE(url.toString(QUrl::RemoveQuery), QStringLiteral("https://api.twitter.com/1/statuses/update.json")); struct OAuthSubClass : QOAuth1 { using QOAuth1::parameterString; using QOAuth1::signatureBaseString; using QOAuth1::signature; }; // Creating parameter string { QVariantMap parameters = oauthParams; parameters.unite(otherParametersFromData); fillParameters(¶meters, QUrlQuery(url.query())); parameterString = OAuthSubClass::parameterString(parameters); parameters.insertMulti(parameters.keys().first(), QString()); QTest::ignoreMessage(QtWarningMsg, QRegularExpression("^QOAuth: duplicated key .*$")); OAuthSubClass::parameterString(parameters); } QCOMPARE(parameterString, QByteArray("include_entities=true" "&oauth_consumer_key=xvz1evFS4wEEPTGEFPHBog" "&oauth_nonce=kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg" "&oauth_signature_method=HMAC-SHA1" "&oauth_timestamp=1318622958" "&oauth_token=370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb" "&oauth_version=1.0" "&status=Hello%20Ladies%20%2B%20Gentlemen%2C%20a%20signed%20OAuth%20request%21")); // Collecting parameters oauthParams.unite(otherParametersFromData); signatureBase = OAuthSubClass::signatureBaseString(oauthParams, url, operation); QCOMPARE(signatureBase, QByteArray("POST&https%3A%2F%2Fapi.twitter.com%2F1%2Fstatuses%2Fupdate.json" "&include_entities%3Dtrue%26oauth_consumer_key%3Dxvz1evFS4wEEPTGEFPHBog" "%26oauth_nonce%3DkYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg" "%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1318622958" "%26oauth_token%3D370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb" "%26oauth_version%3D1.0%26status%3DHello%2520Ladies%2520%252B%2520Gentlemen%252C%2520a%2520signed%2520OAuth%2520request%2521")); // Getting a signing key signature = OAuthSubClass::signature(oauthParams, url, operation, consumerSecret, oauthTokenSecret); QCOMPARE(signature, QByteArray("tnnArxj06cWHq44gCs1OSKk/jLY=")); } void tst_QOAuth::getToken_data() { QTest::addColumn>("clientCredentials"); QTest::addColumn>("token"); QTest::addColumn>("expectedToken"); QTest::addColumn("url"); QTest::addColumn("requestType"); // term.ie const QPair emptyCredentials; QTest::newRow("term.ie_request_get") << qMakePair(QStringLiteral("key"), QStringLiteral("secret")) << emptyCredentials << qMakePair(QStringLiteral("requestkey"), QStringLiteral("requestsecret")) << QUrl("http://term.ie/oauth/example/request_token.php") << QNetworkAccessManager::GetOperation; QTest::newRow("term.ie_request_post") << qMakePair(QStringLiteral("key"), QStringLiteral("secret")) << emptyCredentials << qMakePair(QStringLiteral("requestkey"), QStringLiteral("requestsecret")) << QUrl("http://term.ie/oauth/example/request_token.php") << QNetworkAccessManager::PostOperation; QTest::newRow("term.ie_access_get") << qMakePair(QStringLiteral("key"), QStringLiteral("secret")) << qMakePair(QStringLiteral("requestkey"), QStringLiteral("requestsecret")) << qMakePair(QStringLiteral("accesskey"), QStringLiteral("accesssecret")) << QUrl("http://term.ie/oauth/example/access_token.php") << QNetworkAccessManager::GetOperation; QTest::newRow("term.ie_access_post") << qMakePair(QStringLiteral("key"), QStringLiteral("secret")) << qMakePair(QStringLiteral("requestkey"), QStringLiteral("requestsecret")) << qMakePair(QStringLiteral("accesskey"), QStringLiteral("accesssecret")) << QUrl("http://term.ie/oauth/example/access_token.php") << QNetworkAccessManager::PostOperation; // oauthbin.com QTest::newRow("oauthbin.com_request_get") << qMakePair(QStringLiteral("key"), QStringLiteral("secret")) << emptyCredentials << qMakePair(QStringLiteral("requestkey"), QStringLiteral("requestsecret")) << QUrl("http://oauthbin.com/v1/request-token") << QNetworkAccessManager::GetOperation; QTest::newRow("oauthbin.com_request_post") << qMakePair(QStringLiteral("key"), QStringLiteral("secret")) << emptyCredentials << qMakePair(QStringLiteral("requestkey"), QStringLiteral("requestsecret")) << QUrl("http://oauthbin.com/v1/request-token") << QNetworkAccessManager::PostOperation; QTest::newRow("oauthbin.com_access_get") << qMakePair(QStringLiteral("key"), QStringLiteral("secret")) << qMakePair(QStringLiteral("requestkey"), QStringLiteral("requestsecret")) << qMakePair(QStringLiteral("accesskey"), QStringLiteral("accesssecret")) << QUrl("http://oauthbin.com/v1/access-token") << QNetworkAccessManager::GetOperation; QTest::newRow("oauthbin.com_access_post") << qMakePair(QStringLiteral("key"), QStringLiteral("secret")) << qMakePair(QStringLiteral("requestkey"), QStringLiteral("requestsecret")) << qMakePair(QStringLiteral("accesskey"), QStringLiteral("accesssecret")) << QUrl("http://oauthbin.com/v1/access-token") << QNetworkAccessManager::PostOperation; } void tst_QOAuth::getToken() { using StringPair = QPair; QFETCH(StringPair, clientCredentials); QFETCH(StringPair, token); QFETCH(StringPair, expectedToken); QFETCH(QUrl, url); QFETCH(QNetworkAccessManager::Operation, requestType); QPair tokenReceived; QNetworkAccessManager networkAccessManager; QNetworkReplyPtr reply; struct OAuth1 : QOAuth1 { using QOAuth1::QOAuth1; using QOAuth1::requestTokenCredentials; }o1(&networkAccessManager); o1.setClientCredentials(clientCredentials.first, clientCredentials.second); o1.setTokenCredentials(token); o1.setTemporaryCredentialsUrl(url); reply.reset(o1.requestTokenCredentials(requestType, url, token)); QVERIFY(!reply.isNull()); connect(&o1, &QOAuth1::tokenChanged, [&tokenReceived](const QString &token){ tokenReceived.first = token; }); connect(&o1, &QOAuth1::tokenSecretChanged, [&tokenReceived](const QString &tokenSecret) { tokenReceived.second = tokenSecret; }); QVERIFY(waitForFinish(reply) == Success); QVERIFY(!tokenReceived.first.isEmpty() && !tokenReceived.second.isEmpty()); } void tst_QOAuth::grant_data() { test_data(); } void tst_QOAuth::grant() { QFETCH(QString, consumerKey); QFETCH(QString, consumerSecret); QFETCH(QString, requestToken); QFETCH(QString, requestTokenSecret); QFETCH(QString, accessToken); QFETCH(QString, accessTokenSecret); QFETCH(QUrl, requestTokenUrl); QFETCH(QUrl, accessTokenUrl); bool tokenReceived = false; QNetworkAccessManager networkAccessManager; QOAuth1 o1(&networkAccessManager); { QSignalSpy clientIdentifierSpy(&o1, &QOAuth1::clientIdentifierChanged); QSignalSpy clientSharedSecret(&o1, &QOAuth1::clientSharedSecretChanged); o1.setClientCredentials(consumerKey, consumerSecret); QCOMPARE(clientIdentifierSpy.count(), 1); QCOMPARE(clientSharedSecret.count(), 1); } { QSignalSpy spy(&o1, &QOAuth1::temporaryCredentialsUrlChanged); o1.setTemporaryCredentialsUrl(requestTokenUrl); QCOMPARE(spy.count(), 1); } { QSignalSpy spy(&o1, &QOAuth1::tokenCredentialsUrlChanged); o1.setTokenCredentialsUrl(accessTokenUrl); QCOMPARE(spy.count(), 1); } connect(&o1, &QAbstractOAuth::statusChanged, [&](QAbstractOAuth::Status status) { if (status == QAbstractOAuth::Status::TemporaryTokenReceived) { if (!requestToken.isEmpty()) QCOMPARE(requestToken, o1.tokenCredentials().first); if (!requestTokenSecret.isEmpty()) QCOMPARE(requestTokenSecret, o1.tokenCredentials().second); tokenReceived = true; } else if (status == QAbstractOAuth::Status::Granted) { if (!accessToken.isEmpty()) QCOMPARE(accessToken, o1.tokenCredentials().first); if (!accessTokenSecret.isEmpty()) QCOMPARE(accessTokenSecret, o1.tokenCredentials().second); tokenReceived = true; } }); QEventLoop loop; QTimer::singleShot(10000, &loop, &QEventLoop::quit); connect(&o1, &QOAuth1::granted, &loop, &QEventLoop::quit); o1.grant(); loop.exec(); QVERIFY(tokenReceived); QCOMPARE(o1.status(), QAbstractOAuth::Status::Granted); } void tst_QOAuth::authenticatedCalls_data() { QTest::addColumn("consumerKey"); QTest::addColumn("consumerSecret"); QTest::addColumn("accessKey"); QTest::addColumn("accessKeySecret"); QTest::addColumn("url"); QTest::addColumn("parameters"); QTest::addColumn("operation"); const QVariantMap parameters { { QStringLiteral("first"), QStringLiteral("first") }, { QStringLiteral("second"), QStringLiteral("second") }, { QStringLiteral("third"), QStringLiteral("third") } }; QTest::newRow("term.ie_get") << "key" << "secret" << "accesskey" << "accesssecret" << QUrl("http://term.ie/oauth/example/echo_api.php") << parameters << QNetworkAccessManager::GetOperation; QTest::newRow("term.ie_post") << "key" << "secret" << "accesskey" << "accesssecret" << QUrl("http://term.ie/oauth/example/echo_api.php") << parameters << QNetworkAccessManager::PostOperation; QTest::newRow("oauthbin.com_get") << "key" << "secret" << "accesskey" << "accesssecret" << QUrl("http://oauthbin.com/v1/echo") << parameters << QNetworkAccessManager::GetOperation; QTest::newRow("oauthbin.com_post") << "key" << "secret" << "accesskey" << "accesssecret" << QUrl("http://oauthbin.com/v1/echo") << parameters << QNetworkAccessManager::PostOperation; } void tst_QOAuth::authenticatedCalls() { QFETCH(QString, consumerKey); QFETCH(QString, consumerSecret); QFETCH(QString, accessKey); QFETCH(QString, accessKeySecret); QFETCH(QUrl, url); QFETCH(QVariantMap, parameters); QFETCH(QNetworkAccessManager::Operation, operation); QNetworkAccessManager networkAccessManager; QNetworkReplyPtr reply; QString receivedData; QString parametersString; { 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 == QNetworkAccessManager::GetOperation) reply.reset(o1.get(url, parameters)); else if (operation == QNetworkAccessManager::PostOperation) reply.reset(o1.post(url, parameters)); QVERIFY(!reply.isNull()); QVERIFY(!reply->isFinished()); connect(&networkAccessManager, &QNetworkAccessManager::finished, [&receivedData](QNetworkReply *reply) { receivedData = QString::fromUtf8(reply->readAll()); }); QVERIFY(waitForFinish(reply) == Success); QCOMPARE(receivedData, parametersString); reply.clear(); } QTEST_MAIN(tst_QOAuth) #include "tst_qoauth.moc"