Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qnetworkaccessauthenticationmanager.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
8
9#include "QtCore/qbuffer.h"
10#include "QtCore/qlist.h"
11#include "QtCore/qurl.h"
12#include "QtCore/QMutexLocker"
13#include "QtNetwork/qauthenticator.h"
14
15#include <algorithm>
16
17QT_BEGIN_NAMESPACE
18
19using namespace Qt::StringLiterals;
20
23{
24public:
30
33
34 iterator findClosestMatch(const QString &domain)
35 {
36 iterator it = std::lower_bound(begin(), end(), domain);
37 if (it == end() && !isEmpty())
38 --it;
39 if (it == end() || !domain.startsWith(it->domain))
40 return end();
41 return it;
42 }
43
44 void insert(const QString &domain, const QString &user, const QString &password)
45 {
46 iterator closestMatch = findClosestMatch(domain);
47 if (closestMatch != end() && closestMatch->domain == domain) {
48 // we're overriding the current credentials
49 closestMatch->user = user;
50 closestMatch->password = password;
51 } else {
53 newCredential.domain = domain;
54 newCredential.user = user;
55 newCredential.password = password;
56
57 if (closestMatch != end())
58 QList<QNetworkAuthenticationCredential>::insert(++closestMatch, newCredential);
59 else
60 QList<QNetworkAuthenticationCredential>::insert(end(), newCredential);
61 }
62 }
63
64 virtual void dispose() override { delete this; }
65};
66
67#ifndef QT_NO_NETWORKPROXY
68static QByteArray proxyAuthenticationKey(const QNetworkProxy &proxy, const QString &realm)
69{
70 QUrl key;
71
72 switch (proxy.type()) {
73 case QNetworkProxy::Socks5Proxy:
74 key.setScheme("proxy-socks5"_L1);
75 break;
76
77 case QNetworkProxy::HttpProxy:
78 case QNetworkProxy::HttpCachingProxy:
79 key.setScheme("proxy-http"_L1);
80 break;
81
82 case QNetworkProxy::FtpCachingProxy:
83 key.setScheme("proxy-ftp"_L1);
84 break;
85
86 case QNetworkProxy::DefaultProxy:
87 case QNetworkProxy::NoProxy:
88 // shouldn't happen
89 return QByteArray();
90
91 // no default:
92 // let there be errors if a new proxy type is added in the future
93 }
94
95 if (key.scheme().isEmpty())
96 // proxy type not handled
97 return QByteArray();
98
99 key.setUserName(proxy.user());
100 key.setHost(proxy.hostName());
101 key.setPort(proxy.port());
102 key.setFragment(realm);
103 return "auth:" + key.toEncoded();
104}
105#endif
106
107static inline QByteArray authenticationKey(const QUrl &url, const QString &realm)
108{
109 QUrl copy = url;
110 copy.setFragment(realm);
111 return "auth:" + copy.toEncoded(QUrl::RemovePassword | QUrl::RemovePath | QUrl::RemoveQuery);
112}
113
114
115#ifndef QT_NO_NETWORKPROXY
117 const QAuthenticator *authenticator)
118{
119 Q_ASSERT(authenticator);
120 Q_ASSERT(p.type() != QNetworkProxy::DefaultProxy);
121 Q_ASSERT(p.type() != QNetworkProxy::NoProxy);
122
123 QMutexLocker mutexLocker(&mutex);
124
125 QString realm = authenticator->realm();
126 QNetworkProxy proxy = p;
127 proxy.setUser(authenticator->user());
128
129 // don't cache null passwords, empty password may be valid though
130 if (authenticator->password().isNull())
131 return;
132
133 // Set two credentials: one with the username and one without
134 do {
135 // Set two credentials actually: one with and one without the realm
136 do {
137 QByteArray cacheKey = proxyAuthenticationKey(proxy, realm);
138 if (cacheKey.isEmpty())
139 return; // should not happen
140
142 auth->insert(QString(), authenticator->user(), authenticator->password());
143 authenticationCache.addEntry(cacheKey, auth); // replace the existing one, if there's any
144
145 if (realm.isEmpty()) {
146 break;
147 } else {
148 realm.clear();
149 }
150 } while (true);
151
152 if (proxy.user().isEmpty())
153 break;
154 else
155 proxy.setUser(QString());
156 } while (true);
157}
158
161 const QAuthenticator *authenticator)
162{
163 QNetworkProxy proxy = p;
164 if (proxy.type() == QNetworkProxy::DefaultProxy) {
165 proxy = QNetworkProxy::applicationProxy();
166 }
167 if (!proxy.password().isEmpty())
168 return QNetworkAuthenticationCredential(); // no need to set credentials if it already has them
169
170 QString realm;
171 if (authenticator)
172 realm = authenticator->realm();
173
174 QMutexLocker mutexLocker(&mutex);
175 QByteArray cacheKey = proxyAuthenticationKey(proxy, realm);
176 if (cacheKey.isEmpty())
178 if (!authenticationCache.hasEntry(cacheKey))
180
182 static_cast<QNetworkAuthenticationCache *>(authenticationCache.requestEntryNow(cacheKey));
183 QNetworkAuthenticationCredential cred = *auth->findClosestMatch(QString());
184 authenticationCache.releaseEntry(cacheKey);
185
186 // proxy cache credentials always have exactly one item
187 Q_ASSERT_X(!cred.isNull(), "QNetworkAccessManager",
188 "Internal inconsistency: found a cache key for a proxy, but it's empty");
189 return cred;
190}
191
192#endif
193
195 const QAuthenticator *authenticator)
196{
197 Q_ASSERT(authenticator);
198 if (authenticator->isNull())
199 return;
200 QString domain = QString::fromLatin1("/"); // FIXME: make QAuthenticator return the domain
201 QString realm = authenticator->realm();
202
203 QMutexLocker mutexLocker(&mutex);
204
205 // Set two credentials actually: one with and one without the username in the URL
206 QUrl copy = url;
207 copy.setUserName(authenticator->user());
208 do {
209 QByteArray cacheKey = authenticationKey(copy, realm);
210 if (authenticationCache.hasEntry(cacheKey)) {
212 static_cast<QNetworkAuthenticationCache *>(authenticationCache.requestEntryNow(cacheKey));
213 auth->insert(domain, authenticator->user(), authenticator->password());
214 authenticationCache.releaseEntry(cacheKey);
215 } else {
217 auth->insert(domain, authenticator->user(), authenticator->password());
218 authenticationCache.addEntry(cacheKey, auth);
219 }
220
221 if (copy.userName().isEmpty()) {
222 break;
223 } else {
224 copy.setUserName(QString());
225 }
226 } while (true);
227}
228
229/*!
230 \class QNetworkAccessAuthenticationManager
231 \internal
232*/
233
234/*!
235 Fetch the credential data from the credential cache.
236
237 If auth is 0 (as it is when called from createRequest()), this will try to
238 look up with an empty realm. That fails in most cases for HTTP (because the
239 realm is seldom empty for HTTP challenges). In any case, QHttpNetworkConnection
240 never sends the credentials on the first attempt: it needs to find out what
241 authentication methods the server supports.
242
243 For FTP, realm is always empty.
244*/
247 const QAuthenticator *authentication)
248{
249 if (!url.password().isEmpty())
250 return QNetworkAuthenticationCredential(); // no need to set credentials if it already has them
251
252 QString realm;
253 if (authentication)
254 realm = authentication->realm();
255
256 QByteArray cacheKey = authenticationKey(url, realm);
257
258 QMutexLocker mutexLocker(&mutex);
259 if (!authenticationCache.hasEntry(cacheKey))
261
263 static_cast<QNetworkAuthenticationCache *>(authenticationCache.requestEntryNow(cacheKey));
264 auto cred = auth->findClosestMatch(url.path());
266 if (cred != auth->end())
267 ret = *cred;
268 authenticationCache.releaseEntry(cacheKey);
269 return ret;
270}
271
273{
274 authenticationCache.clear();
275}
276
277QT_END_NAMESPACE
void cacheCredentials(const QUrl &url, const QAuthenticator *auth)
QNetworkAuthenticationCredential fetchCachedCredentials(const QUrl &url, const QAuthenticator *auth=nullptr)
Fetch the credential data from the credential cache.
QNetworkAuthenticationCredential fetchCachedProxyCredentials(const QNetworkProxy &proxy, const QAuthenticator *auth=nullptr)
void cacheProxyCredentials(const QNetworkProxy &proxy, const QAuthenticator *auth)
iterator findClosestMatch(const QString &domain)
void insert(const QString &domain, const QString &user, const QString &password)
static QByteArray proxyAuthenticationKey(const QNetworkProxy &proxy, const QString &realm)
static QByteArray authenticationKey(const QUrl &url, const QString &realm)