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:
Olivier Goffart 2011-11-08 15:37:42 +01:00 committed by Qt by Nokia
parent 1c70cd9980
commit a4f446704e
4 changed files with 91 additions and 8 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()