QNAM: Fix authentication cache when the password is in the URL
Two problems: - The signal cacheCredidentials was not connected in the synchronous case while it must be connected. (Regression when the threaded http was merged) - We cannot cache the credidentials when we proceed the url because at that point, we do not know the realm (this basically reverts 9bc5a32b875b812c3a706034c8c27614f86bd138) Task-number: QTBUG-18411 Change-Id: I8ea11fa23db4314c3f17ed06d2d7f9ee934ccdba Reviewed-by: Peter Hartmann <peter.hartmann@nokia.com>
This commit is contained in:
parent
1c70cd9980
commit
a4f446704e
@ -435,6 +435,11 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket
|
||||
// send any pending requests
|
||||
copyCredentials(i, auth, isProxy);
|
||||
}
|
||||
} else if (priv->phase == QAuthenticatorPrivate::Start) {
|
||||
// If the url's authenticator has a 'user' set we will end up here (phase is only set to 'Done' by
|
||||
// parseHttpResponse above if 'user' is empty). So if credentials were supplied with the request,
|
||||
// such as in the case of an XMLHttpRequest, this is our only opportunity to cache them.
|
||||
emit reply->cacheCredentials(reply->request(), auth);
|
||||
}
|
||||
// - Changing values in QAuthenticator will reset the 'phase'. Therefore if it is still "Done"
|
||||
// then nothing was filled in by the user or the cache
|
||||
|
@ -202,7 +202,6 @@ bool QHttpNetworkConnectionChannel::sendRequest()
|
||||
|| (!url.password().isEmpty() && url.password() != auth.password())) {
|
||||
auth.setUser(url.userName());
|
||||
auth.setPassword(url.password());
|
||||
emit reply->cacheCredentials(request, &auth);
|
||||
connection->d_func()->copyCredentials(connection->d_func()->indexOf(socket), &auth, false);
|
||||
}
|
||||
// clear the userinfo, since we use the same request for resending
|
||||
|
@ -312,8 +312,6 @@ void QHttpThreadDelegate::startRequest()
|
||||
// some signals are only interesting when normal asynchronous style is used
|
||||
connect(httpReply,SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
|
||||
connect(httpReply,SIGNAL(dataReadProgress(qint64, qint64)), this, SLOT(dataReadProgressSlot(qint64,qint64)));
|
||||
connect(httpReply, SIGNAL(cacheCredentials(QHttpNetworkRequest,QAuthenticator*)),
|
||||
this, SLOT(cacheCredentialsSlot(QHttpNetworkRequest,QAuthenticator*)));
|
||||
#ifndef QT_NO_OPENSSL
|
||||
connect(httpReply,SIGNAL(sslErrors(const QList<QSslError>)), this, SLOT(sslErrorsSlot(QList<QSslError>)));
|
||||
#endif
|
||||
@ -325,6 +323,9 @@ void QHttpThreadDelegate::startRequest()
|
||||
connect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
|
||||
this, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
|
||||
}
|
||||
|
||||
connect(httpReply, SIGNAL(cacheCredentials(QHttpNetworkRequest,QAuthenticator*)),
|
||||
this, SLOT(cacheCredentialsSlot(QHttpNetworkRequest,QAuthenticator*)));
|
||||
}
|
||||
|
||||
// This gets called from the user thread or by the synchronous HTTP timeout timer
|
||||
|
@ -378,6 +378,7 @@ private Q_SLOTS:
|
||||
void dontInsertPartialContentIntoTheCache();
|
||||
|
||||
void httpUserAgent();
|
||||
void synchronousAuthenticationCache();
|
||||
|
||||
// NOTE: This test must be last!
|
||||
void parentingRepliesToTheApp();
|
||||
@ -499,6 +500,14 @@ protected:
|
||||
client->setParent(this);
|
||||
++totalConnections;
|
||||
}
|
||||
|
||||
virtual void reply() {
|
||||
// we need to emulate the bytesWrittenSlot call if the data is empty.
|
||||
if (dataToTransmit.size() == 0)
|
||||
QMetaObject::invokeMethod(this, "bytesWrittenSlot", Qt::QueuedConnection);
|
||||
else
|
||||
client->write(dataToTransmit);
|
||||
}
|
||||
private:
|
||||
void connectSocketSignals()
|
||||
{
|
||||
@ -532,11 +541,7 @@ public slots:
|
||||
if (multiple)
|
||||
receivedData.remove(0, doubleEndlPos+4);
|
||||
|
||||
// we need to emulate the bytesWrittenSlot call if the data is empty.
|
||||
if (dataToTransmit.size() == 0)
|
||||
QMetaObject::invokeMethod(this, "bytesWrittenSlot", Qt::QueuedConnection);
|
||||
else
|
||||
client->write(dataToTransmit);
|
||||
reply();
|
||||
}
|
||||
}
|
||||
|
||||
@ -6359,6 +6364,79 @@ void tst_QNetworkReply::httpUserAgent()
|
||||
QVERIFY(server.receivedData.contains("\r\nUser-Agent: abcDEFghi\r\n"));
|
||||
}
|
||||
|
||||
void tst_QNetworkReply::synchronousAuthenticationCache()
|
||||
{
|
||||
class MiniAuthServer : public MiniHttpServer {
|
||||
public:
|
||||
MiniAuthServer(QThread *thread) : MiniHttpServer(QByteArray(), false, thread) {};
|
||||
virtual void reply() {
|
||||
|
||||
dataToTransmit =
|
||||
"HTTP/1.0 401 Unauthorized\r\n"
|
||||
"WWW-Authenticate: Basic realm=\"QNetworkAccessManager Test Realm\"\r\n"
|
||||
"Content-Length: 4\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"\r\n"
|
||||
"auth";
|
||||
QRegExp rx("Authorization: Basic ([^\r\n]*)\r\n");
|
||||
if (rx.indexIn(receivedData) > 0) {
|
||||
if (QByteArray::fromBase64(rx.cap(1).toLatin1()) == "login:password") {
|
||||
dataToTransmit =
|
||||
"HTTP/1.0 200 OK\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Content-Length: 2\r\n"
|
||||
"\r\n"
|
||||
"OK";
|
||||
}
|
||||
}
|
||||
receivedData.clear();
|
||||
MiniHttpServer::reply();
|
||||
}
|
||||
};
|
||||
|
||||
// when using synchronous commands, we need a different event loop for
|
||||
// the server thread, because the client is never returning to the
|
||||
// event loop
|
||||
QScopedPointer<QThread, QThreadCleanup> serverThread(new QThread);
|
||||
QScopedPointer<MiniHttpServer, QDeleteLaterCleanup> server(new MiniAuthServer(serverThread.data()));
|
||||
server->doClose = true;
|
||||
|
||||
//1) URL without credentials, we are not authenticated
|
||||
{
|
||||
QUrl url = "http://localhost:" + QString::number(server->serverPort()) + "/path";
|
||||
QNetworkRequest request(url);
|
||||
request.setAttribute(QNetworkRequest::SynchronousRequestAttribute, true);
|
||||
|
||||
QNetworkReplyPtr reply = manager.get(request);
|
||||
QVERIFY(reply->isFinished());
|
||||
QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError);
|
||||
}
|
||||
|
||||
//2) URL with credentials, we are authenticated
|
||||
{
|
||||
QUrl url = "http://login:password@localhost:" + QString::number(server->serverPort()) + "/path2";
|
||||
QNetworkRequest request(url);
|
||||
request.setAttribute(QNetworkRequest::SynchronousRequestAttribute, true);
|
||||
|
||||
QNetworkReplyPtr reply = manager.get(request);
|
||||
QVERIFY(reply->isFinished());
|
||||
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
||||
QCOMPARE(reply->readAll().constData(), "OK");
|
||||
}
|
||||
|
||||
//3) URL without credentials, we are authenticated because they are cached
|
||||
{
|
||||
QUrl url = "http://localhost:" + QString::number(server->serverPort()) + "/path3";
|
||||
QNetworkRequest request(url);
|
||||
request.setAttribute(QNetworkRequest::SynchronousRequestAttribute, true);
|
||||
|
||||
QNetworkReplyPtr reply = manager.get(request);
|
||||
QVERIFY(reply->isFinished());
|
||||
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
||||
QCOMPARE(reply->readAll().constData(), "OK");
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: This test must be last testcase in tst_qnetworkreply!
|
||||
void tst_QNetworkReply::parentingRepliesToTheApp()
|
||||
|
Loading…
Reference in New Issue
Block a user