Reputation: 14255
In my implementation, it seems as if QNetworkAccessManager
caches the credentials used for a specific url, after successful authentication.
A subsequent request to the same url using different credentials will succeed, but only because it uses the cached credentials instead of the new ones (the authenticationRequired
signal is not emitted).
Is there some way in (Py)Qt4 (also PyQt 5.6) to clear the cache or otherwise force an update of the cached credentials? Should I just delete the manager and construct a new one? Or is there something else I am doing wrong?
Qt 5 offers a clearAccessCache()
method, but Qt 4 does not, as far as I can see.
This small example illustrates the problem:
import sys
import json
from PyQt4 import QtNetwork, QtGui, QtCore
def show_reply_content(reply):
content = json.loads(str(reply.readAll()))
if 'authenticated' in content and content['authenticated']:
print 'authentication successful for user {}'.format(
content['user'])
reply.manager().replies_authenticated += 1
# Quit when all replies are finished
reply.deleteLater()
reply.manager().replies_unfinished -= 1
if not reply.manager().replies_unfinished:
print 'number of successful authentications: {}'.format(
reply.manager().replies_authenticated)
app.quit()
def provide_credentials(reply, authenticator):
print 'Setting credentials for {}:\nusername: {}\n' \
'password: {}\n\n'.format(reply.url(),
reply.credentials['username'],
reply.credentials['password'])
authenticator.setUser(reply.credentials['username'])
authenticator.setPassword(reply.credentials['password'])
# Some initialization
app = QtGui.QApplication(sys.argv)
manager = QtNetwork.QNetworkAccessManager()
manager.finished.connect(show_reply_content)
manager.authenticationRequired.connect(provide_credentials)
manager.replies_unfinished = 0
manager.replies_authenticated = 0
# Specify credentials
all_credentials = [dict(username='aap',
password='njkrfnq'),
dict(username='noot',
password='asdfber')]
# url authenticates successfully only for the specified credentials
url = 'http://httpbin.org/basic-auth/{}/{}'.format(
*all_credentials[1].values())
# Schedule requests
replies = []
for credentials in all_credentials:
replies.append(
manager.get(QtNetwork.QNetworkRequest(QtCore.QUrl(url))))
# Add credentials as dynamic attribute
replies[-1].credentials = credentials
manager.replies_unfinished += 1
# Start event loop
app.exec_()
As soon as one request is successfully authenticated, the next one is as well, even though it should not be.
Upvotes: 2
Views: 996
Reputation: 14255
As it turns out there are several similar Qt bug reports that are still open: QTBUG-16835, QTBUG-30433...
I guess it should be possible to use QNetworkRequest.AuthenticationReuseAttribute
, but I could not make that work with multiple requests to the same url with different (valid) sets of credentials.
A possible workaround would be to forget all about the authenticationRequired
signal/slot, and add a raw header (RFC 7617) to each request, exactly as described here.
In the example that would mean removing the authenticationRequired
signal/slot connection and replacing the manager.get()
part by the following lines (for Python 2.7 with PyQt4):
request = QtNetwork.QNetworkRequest(QtCore.QUrl(url))
header_data = QtCore.QByteArray('{}:{}'.format(
credentials['username'], credentials['password'])).toBase64()
request.setRawHeader('Authorization', 'Basic {}'.format(header_data))
replies.append(manager.get(request))
However, as @ekhumoro pointed out in the comments above: not sure how safe this is.
Upvotes: 2