diff --git a/src/network/access/qnetworkrequestfactory.cpp b/src/network/access/qnetworkrequestfactory.cpp index 2e136c43340..108ac857947 100644 --- a/src/network/access/qnetworkrequestfactory.cpp +++ b/src/network/access/qnetworkrequestfactory.cpp @@ -349,6 +349,86 @@ void QNetworkRequestFactory::clearBearerToken() d->bearerToken.clear(); } +/*! + Returns the username set to this factory. + + \sa setUserName(), clearUserName(), password() +*/ +QString QNetworkRequestFactory::userName() const +{ + return d->userName; +} + +/*! + Sets the username of this factory to \a userName. + + The username is set in the request URL when \l request() is called. + The QRestAccessManager / QNetworkAccessManager will attempt to use + these credentials when the server indicates that authentication + is required. + + \sa userName(), clearUserName(), password() +*/ +void QNetworkRequestFactory::setUserName(const QString &userName) +{ + if (d->userName == userName) + return; + d.detach(); + d->userName = userName; +} + +/*! + Clears the username set to this factory. +*/ +void QNetworkRequestFactory::clearUserName() +{ + if (d->userName.isEmpty()) + return; + d.detach(); + d->userName.clear(); +} + +/*! + Returns the password set to this factory. + + \sa password(), clearPassword(), userName() +*/ +QString QNetworkRequestFactory::password() const +{ + return d->password; +} + +/*! + Sets the password of this factory to \a password. + + The password is set in the request URL when \l request() is called. + The QRestAccessManager / QNetworkAccessManager will attempt to use + these credentials when the server indicates that authentication + is required. + + \sa password(), clearPassword(), userName() +*/ +void QNetworkRequestFactory::setPassword(const QString &password) +{ + if (d->password == password) + return; + d.detach(); + d->password = password; +} + +/*! + Clears the password set to this factory. + + \sa password(), setPassword(), userName() +*/ +void QNetworkRequestFactory::clearPassword() +{ + if (d->password.isEmpty()) + return; + d.detach(); + d->password.clear(); +} + /*! Sets \a timeout used for transfers. @@ -471,6 +551,10 @@ QUrl QNetworkRequestFactoryPrivate::requestUrl(const QString *path, QUrl resultUrl = baseUrl; QUrlQuery resultQuery(providedQuery); QString basePath = baseUrl.path(); + + resultUrl.setUserName(userName); + resultUrl.setPassword(password); + // Separate the path and query parameters components on the application-provided path const QString requestPath{providedPath.path()}; const QUrlQuery pathQueryItems{providedPath}; @@ -518,6 +602,8 @@ bool QNetworkRequestFactoryPrivate::equals( #endif baseUrl == other.baseUrl && bearerToken == other.bearerToken && + userName == other.userName && + password == other.password && headers.equals(other.headers) && queryParameters == other.queryParameters; } @@ -541,6 +627,8 @@ QDebug operator<<(QDebug debug, const QNetworkRequestFactory &factory) << ", queryParameters = " << factory.queryParameters().queryItems() << ", bearerToken = " << (factory.bearerToken().isEmpty() ? "(empty)" : "(is set)") << ", transferTimeout = " << factory.transferTimeout() + << ", userName = " << (factory.userName().isEmpty() ? "(empty)" : "(is set)") + << ", password = " << (factory.password().isEmpty() ? "(empty)" : "(is set)") #if QT_CONFIG(ssl) << ", SSL configuration" << (factory.sslConfiguration().isNull() ? " is not set (default)" : " is set") diff --git a/src/network/access/qnetworkrequestfactory.h b/src/network/access/qnetworkrequestfactory.h index 0c08ce42a60..324e811ddcb 100644 --- a/src/network/access/qnetworkrequestfactory.h +++ b/src/network/access/qnetworkrequestfactory.h @@ -59,6 +59,14 @@ public: Q_NETWORK_EXPORT void setBearerToken(const QByteArray &token); Q_NETWORK_EXPORT void clearBearerToken(); + Q_NETWORK_EXPORT QString userName() const; + Q_NETWORK_EXPORT void setUserName(const QString &userName); + Q_NETWORK_EXPORT void clearUserName(); + + Q_NETWORK_EXPORT QString password() const; + Q_NETWORK_EXPORT void setPassword(const QString &password); + Q_NETWORK_EXPORT void clearPassword(); + Q_NETWORK_EXPORT void setTransferTimeout(std::chrono::milliseconds timeout); Q_NETWORK_EXPORT std::chrono::milliseconds transferTimeout() const; diff --git a/src/network/access/qnetworkrequestfactory_p.h b/src/network/access/qnetworkrequestfactory_p.h index 8378669ce90..c4fb33e8b28 100644 --- a/src/network/access/qnetworkrequestfactory_p.h +++ b/src/network/access/qnetworkrequestfactory_p.h @@ -42,6 +42,8 @@ public: QUrl baseUrl; QHttpHeaders headers; QByteArray bearerToken; + QString userName; + QString password; QUrlQuery queryParameters; std::chrono::milliseconds transferTimeout{0}; }; diff --git a/tests/auto/network/access/qnetworkrequestfactory/tst_qnetworkrequestfactory.cpp b/tests/auto/network/access/qnetworkrequestfactory/tst_qnetworkrequestfactory.cpp index 3f251053f59..6394f2c3227 100644 --- a/tests/auto/network/access/qnetworkrequestfactory/tst_qnetworkrequestfactory.cpp +++ b/tests/auto/network/access/qnetworkrequestfactory/tst_qnetworkrequestfactory.cpp @@ -25,6 +25,7 @@ private Q_SLOTS: void bearerToken(); void operators(); void timeout(); + void userInfo(); private: const QUrl url1{u"http://foo.io"_s}; @@ -335,5 +336,31 @@ void tst_QNetworkRequestFactory::timeout() QCOMPARE(request.transferTimeoutAsDuration(), timeout); } +void tst_QNetworkRequestFactory::userInfo() +{ + QNetworkRequestFactory factory; + QVERIFY(factory.userName().isEmpty()); + QVERIFY(factory.password().isEmpty()); + + const auto uname = u"a_username"_s; + const auto password = u"a_password"_s; + factory.setUserName(uname); + QCOMPARE(factory.userName(), uname); + factory.setPassword(password); + QCOMPARE(factory.password(), password); + + // Verify that debug output does not contain password + QString debugOutput; + QDebug debug(&debugOutput); + debug << factory; + QVERIFY(debugOutput.contains("password = (is set)")); + QVERIFY(!debugOutput.contains(password)); + + factory.clearUserName(); + factory.clearPassword(); + QVERIFY(factory.userName().isEmpty()); + QVERIFY(factory.password().isEmpty()); +} + QTEST_MAIN(tst_QNetworkRequestFactory) #include "tst_qnetworkrequestfactory.moc" diff --git a/tests/auto/network/access/qrestaccessmanager/tst_qrestaccessmanager.cpp b/tests/auto/network/access/qrestaccessmanager/tst_qrestaccessmanager.cpp index 960984be1cf..50c01b42396 100644 --- a/tests/auto/network/access/qrestaccessmanager/tst_qrestaccessmanager.cpp +++ b/tests/auto/network/access/qrestaccessmanager/tst_qrestaccessmanager.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -35,6 +36,7 @@ private slots: void networkRequestReply(); void abort(); void authentication(); + void userInfo(); void errors(); void body(); void json(); @@ -513,6 +515,45 @@ void tst_QRestAccessManager::authentication() QCOMPARE(serverSideRequest.headers["Authorization"_ba], "Basic YV91c2VyOmFfcGFzc3dvcmQ="_ba); } +void tst_QRestAccessManager::userInfo() +{ + // Tests setting of username and password into the request factory + using ReplyPtr = std::unique_ptr; + QRestAccessManager manager; + manager.setDeletesRepliesOnFinished(false); + HttpTestServer server; + QTRY_VERIFY(server.isListening()); + + QNetworkRequestFactory factory(server.url()); + factory.setUserName(u"a_user"_s); + const auto password = u"a_password"_s; + factory.setPassword(password); + + HttpData serverSideRequest; + server.setHandler([&](HttpData request, HttpData& response, ResponseControl&) { + if (!request.headers.contains("Authorization"_ba)) { + response.status = 401; + response.headers.insert("WWW-Authenticate: "_ba, "Basic realm=\"secret_place\""_ba); + } else { + response.status = 200; + } + serverSideRequest = request; // store for checking later the 'Authorization' header value + }); + + ReplyPtr reply(manager.get(factory.request())); + QTRY_VERIFY(reply.get()->isFinished()); + QVERIFY(reply.get()->isSuccess()); + QCOMPARE(reply.get()->httpStatus(), 200); + QCOMPARE(serverSideRequest.headers["Authorization"_ba], "Basic YV91c2VyOmFfcGFzc3dvcmQ="_ba); + + // Verify that debug output does not contain password + QString debugOutput; + QDebug debug(&debugOutput); + debug << factory; + QVERIFY(debugOutput.contains("password = (is set)")); + QVERIFY(!debugOutput.contains(password)); +} + #define VERIFY_HTTP_ERROR_STATUS(STATUS) \ serverSideResponse.status = STATUS; \ reply = manager.get(request); \