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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtbase/src/network/access/qnetworkaccessauthenticationmanager.cpp