1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the FOO module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | |
30 | #include <QtCore/QString> |
31 | #include <QtTest/QtTest> |
32 | #include <QtCore/QCoreApplication> |
33 | #include <QtNetwork/QAuthenticator> |
34 | |
35 | #include <private/qauthenticator_p.h> |
36 | |
37 | class tst_QAuthenticator : public QObject |
38 | { |
39 | Q_OBJECT |
40 | |
41 | public: |
42 | tst_QAuthenticator(); |
43 | |
44 | private Q_SLOTS: |
45 | void basicAuth(); |
46 | void basicAuth_data(); |
47 | |
48 | void ntlmAuth_data(); |
49 | void ntlmAuth(); |
50 | |
51 | void sha256AndMd5Digest(); |
52 | |
53 | void equalityOperators(); |
54 | }; |
55 | |
56 | tst_QAuthenticator::tst_QAuthenticator() |
57 | { |
58 | } |
59 | |
60 | void tst_QAuthenticator::basicAuth_data() |
61 | { |
62 | QTest::addColumn<QString>(name: "data" ); |
63 | QTest::addColumn<QString>(name: "realm" ); |
64 | QTest::addColumn<QString>(name: "user" ); |
65 | QTest::addColumn<QString>(name: "password" ); |
66 | QTest::addColumn<QByteArray>(name: "expectedReply" ); |
67 | |
68 | QTest::newRow(dataTag: "just-user" ) << "" << "" << "foo" << "" << QByteArray("foo:" ).toBase64(); |
69 | QTest::newRow(dataTag: "user-password" ) << "" << "" << "foo" << "bar" << QByteArray("foo:bar" ).toBase64(); |
70 | QTest::newRow(dataTag: "user-password-realm" ) << "realm=\"secure area\"" << "secure area" << "foo" << "bar" << QByteArray("foo:bar" ).toBase64(); |
71 | } |
72 | |
73 | void tst_QAuthenticator::basicAuth() |
74 | { |
75 | QFETCH(QString, data); |
76 | QFETCH(QString, realm); |
77 | QFETCH(QString, user); |
78 | QFETCH(QString, password); |
79 | QFETCH(QByteArray, expectedReply); |
80 | |
81 | QAuthenticator auth; |
82 | auth.detach(); |
83 | QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(auth); |
84 | QCOMPARE(priv->phase, QAuthenticatorPrivate::Start); |
85 | |
86 | QList<QPair<QByteArray, QByteArray> > ; |
87 | headers << qMakePair<QByteArray, QByteArray>(x: QByteArray("WWW-Authenticate" ), y: "Basic " + data.toUtf8()); |
88 | priv->parseHttpResponse(headers, /*isProxy = */ false, host: {}); |
89 | |
90 | QCOMPARE(auth.realm(), realm); |
91 | QCOMPARE(auth.option("realm" ).toString(), realm); |
92 | |
93 | auth.setUser(user); |
94 | auth.setPassword(password); |
95 | |
96 | QCOMPARE(priv->phase, QAuthenticatorPrivate::Start); |
97 | |
98 | QCOMPARE(priv->calculateResponse("GET" , "/" , "" ).constData(), QByteArray("Basic " + expectedReply).constData()); |
99 | } |
100 | |
101 | void tst_QAuthenticator::ntlmAuth_data() |
102 | { |
103 | QTest::addColumn<QString>(name: "data" ); |
104 | QTest::addColumn<QString>(name: "realm" ); |
105 | QTest::addColumn<bool>(name: "sso" ); |
106 | |
107 | QTest::newRow(dataTag: "no-realm" ) << "TlRMTVNTUAACAAAAHAAcADAAAAAFAoEATFZ3OLRQADIAAAAAAAAAAJYAlgBMAAAAUQBUAC0AVABFAFMAVAAtAEQATwBNAEEASQBOAAIAHABRAFQALQBUAEUAUwBUAC0ARABPAE0AQQBJAE4AAQAcAFEAVAAtAFQARQBTAFQALQBTAEUAUgBWAEUAUgAEABYAcQB0AC0AdABlAHMAdAAtAG4AZQB0AAMANABxAHQALQB0AGUAcwB0AC0AcwBlAHIAdgBlAHIALgBxAHQALQB0AGUAcwB0AC0AbgBlAHQAAAAAAA==" << "" << false; |
108 | QTest::newRow(dataTag: "with-realm" ) << "TlRMTVNTUAACAAAADAAMADgAAAAFAoECWCZkccFFAzwAAAAAAAAAAL4AvgBEAAAABQLODgAAAA9NAEcARABOAE8ASwACAAwATQBHAEQATgBPAEsAAQAcAE4ATwBLAC0AQQBNAFMAUwBTAEYARQAtADAAMQAEACAAbQBnAGQAbgBvAGsALgBuAG8AawBpAGEALgBjAG8AbQADAD4AbgBvAGsALQBhAG0AcwBzAHMAZgBlAC0AMAAxAC4AbQBnAGQAbgBvAGsALgBuAG8AawBpAGEALgBjAG8AbQAFACAAbQBnAGQAbgBvAGsALgBuAG8AawBpAGEALgBjAG8AbQAAAAAA" << "NOE" << false; |
109 | QTest::newRow(dataTag: "no-realm-sso" ) << "TlRMTVNTUAACAAAAHAAcADAAAAAFAoEATFZ3OLRQADIAAAAAAAAAAJYAlgBMAAAAUQBUAC0AVABFAFMAVAAtAEQATwBNAEEASQBOAAIAHABRAFQALQBUAEUAUwBUAC0ARABPAE0AQQBJAE4AAQAcAFEAVAAtAFQARQBTAFQALQBTAEUAUgBWAEUAUgAEABYAcQB0AC0AdABlAHMAdAAtAG4AZQB0AAMANABxAHQALQB0AGUAcwB0AC0AcwBlAHIAdgBlAHIALgBxAHQALQB0AGUAcwB0AC0AbgBlAHQAAAAAAA==" << "" << true; |
110 | QTest::newRow(dataTag: "with-realm-sso" ) << "TlRMTVNTUAACAAAADAAMADgAAAAFAoECWCZkccFFAzwAAAAAAAAAAL4AvgBEAAAABQLODgAAAA9NAEcARABOAE8ASwACAAwATQBHAEQATgBPAEsAAQAcAE4ATwBLAC0AQQBNAFMAUwBTAEYARQAtADAAMQAEACAAbQBnAGQAbgBvAGsALgBuAG8AawBpAGEALgBjAG8AbQADAD4AbgBvAGsALQBhAG0AcwBzAHMAZgBlAC0AMAAxAC4AbQBnAGQAbgBvAGsALgBuAG8AawBpAGEALgBjAG8AbQAFACAAbQBnAGQAbgBvAGsALgBuAG8AawBpAGEALgBjAG8AbQAAAAAA" << "NOE" << true; |
111 | } |
112 | |
113 | void tst_QAuthenticator::ntlmAuth() |
114 | { |
115 | QFETCH(QString, data); |
116 | QFETCH(QString, realm); |
117 | QFETCH(bool, sso); |
118 | |
119 | QAuthenticator auth; |
120 | if (!sso) { |
121 | auth.setUser("unimportant" ); |
122 | auth.setPassword("unimportant" ); |
123 | } |
124 | |
125 | auth.detach(); |
126 | QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(auth); |
127 | QCOMPARE(priv->phase, QAuthenticatorPrivate::Start); |
128 | |
129 | QList<QPair<QByteArray, QByteArray> > ; |
130 | |
131 | // NTLM phase 1: negotiate |
132 | // This phase of NTLM contains no information, other than what we're willing to negotiate |
133 | // Current implementation uses flags: |
134 | // NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_REQUEST_TARGET |
135 | headers << qMakePair<QByteArray, QByteArray>(x: "WWW-Authenticate" , y: "NTLM" ); |
136 | priv->parseHttpResponse(headers, /*isProxy = */ false, host: {}); |
137 | if (sso) |
138 | QVERIFY(priv->calculateResponse("GET" , "/" , "" ).startsWith("NTLM " )); |
139 | else |
140 | QCOMPARE(priv->calculateResponse("GET" , "/" , "" ).constData(), "NTLM TlRMTVNTUAABAAAABYIIAAAAAAAAAAAAAAAAAAAAAAA=" ); |
141 | |
142 | // NTLM phase 2: challenge |
143 | headers.clear(); |
144 | headers << qMakePair<QByteArray, QByteArray>(x: QByteArray("WWW-Authenticate" ), y: "NTLM " + data.toUtf8()); |
145 | priv->parseHttpResponse(headers, /*isProxy = */ false, host: {}); |
146 | |
147 | QEXPECT_FAIL("with-realm" , "NTLM authentication code doesn't extract the realm" , Continue); |
148 | QEXPECT_FAIL("with-realm-sso" , "NTLM authentication code doesn't extract the realm" , Continue); |
149 | QCOMPARE(auth.realm(), realm); |
150 | |
151 | QVERIFY(priv->calculateResponse("GET" , "/" , "" ).startsWith("NTLM " )); |
152 | } |
153 | |
154 | // We don't (currently) support SHA256. So, when presented with the option of MD5 or SHA256, |
155 | // we should always pick MD5. |
156 | void tst_QAuthenticator::sha256AndMd5Digest() |
157 | { |
158 | QByteArray md5 = "Digest realm=\"\", nonce=\"\", algorithm=MD5, qop=\"auth\"" ; |
159 | QByteArray sha256 = "Digest realm=\"\", nonce=\"\", algorithm=SHA-256, qop=\"auth\"" ; |
160 | |
161 | QAuthenticator auth; |
162 | auth.setUser("unimportant" ); |
163 | auth.setPassword("unimportant" ); |
164 | |
165 | QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(auth); |
166 | |
167 | QCOMPARE(priv->phase, QAuthenticatorPrivate::Start); |
168 | QList<QPair<QByteArray, QByteArray>> ; |
169 | // Put sha256 first, so that its parsed first... |
170 | headers.push_back(t: {"WWW-Authenticate" , sha256}); |
171 | headers.push_back(t: {"WWW-Authenticate" , md5}); |
172 | priv->parseHttpResponse(headers, isProxy: false, host: QString()); |
173 | |
174 | QByteArray response = priv->calculateResponse(method: "GET" , path: "/index" , host: {}); |
175 | QCOMPARE(priv->phase, QAuthenticatorPrivate::Done); |
176 | |
177 | QVERIFY(!response.isEmpty()); |
178 | QVERIFY(!response.contains("algorithm=SHA-256" )); |
179 | QVERIFY(response.contains("algorithm=MD5" )); |
180 | } |
181 | |
182 | void tst_QAuthenticator::equalityOperators() |
183 | { |
184 | QAuthenticator s1, s2; |
185 | QVERIFY(s2 == s1); |
186 | QVERIFY(s1 == s2); |
187 | QVERIFY(!(s1 != s2)); |
188 | QVERIFY(!(s2 != s1)); |
189 | s1.setUser("User" ); |
190 | QVERIFY(!(s2 == s1)); |
191 | QVERIFY(!(s1 == s2)); |
192 | QVERIFY(s1 != s2); |
193 | QVERIFY(s2 != s1); |
194 | } |
195 | |
196 | QTEST_MAIN(tst_QAuthenticator); |
197 | |
198 | #include "tst_qauthenticator.moc" |
199 | |